aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py17
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py13
-rw-r--r--components/script/dom/bindings/trace.rs2
-rw-r--r--components/script/dom/document.rs39
-rw-r--r--components/script/dom/element.rs18
-rw-r--r--components/script/dom/formdata.rs126
-rw-r--r--components/script/dom/headers.rs154
-rw-r--r--components/script/dom/htmlformelement.rs143
-rw-r--r--components/script/dom/htmllinkelement.rs7
-rw-r--r--components/script/dom/htmlmediaelement.rs3
-rw-r--r--components/script/dom/htmlscriptelement.rs3
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/node.rs14
-rw-r--r--components/script/dom/request.rs808
-rw-r--r--components/script/dom/servohtmlparser.rs4
-rw-r--r--components/script/dom/testbinding.rs7
-rw-r--r--components/script/dom/webglrenderingcontext.rs35
-rw-r--r--components/script/dom/webidls/Body.webidl19
-rw-r--r--components/script/dom/webidls/FormData.webidl5
-rw-r--r--components/script/dom/webidls/Request.webidl108
-rw-r--r--components/script/dom/webidls/TestBinding.webidl5
-rw-r--r--components/script/dom/webidls/XMLHttpRequest.webidl2
-rw-r--r--components/script/dom/window.rs32
-rw-r--r--components/script/dom/xmlhttprequest.rs19
24 files changed, 1335 insertions, 249 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 2e0dc2415dd..8a288694431 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -866,11 +866,22 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
" Err(_) => { %s },\n"
"}" % exceptionCode)
- declType = CGGeneric("ByteString")
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ default = "None"
+ else:
+ assert defaultValue.type.tag() in (IDLType.Tags.domstring, IDLType.Tags.bytestring)
+ default = 'ByteString::new(b"%s".to_vec())' % defaultValue.value
+ if type.nullable():
+ default = "Some(%s)" % default
+
+ declType = "ByteString"
if type.nullable():
- declType = CGWrapper(declType, pre="Option<", post=">")
+ declType = "Option<%s>" % declType
- return handleOptional(conversionCode, declType, handleDefaultNull("None"))
+ return handleOptional(conversionCode, CGGeneric(declType), default)
if type.isEnum():
assert not isEnforceRange and not isClamp
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index da32340dda6..54d510781a1 100644
--- a/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -3391,6 +3391,11 @@ class IDLValue(IDLObject):
# extra normalization step.
assert self.type.isDOMString()
return self
+ elif self.type.isString() and type.isByteString():
+ # Allow ByteStrings to use default value just like
+ # DOMString. No coercion is required here.
+ assert self.type.isDOMString()
+ return self
raise WebIDLError("Cannot coerce type %s to type %s." %
(self.type, type), [location])
@@ -5759,6 +5764,14 @@ class Parser(Tokenizer):
booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean]
p[0] = IDLValue(location, booleanType, p[1])
+ def p_ConstValueByteString(self, p):
+ """
+ ConstValue : BYTESTRING
+ """
+ location = self.getLocation(p, 1)
+ bytestringType = BuiltinTypes[IDLBuiltinType.Types.bytestring]
+ p[0] = IDLValue(location, bytestringType, p[1])
+
def p_ConstValueInteger(self, p):
"""
ConstValue : INTEGER
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/document.rs b/components/script/dom/document.rs
index 9366f9e18bb..cbd4b2ac253 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -672,9 +672,7 @@ impl Document {
};
debug!("{}: at {:?}", mouse_event_type_string, client_point);
- let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32,
- client_point.y + self.window.PageYOffset() as f32);
- let node = match self.window.hit_test_query(page_point, false) {
+ let node = match self.window.hit_test_query(client_point, false) {
Some(node_address) => {
debug!("node address is {:?}", node_address);
node::from_untrusted_node_address(js_runtime, node_address)
@@ -786,9 +784,7 @@ impl Document {
return;
}
- let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32,
- client_point.y + self.window.PageYOffset() as f32);
- let node = match self.window.hit_test_query(page_point, false) {
+ let node = match self.window.hit_test_query(client_point, false) {
Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address),
None => return
};
@@ -864,22 +860,17 @@ impl Document {
js_runtime: *mut JSRuntime,
client_point: Option<Point2D<f32>>,
prev_mouse_over_target: &MutNullableHeap<JS<Element>>) {
- let page_point = match client_point {
+ let client_point = match client_point {
None => {
// If there's no point, there's no target under the mouse
// FIXME: dispatch mouseout here. We have no point.
prev_mouse_over_target.set(None);
return;
}
- Some(ref client_point) => {
- Point2D::new(client_point.x + self.window.PageXOffset() as f32,
- client_point.y + self.window.PageYOffset() as f32)
- }
+ Some(client_point) => client_point,
};
- let client_point = client_point.unwrap();
-
- let maybe_new_target = self.window.hit_test_query(page_point, true).and_then(|address| {
+ let maybe_new_target = self.window.hit_test_query(client_point, true).and_then(|address| {
let node = node::from_untrusted_node_address(js_runtime, address);
node.inclusive_ancestors()
.filter_map(Root::downcast::<Element>)
@@ -1592,8 +1583,12 @@ impl Document {
self.browsing_context.is_none() || !url_has_network_scheme(&self.url)
}
- pub fn nodes_from_point(&self, page_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
- self.window.layout().nodes_from_point(*page_point)
+ pub fn nodes_from_point(&self, client_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
+ let page_point =
+ Point2D::new(client_point.x + self.window.PageXOffset() as f32,
+ client_point.y + self.window.PageYOffset() as f32);
+
+ self.window.layout().nodes_from_point(page_point, *client_point)
}
}
@@ -2812,7 +2807,7 @@ impl DocumentMethods for Document {
fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<Root<Element>> {
let x = *x as f32;
let y = *y as f32;
- let point = &Point2D { x: x, y: y };
+ let point = &Point2D::new(x, y);
let window = window_from_node(self);
let viewport = window.window_size().unwrap().visible_viewport;
@@ -2820,14 +2815,14 @@ impl DocumentMethods for Document {
return None;
}
- if x < 0.0 || y < 0.0 || x > viewport.width.get() || y > viewport.height.get() {
+ if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return None;
}
- let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
-
match self.window.hit_test_query(*point, false) {
Some(untrusted_node_address) => {
+ let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
+
let node = node::from_untrusted_node_address(js_runtime, untrusted_node_address);
let parent_node = node.GetParentNode().unwrap();
let element_ref = node.downcast::<Element>().unwrap_or_else(|| {
@@ -2845,7 +2840,7 @@ impl DocumentMethods for Document {
fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<Root<Element>> {
let x = *x as f32;
let y = *y as f32;
- let point = &Point2D { x: x, y: y };
+ let point = &Point2D::new(x, y);
let window = window_from_node(self);
let viewport = window.window_size().unwrap().visible_viewport;
@@ -2854,7 +2849,7 @@ impl DocumentMethods for Document {
}
// Step 2
- if x < 0.0 || y < 0.0 || x > viewport.width.get() || y > viewport.height.get() {
+ if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return vec!();
}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index b9b02183762..f74f253aa78 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -73,7 +73,6 @@ use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks};
use ref_filter_map::ref_filter_map;
use selectors::matching::{DeclarationBlock, ElementFlags, matches};
use selectors::matching::{HAS_SLOW_SELECTOR, HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
-use selectors::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
use selectors::parser::{AttrSelector, NamespaceConstraint, parse_author_origin_selector_list_from_str};
use std::ascii::AsciiExt;
use std::borrow::Cow;
@@ -83,9 +82,10 @@ use std::default::Default;
use std::mem;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
-use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace, QualName};
+use string_cache::{Atom, Namespace, QualName};
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::element_state::*;
+use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
use style::parser::ParserContextExtraData;
use style::properties::DeclaredValue;
use style::properties::longhands::{self, background_image, border_spacing, font_family, overflow_x, font_size};
@@ -2235,7 +2235,9 @@ impl VirtualMethods for Element {
}
impl<'a> ::selectors::MatchAttrGeneric for Root<Element> {
- fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
+ type Impl = ServoSelectorImpl;
+
+ fn match_attr<F>(&self, attr: &AttrSelector<ServoSelectorImpl>, test: F) -> bool
where F: Fn(&str) -> bool
{
use ::selectors::Element;
@@ -2263,8 +2265,6 @@ impl<'a> ::selectors::MatchAttrGeneric for Root<Element> {
}
impl<'a> ::selectors::Element for Root<Element> {
- type Impl = ServoSelectorImpl;
-
fn parent_element(&self) -> Option<Root<Element>> {
self.upcast::<Node>().GetParentElement()
}
@@ -2299,12 +2299,12 @@ impl<'a> ::selectors::Element for Root<Element> {
})
}
- fn get_local_name(&self) -> BorrowedAtom {
- BorrowedAtom(self.local_name())
+ fn get_local_name(&self) -> &Atom {
+ self.local_name()
}
- fn get_namespace(&self) -> BorrowedNamespace {
- BorrowedNamespace(self.namespace())
+ fn get_namespace(&self) -> &Namespace {
+ self.namespace()
}
fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool {
diff --git a/components/script/dom/formdata.rs b/components/script/dom/formdata.rs
index 41168495847..003412bb435 100644
--- a/components/script/dom/formdata.rs
+++ b/components/script/dom/formdata.rs
@@ -5,40 +5,41 @@
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FormDataBinding;
use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
-use dom::bindings::codegen::UnionTypes::BlobOrUSVString;
+use dom::bindings::codegen::UnionTypes::FileOrUSVString;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
-use dom::bindings::js::{JS, Root};
+use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{DOMString, USVString};
use dom::blob::{Blob, BlobImpl};
use dom::file::File;
-use dom::htmlformelement::HTMLFormElement;
+use dom::htmlformelement::{HTMLFormElement, FormDatumValue, FormDatum};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use string_cache::Atom;
-#[derive(JSTraceable, Clone)]
-#[must_root]
-#[derive(HeapSizeOf)]
-pub enum FormDatum {
- StringData(String),
- BlobData(JS<Blob>)
-}
-
#[dom_struct]
pub struct FormData {
reflector_: Reflector,
data: DOMRefCell<HashMap<Atom, Vec<FormDatum>>>,
- form: Option<JS<HTMLFormElement>>
}
impl FormData {
- fn new_inherited(form: Option<&HTMLFormElement>) -> FormData {
+ fn new_inherited(opt_form: Option<&HTMLFormElement>) -> FormData {
+ let mut hashmap: HashMap<Atom, Vec<FormDatum>> = HashMap::new();
+
+ if let Some(form) = opt_form {
+ for datum in form.get_form_dataset(None) {
+ match hashmap.entry(Atom::from(datum.name.as_ref())) {
+ Occupied(entry) => entry.into_mut().push(datum),
+ Vacant(entry) => { entry.insert(vec!(datum)); }
+ }
+ }
+ }
+
FormData {
reflector_: Reflector::new(),
- data: DOMRefCell::new(HashMap::new()),
- form: form.map(|f| JS::from_ref(f)),
+ data: DOMRefCell::new(hashmap),
}
}
@@ -55,24 +56,34 @@ impl FormData {
impl FormDataMethods for FormData {
// https://xhr.spec.whatwg.org/#dom-formdata-append
- fn Append(&self, name: USVString, value: USVString) {
+ fn Append(&self, name: USVString, str_value: USVString) {
+ let datum = FormDatum {
+ ty: DOMString::from("string"),
+ name: DOMString::from(name.0.clone()),
+ value: FormDatumValue::String(DOMString::from(str_value.0)),
+ };
+
let mut data = self.data.borrow_mut();
match data.entry(Atom::from(name.0)) {
- Occupied(entry) => entry.into_mut().push(FormDatum::StringData(value.0)),
- Vacant (entry) => { entry.insert(vec!(FormDatum::StringData(value.0))); }
+ Occupied(entry) => entry.into_mut().push(datum),
+ Vacant(entry) => { entry.insert(vec!(datum)); }
}
}
#[allow(unrooted_must_root)]
// https://xhr.spec.whatwg.org/#dom-formdata-append
- fn Append_(&self, name: USVString, value: &Blob, filename: Option<USVString>) {
- let blob = FormDatum::BlobData(JS::from_ref(&*self.get_file_or_blob(value, filename)));
+ fn Append_(&self, name: USVString, blob: &Blob, filename: Option<USVString>) {
+ let datum = FormDatum {
+ ty: DOMString::from("file"),
+ name: DOMString::from(name.0.clone()),
+ value: FormDatumValue::File(Root::from_ref(&*self.get_file(blob, filename))),
+ };
+
let mut data = self.data.borrow_mut();
+
match data.entry(Atom::from(name.0)) {
- Occupied(entry) => entry.into_mut().push(blob),
- Vacant(entry) => {
- entry.insert(vec!(blob));
- }
+ Occupied(entry) => entry.into_mut().push(datum),
+ Vacant(entry) => { entry.insert(vec!(datum)); },
}
}
@@ -82,23 +93,23 @@ impl FormDataMethods for FormData {
}
// https://xhr.spec.whatwg.org/#dom-formdata-get
- fn Get(&self, name: USVString) -> Option<BlobOrUSVString> {
+ fn Get(&self, name: USVString) -> Option<FileOrUSVString> {
self.data.borrow()
.get(&Atom::from(name.0))
- .map(|entry| match entry[0] {
- FormDatum::StringData(ref s) => BlobOrUSVString::USVString(USVString(s.clone())),
- FormDatum::BlobData(ref b) => BlobOrUSVString::Blob(Root::from_ref(&*b)),
+ .map(|entry| match entry[0].value {
+ FormDatumValue::String(ref s) => FileOrUSVString::USVString(USVString(s.to_string())),
+ FormDatumValue::File(ref b) => FileOrUSVString::File(Root::from_ref(&*b)),
})
}
// https://xhr.spec.whatwg.org/#dom-formdata-getall
- fn GetAll(&self, name: USVString) -> Vec<BlobOrUSVString> {
+ fn GetAll(&self, name: USVString) -> Vec<FileOrUSVString> {
self.data.borrow()
.get(&Atom::from(name.0))
.map_or(vec![], |data|
- data.iter().map(|item| match *item {
- FormDatum::StringData(ref s) => BlobOrUSVString::USVString(USVString(s.clone())),
- FormDatum::BlobData(ref b) => BlobOrUSVString::Blob(Root::from_ref(&*b)),
+ data.iter().map(|item| match item.value {
+ FormDatumValue::String(ref s) => FileOrUSVString::USVString(USVString(s.to_string())),
+ FormDatumValue::File(ref b) => FileOrUSVString::File(Root::from_ref(&*b)),
}).collect()
)
}
@@ -108,29 +119,48 @@ impl FormDataMethods for FormData {
self.data.borrow().contains_key(&Atom::from(name.0))
}
+ // https://xhr.spec.whatwg.org/#dom-formdata-set
+ fn Set(&self, name: USVString, str_value: USVString) {
+ self.data.borrow_mut().insert(Atom::from(name.0.clone()), vec![FormDatum {
+ ty: DOMString::from("string"),
+ name: DOMString::from(name.0),
+ value: FormDatumValue::String(DOMString::from(str_value.0)),
+ }]);
+ }
+
#[allow(unrooted_must_root)]
// https://xhr.spec.whatwg.org/#dom-formdata-set
- fn Set(&self, name: USVString, value: BlobOrUSVString) {
- let val = match value {
- BlobOrUSVString::USVString(s) => FormDatum::StringData(s.0),
- BlobOrUSVString::Blob(b) => FormDatum::BlobData(JS::from_ref(&*b))
- };
- self.data.borrow_mut().insert(Atom::from(name.0), vec!(val));
+ fn Set_(&self, name: USVString, blob: &Blob, filename: Option<USVString>) {
+ self.data.borrow_mut().insert(Atom::from(name.0.clone()), vec![FormDatum {
+ ty: DOMString::from("file"),
+ name: DOMString::from(name.0),
+ value: FormDatumValue::File(Root::from_ref(&*self.get_file(blob, filename))),
+ }]);
}
+
}
impl FormData {
- fn get_file_or_blob(&self, blob: &Blob, filename: Option<USVString>) -> Root<Blob> {
- match filename {
- Some(fname) => {
- let global = self.global();
- let name = DOMString::from(fname.0);
- let bytes = blob.get_bytes().unwrap_or(vec![]);
-
- Root::upcast(File::new(global.r(), BlobImpl::new_from_bytes(bytes), name, None, ""))
- }
- None => Root::from_ref(blob)
+ fn get_file(&self, blob: &Blob, opt_filename: Option<USVString>) -> Root<File> {
+ let global = self.global();
+
+ let name = match opt_filename {
+ Some(filename) => DOMString::from(filename.0),
+ None => DOMString::from(""),
+ };
+
+ let bytes = blob.get_bytes().unwrap_or(vec![]);
+
+ File::new(global.r(), BlobImpl::new_from_bytes(bytes), name, None, "")
+ }
+
+ pub fn datums(&self) -> Vec<FormDatum> {
+ let mut ret = vec![];
+ for values in self.data.borrow().values() {
+ ret.append(&mut values.clone());
}
+
+ ret
}
}
diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs
index 4ae212aee78..cb3b4d13e48 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,81 +37,54 @@ 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(())
@@ -121,19 +95,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 +140,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 +162,64 @@ 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)) => {
+ 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`,
@@ -315,20 +347,24 @@ fn is_field_name(name: &ByteString) -> bool {
// 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;
}
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 72b8cc19c16..db9e3cb8d41 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -250,13 +250,6 @@ pub enum ResetFrom {
impl HTMLFormElement {
- fn generate_boundary(&self) -> String {
- let i1 = random::<u32>();
- let i2 = random::<u32>();
-
- format!("---------------------------{0}{1}", i1, i2)
- }
-
// https://html.spec.whatwg.org/multipage/#picking-an-encoding-for-the-form
fn pick_encoding(&self) -> EncodingRef {
// Step 2
@@ -275,66 +268,6 @@ impl HTMLFormElement {
document_from_node(self).encoding()
}
- // https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm
- fn encode_multipart_form_data(&self, form_data: &mut Vec<FormDatum>,
- boundary: String, encoding: EncodingRef) -> Vec<u8> {
- // Step 1
- let mut result = vec![];
-
- // Step 2
- let charset = &*encoding.whatwg_name().unwrap_or("UTF-8");
-
- // Step 3
- for entry in form_data.iter_mut() {
- // 3.1
- if entry.name == "_charset_" && entry.ty == "hidden" {
- entry.value = FormDatumValue::String(DOMString::from(charset.clone()));
- }
- // TODO: 3.2
-
- // Step 4
- // https://tools.ietf.org/html/rfc7578#section-4
- // NOTE(izgzhen): The encoding here expected by most servers seems different from
- // what spec says (that it should start with a '\r\n').
- let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
- result.append(&mut boundary_bytes);
- let mut content_disposition = ContentDisposition {
- disposition: DispositionType::Ext("form-data".to_owned()),
- parameters: vec![DispositionParam::Ext("name".to_owned(), String::from(entry.name.clone()))]
- };
-
- match entry.value {
- FormDatumValue::String(ref s) => {
- let mut bytes = format!("Content-Disposition: {}\r\n\r\n{}",
- content_disposition, s).into_bytes();
- result.append(&mut bytes);
- }
- FormDatumValue::File(ref f) => {
- content_disposition.parameters.push(
- DispositionParam::Filename(Charset::Ext(String::from(charset.clone())),
- None,
- f.name().clone().into()));
- // https://tools.ietf.org/html/rfc7578#section-4.4
- let content_type = ContentType(f.upcast::<Blob>().Type()
- .parse().unwrap_or(mime!(Text / Plain)));
- let mut type_bytes = format!("Content-Disposition: {}\r\n{}\r\n\r\n",
- content_disposition,
- content_type).into_bytes();
- result.append(&mut type_bytes);
-
- let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
-
- result.append(&mut bytes);
- }
- }
- }
-
- let mut boundary_bytes = format!("\r\n--{}--", boundary).into_bytes();
- result.append(&mut boundary_bytes);
-
- result
- }
-
// https://html.spec.whatwg.org/multipage/#text/plain-encoding-algorithm
fn encode_plaintext(&self, form_data: &mut Vec<FormDatum>) -> String {
// Step 1
@@ -459,7 +392,7 @@ impl HTMLFormElement {
// https://html.spec.whatwg.org/multipage/#submit-body
fn submit_entity_body(&self, form_data: &mut Vec<FormDatum>, mut load_data: LoadData,
enctype: FormEncType, encoding: EncodingRef) {
- let boundary = self.generate_boundary();
+ let boundary = generate_boundary();
let bytes = match enctype {
FormEncType::UrlEncoded => {
let mut url = load_data.url.clone();
@@ -476,7 +409,7 @@ impl HTMLFormElement {
FormEncType::FormDataEncoded => {
let mime = mime!(Multipart / FormData; Boundary =(&boundary));
load_data.headers.set(ContentType(mime));
- self.encode_multipart_form_data(form_data, boundary, encoding)
+ encode_multipart_form_data(form_data, boundary, encoding)
}
FormEncType::TextPlainEncoded => {
load_data.headers.set(ContentType(mime!(Text / Plain)));
@@ -718,12 +651,13 @@ impl HTMLFormElement {
}
+#[derive(JSTraceable, HeapSizeOf, Clone)]
pub enum FormDatumValue {
File(Root<File>),
String(DOMString)
}
-// #[derive(HeapSizeOf)]
+#[derive(HeapSizeOf, JSTraceable, Clone)]
pub struct FormDatum {
pub ty: DOMString,
pub name: DOMString,
@@ -972,3 +906,72 @@ impl Runnable for PlannedNavigation {
}
}
}
+
+
+// https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm
+pub fn encode_multipart_form_data(form_data: &mut Vec<FormDatum>,
+ boundary: String, encoding: EncodingRef) -> Vec<u8> {
+ // Step 1
+ let mut result = vec![];
+
+ // Step 2
+ let charset = &*encoding.whatwg_name().unwrap_or("UTF-8");
+
+ // Step 3
+ for entry in form_data.iter_mut() {
+ // 3.1
+ if entry.name == "_charset_" && entry.ty == "hidden" {
+ entry.value = FormDatumValue::String(DOMString::from(charset.clone()));
+ }
+ // TODO: 3.2
+
+ // Step 4
+ // https://tools.ietf.org/html/rfc7578#section-4
+ // NOTE(izgzhen): The encoding here expected by most servers seems different from
+ // what spec says (that it should start with a '\r\n').
+ let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
+ result.append(&mut boundary_bytes);
+ let mut content_disposition = ContentDisposition {
+ disposition: DispositionType::Ext("form-data".to_owned()),
+ parameters: vec![DispositionParam::Ext("name".to_owned(), String::from(entry.name.clone()))]
+ };
+
+ match entry.value {
+ FormDatumValue::String(ref s) => {
+ let mut bytes = format!("Content-Disposition: {}\r\n\r\n{}",
+ content_disposition, s).into_bytes();
+ result.append(&mut bytes);
+ }
+ FormDatumValue::File(ref f) => {
+ content_disposition.parameters.push(
+ DispositionParam::Filename(Charset::Ext(String::from(charset.clone())),
+ None,
+ f.name().clone().into()));
+ // https://tools.ietf.org/html/rfc7578#section-4.4
+ let content_type = ContentType(f.upcast::<Blob>().Type()
+ .parse().unwrap_or(mime!(Text / Plain)));
+ let mut type_bytes = format!("Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n",
+ content_disposition,
+ content_type).into_bytes();
+ result.append(&mut type_bytes);
+
+ let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
+
+ result.append(&mut bytes);
+ }
+ }
+ }
+
+ let mut boundary_bytes = format!("\r\n--{}--", boundary).into_bytes();
+ result.append(&mut boundary_bytes);
+
+ result
+}
+
+// https://tools.ietf.org/html/rfc7578#section-4.1
+pub fn generate_boundary() -> String {
+ let i1 = random::<u32>();
+ let i2 = random::<u32>();
+
+ format!("---------------------------{0}{1}", i1, i2)
+}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index 0d5ae4ee179..9264bcb0490 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -24,6 +24,7 @@ use encoding::all::UTF_8;
use hyper::header::ContentType;
use hyper::http::RawStatus;
use hyper::mime::{Mime, TopLevel, SubLevel};
+use hyper_serde::Serde;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
@@ -282,7 +283,7 @@ impl AsyncResponseListener for StylesheetContext {
fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
self.metadata = metadata.ok();
if let Some(ref meta) = self.metadata {
- if let Some(ContentType(Mime(TopLevel::Text, SubLevel::Css, _))) = meta.content_type {
+ if let Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Css, _)))) = meta.content_type {
} else {
self.elem.root().upcast::<EventTarget>().fire_simple_event("error");
}
@@ -304,7 +305,7 @@ impl AsyncResponseListener for StylesheetContext {
Some(meta) => meta,
None => return,
};
- let is_css = metadata.content_type.map_or(false, |ContentType(Mime(top, sub, _))|
+ let is_css = metadata.content_type.map_or(false, |Serde(ContentType(Mime(top, sub, _)))|
top == TopLevel::Text && sub == SubLevel::Css);
let data = if is_css { mem::replace(&mut self.data, vec!()) } else { vec!() };
@@ -334,7 +335,7 @@ impl AsyncResponseListener for StylesheetContext {
document.invalidate_stylesheets();
// FIXME: Revisit once consensus is reached at: https://github.com/whatwg/html/issues/1142
- successful = metadata.status.map_or(false, |RawStatus(code, _)| code == 200);
+ successful = metadata.status.map_or(false, |Serde(RawStatus(code, _))| code == 200);
}
if elem.parser_inserted.get() {
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 119139ae6d8..1c7a27fcef6 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -24,6 +24,7 @@ use dom::htmlsourceelement::HTMLSourceElement;
use dom::mediaerror::MediaError;
use dom::node::{window_from_node, document_from_node, Node, UnbindContext};
use dom::virtualmethods::VirtualMethods;
+use hyper_serde::Serde;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
@@ -67,7 +68,7 @@ impl AsyncResponseListener for HTMLMediaElementContext {
.as_ref()
.and_then(|m| m.status
.as_ref()
- .map(|s| s.0 < 200 || s.0 >= 300))
+ .map(|&Serde(ref s)| s.0 < 200 || s.0 >= 300))
.unwrap_or(false);
if is_failure {
// Ensure that the element doesn't receive any further notifications
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 442f3063524..6a6fe7d50b4 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -27,6 +27,7 @@ use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncodingRef};
use html5ever::tree_builder::NextParserState;
use hyper::http::RawStatus;
+use hyper_serde::Serde;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsval::UndefinedValue;
@@ -158,7 +159,7 @@ impl AsyncResponseListener for ScriptContext {
let status_code = self.metadata.as_ref().and_then(|m| {
match m.status {
- Some(RawStatus(c, _)) => Some(c),
+ Some(Serde(RawStatus(c, _))) => Some(c),
_ => None,
}
}).unwrap_or(0);
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/node.rs b/components/script/dom/node.rs
index 2769b7503bc..34e69552f18 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -70,7 +70,7 @@ use std::borrow::ToOwned;
use std::cell::{Cell, UnsafeCell};
use std::cmp::max;
use std::default::Default;
-use std::iter::{self, FilterMap, Peekable};
+use std::iter;
use std::mem;
use std::ops::Range;
use string_cache::{Atom, Namespace, QualName};
@@ -781,7 +781,7 @@ impl Node {
}
}
- pub fn child_elements(&self) -> ChildElementIterator {
+ pub fn child_elements(&self) -> impl Iterator<Item=Root<Element>> {
self.children().filter_map(Root::downcast as fn(_) -> _).peekable()
}
@@ -1111,10 +1111,6 @@ impl LayoutNodeHelpers for LayoutJS<Node> {
// Iteration and traversal
//
-pub type ChildElementIterator =
- Peekable<FilterMap<NodeSiblingIterator,
- fn(Root<Node>) -> Option<Root<Element>>>>;
-
pub struct NodeSiblingIterator {
current: Option<Root<Node>>,
}
@@ -1460,7 +1456,7 @@ impl Node {
0 => (),
// Step 6.1.2
1 => {
- if !parent.child_elements().peek().is_none() {
+ if !parent.child_elements().next().is_none() {
return Err(Error::HierarchyRequest);
}
if let Some(child) = child {
@@ -1476,7 +1472,7 @@ impl Node {
},
// Step 6.2
NodeTypeId::Element(_) => {
- if !parent.child_elements().peek().is_none() {
+ if !parent.child_elements().next().is_none() {
return Err(Error::HierarchyRequest);
}
if let Some(ref child) = child {
@@ -1503,7 +1499,7 @@ impl Node {
}
},
None => {
- if !parent.child_elements().peek().is_none() {
+ if !parent.child_elements().next().is_none() {
return Err(Error::HierarchyRequest);
}
},
diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs
new file mode 100644
index 00000000000..c396c0d36e1
--- /dev/null
+++ b/components/script/dom/request.rs
@@ -0,0 +1,808 @@
+/* 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;
+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,
+ temporary_request.current_url(),
+ 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()));
+ }
+ // 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
+ try!(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))
+}
+
+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/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs
index 09ee38a1232..d4d2cbc911a 100644
--- a/components/script/dom/servohtmlparser.rs
+++ b/components/script/dom/servohtmlparser.rs
@@ -28,6 +28,7 @@ use html5ever::tree_builder;
use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts};
use hyper::header::ContentType;
use hyper::mime::{Mime, SubLevel, TopLevel};
+use hyper_serde::Serde;
use js::jsapi::JSTracer;
use msg::constellation_msg::{PipelineId, SubpageId};
use net_traits::{AsyncResponseListener, Metadata, NetworkError};
@@ -98,7 +99,8 @@ impl AsyncResponseListener for ParserContext {
},
Err(_) => None,
};
- let content_type = metadata.clone().and_then(|meta| meta.content_type);
+ let content_type =
+ metadata.clone().and_then(|meta| meta.content_type).map(Serde::into_inner);
let parser = match ScriptThread::page_headers_available(&self.id,
self.subpage.as_ref(),
metadata) {
diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs
index 13579d71330..9143c8b0188 100644
--- a/components/script/dom/testbinding.rs
+++ b/components/script/dom/testbinding.rs
@@ -312,6 +312,7 @@ impl TestBindingMethods for TestBinding {
UnrestrictedDoubleValue: 0.0,
anyValue: NullValue(),
booleanValue: false,
+ bytestringValue: ByteString::new(vec![]),
byteValue: 0,
doubleValue: Finite::new(1.0).unwrap(),
enumValue: TestEnum::Foo,
@@ -319,6 +320,7 @@ impl TestBindingMethods for TestBinding {
longLongValue: 54,
longValue: 12,
nullableBooleanValue: None,
+ nullableBytestringValue: None,
nullableByteValue: None,
nullableDoubleValue: None,
nullableFloatValue: None,
@@ -506,6 +508,7 @@ impl TestBindingMethods for TestBinding {
fn PassOptionalUnsignedLongLongWithDefault(&self, _: u64) {}
fn PassOptionalStringWithDefault(&self, _: DOMString) {}
fn PassOptionalUsvstringWithDefault(&self, _: USVString) {}
+ fn PassOptionalBytestringWithDefault(&self, _: ByteString) {}
fn PassOptionalEnumWithDefault(&self, _: TestEnum) {}
fn PassOptionalNullableBooleanWithDefault(&self, _: Option<bool>) {}
@@ -613,8 +616,8 @@ impl TestBindingMethods for TestBinding {
}
}
- fn AdvanceClock(&self, ms: i32) {
- self.global().r().as_window().advance_animation_clock(ms);
+ fn AdvanceClock(&self, ms: i32, tick: bool) {
+ self.global().r().as_window().advance_animation_clock(ms, tick);
}
fn Panic(&self) { panic!("explicit panic from script") }
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index dbb774ad87b..4db5654d738 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -82,6 +82,7 @@ pub struct WebGLRenderingContext {
#[ignore_heap_size_of = "Defined in webrender_traits"]
last_error: Cell<Option<WebGLError>>,
texture_unpacking_settings: Cell<TextureUnpacking>,
+ bound_framebuffer: MutNullableHeap<JS<WebGLFramebuffer>>,
bound_texture_2d: MutNullableHeap<JS<WebGLTexture>>,
bound_texture_cube_map: MutNullableHeap<JS<WebGLTexture>>,
bound_buffer_array: MutNullableHeap<JS<WebGLBuffer>>,
@@ -111,6 +112,7 @@ impl WebGLRenderingContext {
canvas: JS::from_ref(canvas),
last_error: Cell::new(None),
texture_unpacking_settings: Cell::new(CONVERT_COLORSPACE),
+ bound_framebuffer: MutNullableHeap::new(None),
bound_texture_2d: MutNullableHeap::new(None),
bound_texture_cube_map: MutNullableHeap::new(None),
bound_buffer_array: MutNullableHeap::new(None),
@@ -511,6 +513,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal {
+ // Handle the GL_FRAMEBUFFER_BINDING without going all the way
+ // to the GL, since we would just need to map back from GL's
+ // returned ID to the WebGLFramebuffer we're tracking.
+ match parameter {
+ constants::FRAMEBUFFER_BINDING => {
+ rooted!(in(cx) let mut rval = NullValue());
+ if let Some(bound_fb) = self.bound_framebuffer.get() {
+ unsafe {
+ bound_fb.to_jsval(cx, rval.handle_mut());
+ }
+ }
+ return rval.get()
+ }
+ _ => {}
+ }
+
let (sender, receiver) = ipc::channel().unwrap();
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::GetParameter(parameter, sender)))
@@ -671,6 +689,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidOperation);
}
+ self.bound_framebuffer.set(framebuffer);
if let Some(framebuffer) = framebuffer {
framebuffer.bind(target)
} else {
@@ -987,6 +1006,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn DepthRange(&self, near: f32, far: f32) {
+ // From the WebGL 1.0 spec, 6.12: Viewport Depth Range:
+ //
+ // "A call to depthRange will generate an
+ // INVALID_OPERATION error if zNear is greater than
+ // zFar."
+ if near > far {
+ return self.webgl_error(InvalidOperation);
+ }
+
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::DepthRange(near as f64, far as f64)))
.unwrap()
@@ -1074,6 +1102,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
if let Some(framebuffer) = framebuffer {
+ if let Some(bound_fb) = self.bound_framebuffer.get() {
+ if bound_fb.id() == framebuffer.id() {
+ self.bound_framebuffer.set(None);
+ }
+ }
framebuffer.delete()
}
}
@@ -1779,7 +1812,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn VertexAttrib1fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
if let Some(data_vec) = array_buffer_view_to_vec_checked::<f32>(data) {
- if data_vec.len() < 4 {
+ if data_vec.len() < 1 {
return self.webgl_error(InvalidOperation);
}
self.vertex_attrib(indx, data_vec[0], 0f32, 0f32, 1f32)
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/FormData.webidl b/components/script/dom/webidls/FormData.webidl
index a4077a7940f..0de3ef36760 100644
--- a/components/script/dom/webidls/FormData.webidl
+++ b/components/script/dom/webidls/FormData.webidl
@@ -6,7 +6,7 @@
* https://xhr.spec.whatwg.org/#interface-formdata
*/
-typedef (Blob or USVString) FormDataEntryValue;
+typedef (File or USVString) FormDataEntryValue;
[Constructor(optional HTMLFormElement form),
Exposed=(Window,Worker)]
@@ -17,6 +17,7 @@ interface FormData {
FormDataEntryValue? get(USVString name);
sequence<FormDataEntryValue> getAll(USVString name);
boolean has(USVString name);
- void set(USVString name, FormDataEntryValue value);
+ void set(USVString name, USVString value);
+ void set(USVString name, Blob value, optional USVString filename);
// iterable<USVString, FormDataEntryValue>;
};
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"
+};
diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl
index 9fc8a9e3bb6..392aee5963b 100644
--- a/components/script/dom/webidls/TestBinding.webidl
+++ b/components/script/dom/webidls/TestBinding.webidl
@@ -53,6 +53,7 @@ dictionary TestDictionaryDefaults {
float floatValue = 7.0;
unrestricted double UnrestrictedDoubleValue = 7.0;
double doubleValue = 7.0;
+ ByteString bytestringValue = "foo";
DOMString stringValue = "foo";
USVString usvstringValue = "foo";
TestEnum enumValue = "bar";
@@ -71,6 +72,7 @@ dictionary TestDictionaryDefaults {
float? nullableFloatValue = 7.0;
unrestricted double? nullableUnrestrictedDoubleValue = 7.0;
double? nullableDoubleValue = 7.0;
+ ByteString? nullableBytestringValue = "foo";
DOMString? nullableStringValue = "foo";
USVString? nullableUsvstringValue = "foo";
// TestEnum? nullableEnumValue = "bar";
@@ -344,6 +346,7 @@ interface TestBinding {
void passOptionalUnsignedLongWithDefault(optional unsigned long arg = 6);
void passOptionalLongLongWithDefault(optional long long arg = -12);
void passOptionalUnsignedLongLongWithDefault(optional unsigned long long arg = 17);
+ void passOptionalBytestringWithDefault(optional ByteString arg = "x");
void passOptionalStringWithDefault(optional DOMString arg = "x");
void passOptionalUsvstringWithDefault(optional USVString arg = "x");
void passOptionalEnumWithDefault(optional TestEnum arg = "foo");
@@ -439,7 +442,7 @@ interface TestBinding {
[Pref="dom.testbinding.prefcontrolled.enabled"]
const unsigned short prefControlledConstDisabled = 0;
[Pref="layout.animations.test.enabled"]
- void advanceClock(long millis);
+ void advanceClock(long millis, optional boolean forceLayoutTick = true);
[Pref="dom.testbinding.prefcontrolled2.enabled"]
readonly attribute boolean prefControlledAttributeEnabled;
diff --git a/components/script/dom/webidls/XMLHttpRequest.webidl b/components/script/dom/webidls/XMLHttpRequest.webidl
index 9218a0ea45b..05d7256e767 100644
--- a/components/script/dom/webidls/XMLHttpRequest.webidl
+++ b/components/script/dom/webidls/XMLHttpRequest.webidl
@@ -13,7 +13,7 @@
*/
// https://fetch.spec.whatwg.org/#bodyinit
-typedef (Blob or /*BufferSource or FormData or */DOMString or URLSearchParams) BodyInit;
+typedef (Blob or /*BufferSource or */ FormData or DOMString or URLSearchParams) BodyInit;
enum XMLHttpRequestResponseType {
"",
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index c25c826b4ec..e728e38a451 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -103,7 +103,7 @@ use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
use tinyfiledialogs::{self, MessageBoxIcon};
use url::Url;
-use util::geometry::{self, MAX_RECT};
+use util::geometry::{self, max_rect};
use util::opts;
use util::prefs::PREFS;
use webdriver_handlers::jsval_to_webdriver;
@@ -751,7 +751,7 @@ impl WindowMethods for Window {
//TODO Include Scrollbar
fn InnerHeight(&self) -> i32 {
self.window_size.get()
- .and_then(|e| e.visible_viewport.height.get().to_i32())
+ .and_then(|e| e.visible_viewport.height.to_i32())
.unwrap_or(0)
}
@@ -759,7 +759,7 @@ impl WindowMethods for Window {
//TODO Include Scrollbar
fn InnerWidth(&self) -> i32 {
self.window_size.get()
- .and_then(|e| e.visible_viewport.width.get().to_i32())
+ .and_then(|e| e.visible_viewport.width.to_i32())
.unwrap_or(0)
}
@@ -1074,9 +1074,9 @@ impl Window {
}
/// Advances the layout animation clock by `delta` milliseconds, and then
- /// forces a reflow.
- pub fn advance_animation_clock(&self, delta: i32) {
- self.layout_chan.send(Msg::AdvanceClockMs(delta)).unwrap();
+ /// forces a reflow if `tick` is true.
+ pub fn advance_animation_clock(&self, delta: i32, tick: bool) {
+ self.layout_chan.send(Msg::AdvanceClockMs(delta, tick)).unwrap();
}
/// Reflows the page unconditionally if possible and not suppressed. This
@@ -1271,10 +1271,18 @@ impl Window {
self.layout_rpc.node_geometry().client_rect
}
- pub fn hit_test_query(&self, hit_test_request: Point2D<f32>, update_cursor: bool)
+ pub fn hit_test_query(&self,
+ client_point: Point2D<f32>,
+ update_cursor: bool)
-> Option<UntrustedNodeAddress> {
+ let translated_point =
+ Point2D::new(client_point.x + self.PageXOffset() as f32,
+ client_point.y + self.PageYOffset() as f32);
+
if !self.reflow(ReflowGoal::ForScriptQuery,
- ReflowQueryType::HitTestQuery(hit_test_request, update_cursor),
+ ReflowQueryType::HitTestQuery(translated_point,
+ client_point,
+ update_cursor),
ReflowReason::Query) {
return None
}
@@ -1521,7 +1529,7 @@ impl Window {
return false;
}
- let had_clip_rect = clip_rect != MAX_RECT;
+ let had_clip_rect = clip_rect != max_rect();
if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) {
return false;
}
@@ -1529,7 +1537,7 @@ impl Window {
self.page_clip_rect.set(proposed_clip_rect);
// If we didn't have a clip rect, the previous display doesn't need rebuilding
- // because it was built for infinite clip (MAX_RECT).
+ // because it was built for infinite clip (max_rect()).
had_clip_rect
}
@@ -1713,7 +1721,7 @@ impl Window {
resource_threads: resource_threads,
bluetooth_thread: bluetooth_thread,
constellation_chan: constellation_chan,
- page_clip_rect: Cell::new(MAX_RECT),
+ page_clip_rect: Cell::new(max_rect()),
fragment_name: DOMRefCell::new(None),
resize_event: Cell::new(None),
next_subpage_id: Cell::new(SubpageId(0)),
@@ -1770,7 +1778,7 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
ReflowQueryType::NoQuery => "\tNoQuery",
ReflowQueryType::ContentBoxQuery(_n) => "\tContentBoxQuery",
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
- ReflowQueryType::HitTestQuery(_n, _o) => "\tHitTestQuery",
+ ReflowQueryType::HitTestQuery(..) => "\tHitTestQuery",
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
ReflowQueryType::NodeLayerIdQuery(_n) => "\tNodeLayerIdQuery",
ReflowQueryType::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery",
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index b35ffef73c5..382e4c69faa 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -26,6 +26,7 @@ use dom::document::{Document, IsHTMLDocument};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::EventTarget;
use dom::headers::is_forbidden_header_name;
+use dom::htmlformelement::{encode_multipart_form_data, generate_boundary};
use dom::progressevent::ProgressEvent;
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
use dom::xmlhttprequestupload::XMLHttpRequestUpload;
@@ -38,6 +39,7 @@ use hyper::header::{ContentLength, ContentType};
use hyper::http::RawStatus;
use hyper::method::Method;
use hyper::mime::{self, Mime, Attr as MimeAttr, Value as MimeValue};
+use hyper_serde::Serde;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsapi::JS_ClearPendingException;
@@ -874,7 +876,10 @@ impl XMLHttpRequest {
*self.response_url.borrow_mut() = metadata.final_url[..Position::AfterQuery].to_owned();
// XXXManishearth Clear cache entries in case of a network error
- self.process_partial_response(XHRProgress::HeadersReceived(gen_id, metadata.headers, metadata.status));
+ self.process_partial_response(XHRProgress::HeadersReceived(
+ gen_id,
+ metadata.headers.map(Serde::into_inner),
+ metadata.status.map(Serde::into_inner)));
Ok(())
}
@@ -1355,12 +1360,12 @@ impl Extractable for BodyInit {
let encoding = UTF_8 as EncodingRef;
(encoding.encode(s, EncoderTrap::Replace).unwrap(),
Some(DOMString::from("text/plain;charset=UTF-8")))
- },
+ }
BodyInit::URLSearchParams(ref usp) => {
// Default encoding is UTF-8.
(usp.serialize(None).into_bytes(),
Some(DOMString::from("application/x-www-form-urlencoded;charset=UTF-8")))
- },
+ }
BodyInit::Blob(ref b) => {
let content_type = if b.Type().as_ref().is_empty() {
None
@@ -1369,7 +1374,13 @@ impl Extractable for BodyInit {
};
let bytes = b.get_bytes().unwrap_or(vec![]);
(bytes, content_type)
- },
+ }
+ BodyInit::FormData(ref formdata) => {
+ let boundary = generate_boundary();
+ let bytes = encode_multipart_form_data(&mut formdata.datums(), boundary.clone(),
+ UTF_8 as EncodingRef);
+ (bytes, Some(DOMString::from(format!("multipart/form-data;boundary={}", boundary))))
+ }
}
}
}