diff --git a/components/script/dom/webidls/XMLHttpRequest.webidl b/components/script/dom/webidls/XMLHttpRequest.webidl index 05d7256e7672..d5a70a78be7e 100644 --- a/components/script/dom/webidls/XMLHttpRequest.webidl +++ b/components/script/dom/webidls/XMLHttpRequest.webidl @@ -53,7 +53,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget { attribute boolean withCredentials; readonly attribute XMLHttpRequestUpload upload; [Throws] - void send(optional /*Document or*/ BodyInit? data = null); + void send(optional (Document or BodyInit)? data = null); void abort(); // response diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index d034b60aebdf..48860dea963b 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -5,12 +5,13 @@ use document_loader::DocumentLoader; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; +use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::XMLHttpRequestBinding; -use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::BodyInit; use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestMethods; use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestResponseType; +use dom::bindings::codegen::UnionTypes::DocumentOrBodyInit; use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::{GlobalRef, GlobalRoot}; @@ -27,6 +28,7 @@ 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::node::Node; use dom::progressevent::ProgressEvent; use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; use dom::xmlhttprequestupload::XMLHttpRequestUpload; @@ -34,6 +36,9 @@ use encoding::all::UTF_8; use encoding::label::encoding_from_whatwg_label; use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef}; use euclid::length::Length; +use html5ever::serialize; +use html5ever::serialize::SerializeOpts; +use html5ever::serialize::TraversalScope::ChildrenOnly; use hyper::header::{ContentLength, ContentType}; use hyper::header::Headers; use hyper::method::Method; @@ -499,7 +504,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { } // https://xhr.spec.whatwg.org/#the-send()-method - fn Send(&self, data: Option) -> ErrorResult { + fn Send(&self, data: Option) -> ErrorResult { // Step 1, 2 if self.ready_state.get() != XMLHttpRequestState::Opened || self.send_flag.get() { return Err(Error::InvalidState); @@ -606,7 +611,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { match extracted { Some((_, ref content_type)) => { // this should handle Document bodies too, not just BodyInit - let encoding = if let Some(BodyInit::String(_)) = data { + let encoding = if let Some(DocumentOrBodyInit::String(_)) = data { // XHR spec differs from http, and says UTF-8 should be in capitals, // instead of "utf-8", which is what Hyper defaults to. So not // using content types provided by Hyper. @@ -1354,21 +1359,22 @@ impl XHRTimeoutCallback { trait Extractable { fn extract(&self) -> (Vec, Option); } -impl Extractable for BodyInit { + +impl Extractable for DocumentOrBodyInit { // https://fetch.spec.whatwg.org/#concept-bodyinit-extract fn extract(&self) -> (Vec, Option) { match *self { - BodyInit::String(ref s) => { + DocumentOrBodyInit::String(ref s) => { let encoding = UTF_8 as EncodingRef; (encoding.encode(s, EncoderTrap::Replace).unwrap(), Some(DOMString::from("text/plain;charset=UTF-8"))) } - BodyInit::URLSearchParams(ref usp) => { + DocumentOrBodyInit::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) => { + DocumentOrBodyInit::Blob(ref b) => { let content_type = if b.Type().as_ref().is_empty() { None } else { @@ -1377,11 +1383,33 @@ impl Extractable for BodyInit { let bytes = b.get_bytes().unwrap_or(vec![]); (bytes, content_type) } - BodyInit::FormData(ref formdata) => { + DocumentOrBodyInit::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)))) + }, + DocumentOrBodyInit::Document(ref d) => { + let data: Vec = serialize_document(d).unwrap().into(); + let decoded_data: Vec = match &*d.CharacterSet() { + "UTF-8" => { + debug!("Document is already utf-8, skipping conversion {:?}", d.url()); + data + }, + document_charset => { + debug!("Document is {:?} we have to decode", document_charset); + let charset = encoding_from_whatwg_label(&*d.CharacterSet()).unwrap_or(UTF_8); + charset.decode(&*data, DecoderTrap::Replace).unwrap().into_bytes() + } + }; + let mut content_type = String::new(); + if d.is_html_document() { + content_type.push_str("text/html"); + } else { + content_type.push_str("application/xml"); + }; + content_type.push_str(";charset=UTF-8"); + (decoded_data, Some(DOMString::from(content_type))) } } } @@ -1450,3 +1478,14 @@ pub fn is_field_value(slice: &[u8]) -> bool { } }) } + +// Serialize a Document struct +fn serialize_document(document: &Document) -> Fallible { + let mut writer = vec![]; + if let Ok(()) = serialize(&mut writer, &document.upcast::(), + SerializeOpts { traversal_scope: ChildrenOnly, ..Default::default() }) { + Ok(DOMString::from(String::from_utf8(writer).unwrap())) + } else { + Err(Error::InvalidState) + } +} diff --git a/tests/wpt/metadata/XMLHttpRequest/send-entity-body-document.htm.ini b/tests/wpt/metadata/XMLHttpRequest/send-entity-body-document.htm.ini index 3d6b19f8d72a..0be15fb55bd1 100644 --- a/tests/wpt/metadata/XMLHttpRequest/send-entity-body-document.htm.ini +++ b/tests/wpt/metadata/XMLHttpRequest/send-entity-body-document.htm.ini @@ -2,22 +2,6 @@ type: testharness [XML document, windows-1252] expected: FAIL - - [HTML document, invalid UTF-8] - expected: FAIL - [HTML document, shift-jis] expected: FAIL - [plain text file] - expected: FAIL - - [image file] - expected: FAIL - - [img tag] - expected: FAIL - - [empty div] - expected: FAIL -