зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #14648 - Implemented XMLHttpRequest.send(Document) (from cynicaldevil:xmlhttpreq-send); r=KiChjang
<!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #9490 . <!-- Either: --> r? @KiChjang `XMLHttpRequest/send-entity-body-document.htm` is not working atm, I get this output: ``` $ ./mach test-wpt tests/wpt/web-platform-tests/XMLHttpRequest/send-entity-body-document.htm Running 1 tests in web-platform-tests Ran 1 tests finished in 0.0 seconds. • 1 ran as expected. 1 tests skipped. ``` <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 87cce8180a3feba92086da0e96ccea09fa177d98
This commit is contained in:
Родитель
ad30e3c8d0
Коммит
fc73f1abd9
|
@ -53,7 +53,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
|
||||||
attribute boolean withCredentials;
|
attribute boolean withCredentials;
|
||||||
readonly attribute XMLHttpRequestUpload upload;
|
readonly attribute XMLHttpRequestUpload upload;
|
||||||
[Throws]
|
[Throws]
|
||||||
void send(optional /*Document or*/ BodyInit? data = null);
|
void send(optional (Document or BodyInit)? data = null);
|
||||||
void abort();
|
void abort();
|
||||||
|
|
||||||
// response
|
// response
|
||||||
|
|
|
@ -4,13 +4,14 @@
|
||||||
|
|
||||||
use document_loader::DocumentLoader;
|
use document_loader::DocumentLoader;
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
use dom::bindings::codegen::Bindings::BlobBinding::BlobBinding::BlobMethods;
|
||||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding;
|
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding;
|
||||||
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::BodyInit;
|
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::BodyInit;
|
||||||
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestMethods;
|
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestMethods;
|
||||||
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestResponseType;
|
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestResponseType;
|
||||||
|
use dom::bindings::codegen::UnionTypes::DocumentOrBodyInit;
|
||||||
use dom::bindings::conversions::ToJSValConvertible;
|
use dom::bindings::conversions::ToJSValConvertible;
|
||||||
use dom::bindings::error::{Error, ErrorResult, Fallible};
|
use dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
|
@ -23,11 +24,14 @@ use dom::document::{Document, IsHTMLDocument};
|
||||||
use dom::document::DocumentSource;
|
use dom::document::DocumentSource;
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
|
use dom::formdata::FormData;
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
use dom::headers::is_forbidden_header_name;
|
use dom::headers::is_forbidden_header_name;
|
||||||
use dom::htmlformelement::{encode_multipart_form_data, generate_boundary};
|
use dom::htmlformelement::{encode_multipart_form_data, generate_boundary};
|
||||||
|
use dom::node::Node;
|
||||||
use dom::progressevent::ProgressEvent;
|
use dom::progressevent::ProgressEvent;
|
||||||
use dom::servoparser::ServoParser;
|
use dom::servoparser::ServoParser;
|
||||||
|
use dom::urlsearchparams::URLSearchParams;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom::workerglobalscope::WorkerGlobalScope;
|
use dom::workerglobalscope::WorkerGlobalScope;
|
||||||
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
|
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
|
||||||
|
@ -36,6 +40,7 @@ use encoding::all::UTF_8;
|
||||||
use encoding::label::encoding_from_whatwg_label;
|
use encoding::label::encoding_from_whatwg_label;
|
||||||
use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef};
|
use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef};
|
||||||
use euclid::length::Length;
|
use euclid::length::Length;
|
||||||
|
use html5ever::serialize::{self, SerializeOpts};
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
use hyper::header::Headers;
|
use hyper::header::Headers;
|
||||||
use hyper::method::Method;
|
use hyper::method::Method;
|
||||||
|
@ -479,7 +484,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://xhr.spec.whatwg.org/#the-send()-method
|
// https://xhr.spec.whatwg.org/#the-send()-method
|
||||||
fn Send(&self, data: Option<BodyInit>) -> ErrorResult {
|
fn Send(&self, data: Option<DocumentOrBodyInit>) -> ErrorResult {
|
||||||
// Step 1, 2
|
// Step 1, 2
|
||||||
if self.ready_state.get() != XMLHttpRequestState::Opened || self.send_flag.get() {
|
if self.ready_state.get() != XMLHttpRequestState::Opened || self.send_flag.get() {
|
||||||
return Err(Error::InvalidState);
|
return Err(Error::InvalidState);
|
||||||
|
@ -491,16 +496,31 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
||||||
_ => data
|
_ => data
|
||||||
};
|
};
|
||||||
// Step 4 (first half)
|
// Step 4 (first half)
|
||||||
let extracted = data.as_ref().map(|d| d.extract());
|
let extracted_or_serialized = match data {
|
||||||
|
Some(DocumentOrBodyInit::Document(ref doc)) => {
|
||||||
|
let data = Vec::from(try!(serialize_document(&doc)).as_ref());
|
||||||
|
let content_type = if doc.is_html_document() {
|
||||||
|
"text/html;charset=UTF-8"
|
||||||
|
} else {
|
||||||
|
"application/xml;charset=UTF-8"
|
||||||
|
};
|
||||||
|
Some((data, Some(DOMString::from(content_type))))
|
||||||
|
},
|
||||||
|
Some(DocumentOrBodyInit::Blob(ref b)) => Some(b.extract()),
|
||||||
|
Some(DocumentOrBodyInit::FormData(ref formdata)) => Some(formdata.extract()),
|
||||||
|
Some(DocumentOrBodyInit::String(ref str)) => Some(str.extract()),
|
||||||
|
Some(DocumentOrBodyInit::URLSearchParams(ref urlsp)) => Some(urlsp.extract()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
self.request_body_len.set(extracted.as_ref().map_or(0, |e| e.0.len()));
|
self.request_body_len.set(extracted_or_serialized.as_ref().map_or(0, |e| e.0.len()));
|
||||||
|
|
||||||
// todo preserved headers?
|
// todo preserved headers?
|
||||||
|
|
||||||
// Step 6
|
// Step 6
|
||||||
self.upload_complete.set(false);
|
self.upload_complete.set(false);
|
||||||
// Step 7
|
// Step 7
|
||||||
self.upload_complete.set(match extracted {
|
self.upload_complete.set(match extracted_or_serialized {
|
||||||
None => true,
|
None => true,
|
||||||
Some(ref e) if e.0.is_empty() => true,
|
Some(ref e) if e.0.is_empty() => true,
|
||||||
_ => false
|
_ => false
|
||||||
|
@ -562,7 +582,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
||||||
headers: (*self.request_headers.borrow()).clone(),
|
headers: (*self.request_headers.borrow()).clone(),
|
||||||
unsafe_request: true,
|
unsafe_request: true,
|
||||||
// XXXManishearth figure out how to avoid this clone
|
// XXXManishearth figure out how to avoid this clone
|
||||||
body: extracted.as_ref().map(|e| e.0.clone()),
|
body: extracted_or_serialized.as_ref().map(|e| e.0.clone()),
|
||||||
// XXXManishearth actually "subresource", but it doesn't exist
|
// XXXManishearth actually "subresource", but it doesn't exist
|
||||||
// https://github.com/whatwg/xhr/issues/71
|
// https://github.com/whatwg/xhr/issues/71
|
||||||
destination: Destination::None,
|
destination: Destination::None,
|
||||||
|
@ -583,16 +603,15 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 4 (second half)
|
// step 4 (second half)
|
||||||
match extracted {
|
match extracted_or_serialized {
|
||||||
Some((_, ref content_type)) => {
|
Some((_, ref content_type)) => {
|
||||||
// this should handle Document bodies too, not just BodyInit
|
let encoding = match data {
|
||||||
let encoding = if let Some(BodyInit::String(_)) = data {
|
Some(DocumentOrBodyInit::String(_)) | Some(DocumentOrBodyInit::Document(_)) =>
|
||||||
// XHR spec differs from http, and says UTF-8 should be in capitals,
|
// 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
|
// instead of "utf-8", which is what Hyper defaults to. So not
|
||||||
// using content types provided by Hyper.
|
// using content types provided by Hyper.
|
||||||
Some(MimeValue::Ext("UTF-8".to_string()))
|
Some(MimeValue::Ext("UTF-8".to_string())),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut content_type_set = false;
|
let mut content_type_set = false;
|
||||||
|
@ -1332,35 +1351,60 @@ impl XHRTimeoutCallback {
|
||||||
pub trait Extractable {
|
pub trait Extractable {
|
||||||
fn extract(&self) -> (Vec<u8>, Option<DOMString>);
|
fn extract(&self) -> (Vec<u8>, Option<DOMString>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Extractable for Blob {
|
||||||
|
fn extract(&self) -> (Vec<u8>, Option<DOMString>) {
|
||||||
|
let content_type = if self.Type().as_ref().is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.Type())
|
||||||
|
};
|
||||||
|
let bytes = self.get_bytes().unwrap_or(vec![]);
|
||||||
|
(bytes, content_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Extractable for DOMString {
|
||||||
|
fn extract(&self) -> (Vec<u8>, Option<DOMString>) {
|
||||||
|
(UTF_8.encode(self, EncoderTrap::Replace).unwrap(),
|
||||||
|
Some(DOMString::from("text/plain;charset=UTF-8")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extractable for FormData {
|
||||||
|
fn extract(&self) -> (Vec<u8>, Option<DOMString>) {
|
||||||
|
let boundary = generate_boundary();
|
||||||
|
let bytes = encode_multipart_form_data(&mut self.datums(), boundary.clone(),
|
||||||
|
UTF_8 as EncodingRef);
|
||||||
|
(bytes, Some(DOMString::from(format!("multipart/form-data;boundary={}", boundary))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extractable for URLSearchParams {
|
||||||
|
fn extract(&self) -> (Vec<u8>, Option<DOMString>) {
|
||||||
|
// Default encoding is UTF-8.
|
||||||
|
(self.serialize(None).into_bytes(),
|
||||||
|
Some(DOMString::from("application/x-www-form-urlencoded;charset=UTF-8")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_document(doc: &Document) -> Fallible<DOMString> {
|
||||||
|
let mut writer = vec![];
|
||||||
|
match serialize(&mut writer, &doc.upcast::<Node>(), SerializeOpts::default()) {
|
||||||
|
Ok(_) => Ok(DOMString::from(String::from_utf8(writer).unwrap())),
|
||||||
|
Err(_) => Err(Error::InvalidState),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Extractable for BodyInit {
|
impl Extractable for BodyInit {
|
||||||
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
||||||
fn extract(&self) -> (Vec<u8>, Option<DOMString>) {
|
fn extract(&self) -> (Vec<u8>, Option<DOMString>) {
|
||||||
match *self {
|
match *self {
|
||||||
BodyInit::String(ref s) => {
|
BodyInit::String(ref s) => s.extract(),
|
||||||
let encoding = UTF_8 as EncodingRef;
|
BodyInit::URLSearchParams(ref usp) => usp.extract(),
|
||||||
(encoding.encode(s, EncoderTrap::Replace).unwrap(),
|
BodyInit::Blob(ref b) => b.extract(),
|
||||||
Some(DOMString::from("text/plain;charset=UTF-8")))
|
BodyInit::FormData(ref formdata) => formdata.extract(),
|
||||||
}
|
|
||||||
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
|
|
||||||
} else {
|
|
||||||
Some(b.Type())
|
|
||||||
};
|
|
||||||
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))))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче