diff options
Diffstat (limited to 'components/script/dom')
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)))) + } } } } |