diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/blob.rs | 71 | ||||
-rw-r--r-- | components/script/dom/headers.rs | 73 | ||||
-rw-r--r-- | components/script/dom/xmlhttprequest.rs | 155 |
3 files changed, 128 insertions, 171 deletions
diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index c5c5c480707..18e968aaa70 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -12,11 +12,10 @@ use dom_struct::dom_struct; use encoding_rs::UTF_8; use js::jsapi::JSObject; use js::rust::HandleObject; -use js::typedarray::Uint8; +use js::typedarray::{ArrayBufferU8, Uint8}; use net_traits::filemanager_thread::RelativePos; use uuid::Uuid; -use crate::body::{FetchedData, run_array_buffer_data_algorithm}; use crate::dom::bindings::buffer_source::create_buffer_source; use crate::dom::bindings::codegen::Bindings::BlobBinding; use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; @@ -226,7 +225,7 @@ impl BlobMethods<crate::DomTypeHolder> for Blob { Blob::new(&global, blob_impl, can_gc) } - // https://w3c.github.io/FileAPI/#text-method-algo + /// <https://w3c.github.io/FileAPI/#text-method-algo> fn Text(&self, can_gc: CanGc) -> Rc<Promise> { let global = self.global(); let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>(); @@ -250,35 +249,51 @@ impl BlobMethods<crate::DomTypeHolder> for Blob { } // https://w3c.github.io/FileAPI/#arraybuffer-method-algo - fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> { - let global = self.global(); - let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>(); - let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); + fn ArrayBuffer(&self, in_realm: InRealm, can_gc: CanGc) -> Rc<Promise> { + let cx = GlobalScope::get_cx(); + let global = GlobalScope::from_safe_context(cx, in_realm); + let promise = Promise::new_in_current_realm(in_realm, can_gc); - let id = self.get_blob_url_id(); + // 1. Let stream be the result of calling get stream on this. + let stream = self.get_stream(can_gc); - global.read_file_async( - id, - p.clone(), - Box::new(|promise, bytes| { - match bytes { - Ok(b) => { - let cx = GlobalScope::get_cx(); - let result = run_array_buffer_data_algorithm(cx, b, CanGc::note()); - - match result { - Ok(FetchedData::ArrayBuffer(a)) => { - promise.resolve_native(&a, CanGc::note()) - }, - Err(e) => promise.reject_error(e, CanGc::note()), - _ => panic!("Unexpected result from run_array_buffer_data_algorithm"), - } - }, - Err(e) => promise.reject_error(e, CanGc::note()), - }; + // 2. Let reader be the result of getting a reader from stream. + // If that threw an exception, return a new promise rejected with that exception. + let reader = match stream.and_then(|s| s.acquire_default_reader(can_gc)) { + Ok(reader) => reader, + Err(error) => { + promise.reject_error(error, can_gc); + return promise; + }, + }; + + // 3. Let promise be the result of reading all bytes from stream with reader. + let success_promise = promise.clone(); + let failure_promise = promise.clone(); + reader.read_all_bytes( + cx, + &global, + Rc::new(move |bytes| { + rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>()); + // 4. Return the result of transforming promise by a fulfillment handler that returns a new + // [ArrayBuffer] + let array_buffer = create_buffer_source::<ArrayBufferU8>( + cx, + bytes, + js_object.handle_mut(), + can_gc, + ) + .expect("Converting input to ArrayBufferU8 should never fail"); + success_promise.resolve_native(&array_buffer, can_gc); }), + Rc::new(move |cx, value| { + failure_promise.reject(cx, value, can_gc); + }), + in_realm, + can_gc, ); - p + + promise } /// <https://w3c.github.io/FileAPI/#dom-blob-bytes> diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs index 10a8be731bf..0e8dcf92ccd 100644 --- a/components/script/dom/headers.rs +++ b/components/script/dom/headers.rs @@ -5,12 +5,12 @@ use std::cell::Cell; use std::str::{self, FromStr}; -use data_url::mime::Mime as DataUrlMime; use dom_struct::dom_struct; use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue}; use js::rust::HandleObject; use net_traits::fetch::headers::{ - get_decode_and_split_header_value, get_value_from_header_list, is_forbidden_method, + extract_mime_type, get_decode_and_split_header_value, get_value_from_header_list, + is_forbidden_method, }; use net_traits::request::is_cors_safelisted_request_header; @@ -564,72 +564,3 @@ pub(crate) fn is_vchar(x: u8) -> bool { pub(crate) fn is_obs_text(x: u8) -> bool { matches!(x, 0x80..=0xFF) } - -// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type -// This function uses data_url::Mime to parse the MIME Type because -// mime::Mime does not provide a parser following the Fetch spec -// see https://github.com/hyperium/mime/issues/106 -pub(crate) fn extract_mime_type(headers: &HyperHeaders) -> Option<Vec<u8>> { - let mut charset: Option<String> = None; - let mut essence: String = "".to_string(); - let mut mime_type: Option<DataUrlMime> = None; - - // Step 4 - let headers_values = headers.get_all(http::header::CONTENT_TYPE).iter(); - - // Step 5 - if headers_values.size_hint() == (0, Some(0)) { - return None; - } - - // Step 6 - for header_value in headers_values { - // Step 6.1 - match DataUrlMime::from_str(header_value.to_str().unwrap_or("")) { - // Step 6.2 - Err(_) => continue, - Ok(temp_mime) => { - let temp_essence = format!("{}/{}", temp_mime.type_, temp_mime.subtype); - - // Step 6.2 - if temp_essence == "*/*" { - continue; - } - - let temp_charset = &temp_mime.get_parameter("charset"); - - // Step 6.3 - mime_type = Some(DataUrlMime { - type_: temp_mime.type_.to_string(), - subtype: temp_mime.subtype.to_string(), - parameters: temp_mime.parameters.clone(), - }); - - // Step 6.4 - if temp_essence != essence { - charset = temp_charset.map(|c| c.to_string()); - temp_essence.clone_into(&mut essence); - } else { - // Step 6.5 - if temp_charset.is_none() && charset.is_some() { - let DataUrlMime { - type_: t, - subtype: st, - parameters: p, - } = mime_type.unwrap(); - let mut params = p; - params.push(("charset".to_string(), charset.clone().unwrap())); - mime_type = Some(DataUrlMime { - type_: t.to_string(), - subtype: st.to_string(), - parameters: params, - }) - } - } - }, - } - } - - // Step 7, 8 - mime_type.map(|m| format!("{}", m).into_bytes()) -} diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index ca5bb72a1dc..4e7c136f42b 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -26,6 +26,7 @@ use js::jsval::{JSVal, NullValue}; use js::rust::wrappers::JS_ParseJSON; use js::rust::{HandleObject, MutableHandleValue}; use js::typedarray::{ArrayBuffer, ArrayBufferU8}; +use net_traits::fetch::headers::extract_mime_type_as_dataurl_mime; use net_traits::http_status::HttpStatus; use net_traits::request::{CredentialsMode, Referrer, RequestBuilder, RequestId, RequestMode}; use net_traits::{ @@ -59,7 +60,7 @@ use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLD use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; -use crate::dom::headers::{extract_mime_type, is_forbidden_request_header}; +use crate::dom::headers::is_forbidden_request_header; use crate::dom::node::Node; use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::progressevent::ProgressEvent; @@ -1324,11 +1325,7 @@ impl XMLHttpRequest { return response; } // Step 2 - let mime = self - .final_mime_type() - .as_ref() - .map(|m| normalize_type_string(&m.to_string())) - .unwrap_or("".to_owned()); + let mime = normalize_type_string(&self.final_mime_type().to_string()); // Step 3, 4 let bytes = self.response.borrow().to_vec(); @@ -1366,64 +1363,77 @@ impl XMLHttpRequest { return response; } - // Step 1 + // Step 1: If xhr’s response’s body is null, then return. if self.response_status.get().is_err() { return None; } - // Step 2 - let mime_type = self.final_mime_type(); - // Step 5.3, 7 - let charset = self.final_charset().unwrap_or(UTF_8); - let temp_doc: DomRoot<Document>; - match mime_type { - Some(ref mime) if mime.matches(TEXT, HTML) => { - // Step 4 - if self.response_type.get() == XMLHttpRequestResponseType::_empty { - return None; - } else { - // TODO Step 5.2 "If charset is null, prescan the first 1024 bytes of xhr’s received bytes" - // Step 5 - temp_doc = self.document_text_html(can_gc); - } - }, - // Step 7 - None => { - temp_doc = self.handle_xml(can_gc); - // Not sure it the parser should throw an error for this case - // The specification does not indicates this test, - // but for now we check the document has no child nodes - let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none(); - if has_no_child_nodes { - return None; - } - }, - Some(ref mime) - if mime.matches(TEXT, XML) || - mime.matches(APPLICATION, XML) || - mime.has_suffix(XML) => - { - temp_doc = self.handle_xml(can_gc); - // Not sure it the parser should throw an error for this case - // The specification does not indicates this test, - // but for now we check the document has no child nodes - let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none(); - if has_no_child_nodes { - return None; - } - }, - // Step 3 - _ => { + // Step 2: Let finalMIME be the result of get a final MIME type for xhr. + let final_mime = self.final_mime_type(); + + // Step 3: If finalMIME is not an HTML MIME type or an XML MIME type, then return. + let is_xml_mime_type = final_mime.matches(TEXT, XML) || + final_mime.matches(APPLICATION, XML) || + final_mime.has_suffix(XML); + if !final_mime.matches(TEXT, HTML) && !is_xml_mime_type { + return None; + } + + // Step 4: If xhr’s response type is the empty string and finalMIME is an HTML MIME + // type, then return. + let charset; + let temp_doc; + if final_mime.matches(TEXT, HTML) { + if self.response_type.get() == XMLHttpRequestResponseType::_empty { return None; - }, + } + + // Step 5: If finalMIME is an HTML MIME type, then: + // Step 5.1: Let charset be the result of get a final encoding for xhr. + // Step 5.2: If charset is null, prescan the first 1024 bytes of xhr’s received bytes + // and if that does not terminate unsuccessfully then let charset be the return value. + // TODO: This isn't happening right now. + // Step 5.3. If charset is null, then set charset to UTF-8. + charset = Some(self.final_charset().unwrap_or(UTF_8)); + + // Step 5.4: Let document be a document that represents the result parsing xhr’s + // received bytes following the rules set forth in the HTML Standard for an HTML parser + // with scripting disabled and a known definite encoding charset. [HTML] + temp_doc = self.document_text_html(can_gc); + } else { + assert!(is_xml_mime_type); + + // Step 6: Otherwise, let document be a document that represents the result of running + // the XML parser with XML scripting support disabled on xhr’s received bytes. If that + // fails (unsupported character encoding, namespace well-formedness error, etc.), then + // return null. [HTML] + // + // TODO: The spec seems to suggest the charset should come from the XML parser here. + temp_doc = self.handle_xml(can_gc); + charset = self.final_charset(); + + // Not sure it the parser should throw an error for this case + // The specification does not indicates this test, + // but for now we check the document has no child nodes + let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none(); + if has_no_child_nodes { + return None; + } } - // Step 8 + + // Step 7: If charset is null, then set charset to UTF-8. + let charset = charset.unwrap_or(UTF_8); + + // Step 8: Set document’s encoding to charset. temp_doc.set_encoding(charset); - // Step 9 to 11 - // Done by handle_text_html and handle_xml + // Step 9: Set document’s content type to finalMIME. + // Step 10: Set document’s URL to xhr’s response’s URL. + // Step 11: Set document’s origin to xhr’s relevant settings object’s origin. + // + // Done by `handle_text_html()` and `handle_xml()`. - // Step 12 + // Step 12: Set xhr’s response object to document. self.response_xml.set(Some(&temp_doc)); self.response_xml.get() } @@ -1507,7 +1517,7 @@ impl XMLHttpRequest { Ok(parsed) => Some(parsed), Err(_) => None, // Step 7 }; - let content_type = self.final_mime_type(); + let content_type = Some(self.final_mime_type()); Document::new( win, HasBrowsingContext::No, @@ -1598,14 +1608,16 @@ impl XMLHttpRequest { // 3. If responseMIME’s parameters["charset"] exists, then set label to it. let response_charset = self .response_mime_type() - .and_then(|mime| mime.get_parameter(CHARSET).map(|c| c.to_string())); + .get_parameter(CHARSET) + .map(ToString::to_string); // 4. If xhr’s override MIME type’s parameters["charset"] exists, then set label to it. let override_charset = self .override_mime_type .borrow() .as_ref() - .and_then(|mime| mime.get_parameter(CHARSET).map(|c| c.to_string())); + .and_then(|mime| mime.get_parameter(CHARSET)) + .map(ToString::to_string); // 5. If label is null, then return null. // 6. Let encoding be the result of getting an encoding from label. @@ -1617,23 +1629,22 @@ impl XMLHttpRequest { } /// <https://xhr.spec.whatwg.org/#response-mime-type> - fn response_mime_type(&self) -> Option<Mime> { - return extract_mime_type(&self.response_headers.borrow()) - .and_then(|mime_as_bytes| { - String::from_utf8(mime_as_bytes) - .unwrap_or_default() - .parse() - .ok() - }) - .or(Some(Mime::new(TEXT, XML))); + fn response_mime_type(&self) -> Mime { + // 1. Let mimeType be the result of extracting a MIME type from xhr’s response’s + // header list. + // 2. If mimeType is failure, then set mimeType to text/xml. + // 3. Return mimeType. + extract_mime_type_as_dataurl_mime(&self.response_headers.borrow()) + .unwrap_or_else(|| Mime::new(TEXT, XML)) } /// <https://xhr.spec.whatwg.org/#final-mime-type> - fn final_mime_type(&self) -> Option<Mime> { - match *self.override_mime_type.borrow() { - Some(ref override_mime) => Some(override_mime.clone()), - None => self.response_mime_type(), - } + fn final_mime_type(&self) -> Mime { + self.override_mime_type + .borrow() + .as_ref() + .map(MimeExt::clone) + .unwrap_or_else(|| self.response_mime_type()) } } |