Skip to content

Commit 2b297a3

Browse files
committed
1 parent c661cc8 commit 2b297a3

File tree

8 files changed

+119
-60
lines changed

8 files changed

+119
-60
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/script/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ content-security-policy = { version = "0.4.0", features = ["serde"] }
4343
cookie = "0.11"
4444
crossbeam-channel = "0.4"
4545
cssparser = "0.27"
46+
data-url = "0.1.0"
4647
deny_public_fields = { path = "../deny_public_fields" }
4748
devtools_traits = { path = "../devtools_traits" }
4849
dom_struct = { path = "../dom_struct" }

components/script/dom/headers.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
1010
use crate::dom::bindings::root::DomRoot;
1111
use crate::dom::bindings::str::{is_token, ByteString};
1212
use crate::dom::globalscope::GlobalScope;
13+
use data_url::mime::Mime as DataUrlMime;
1314
use dom_struct::dom_struct;
14-
use http::header::{self, HeaderMap as HyperHeaders, HeaderName, HeaderValue};
15+
use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue};
1516
use net_traits::request::is_cors_safelisted_request_header;
1617
use std::cell::Cell;
1718
use std::str::{self, FromStr};
@@ -269,10 +270,7 @@ impl Headers {
269270

270271
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
271272
pub fn extract_mime_type(&self) -> Vec<u8> {
272-
self.header_list
273-
.borrow()
274-
.get(header::CONTENT_TYPE)
275-
.map_or(vec![], |v| v.as_bytes().to_owned())
273+
extract_mime_type(&*self.header_list.borrow()).unwrap_or(vec![])
276274
}
277275

278276
pub fn sort_header_list(&self) -> Vec<(String, Vec<u8>)> {
@@ -469,3 +467,69 @@ pub fn is_obs_text(x: u8) -> bool {
469467
_ => false,
470468
}
471469
}
470+
471+
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
472+
// This function uses data_url::Mime to parse the MIME Type because
473+
// mime::Mime does not provide a parser following the Fetch spec
474+
// see https://github.com/hyperium/mime/issues/106
475+
pub fn extract_mime_type(headers: &HyperHeaders) -> Option<Vec<u8>> {
476+
let mut charset: Option<String> = None;
477+
let mut essence: String = "".to_string();
478+
let mut mime_type: Option<DataUrlMime> = None;
479+
480+
// Step 4
481+
let headers_values = headers.get_all(http::header::CONTENT_TYPE).iter();
482+
483+
// Step 5
484+
if headers_values.size_hint() == (0, Some(0)) {
485+
return None;
486+
}
487+
488+
// Step 6
489+
for header_value in headers_values {
490+
// Step 6.1
491+
match DataUrlMime::from_str(header_value.to_str().unwrap_or("")) {
492+
// Step 6.2
493+
Err(_) => continue,
494+
Ok(temp_mime) => {
495+
let temp_essence = format!("{}/{}", temp_mime.type_, temp_mime.subtype);
496+
497+
// Step 6.2
498+
if temp_essence == "*/*" {
499+
continue;
500+
}
501+
502+
let temp_charset = &temp_mime.get_parameter("charset");
503+
504+
// Step 6.3
505+
mime_type = Some(DataUrlMime {
506+
type_: temp_mime.type_.to_string(),
507+
subtype: temp_mime.subtype.to_string(),
508+
parameters: vec![],
509+
});
510+
511+
// Step 6.4
512+
if temp_essence != essence {
513+
charset = temp_charset.map(|c| c.to_string());
514+
essence = temp_essence.to_owned();
515+
} else {
516+
// Step 6.5
517+
if temp_charset.is_none() && charset.is_some() {
518+
let DataUrlMime {
519+
type_: t,
520+
subtype: st,
521+
parameters: _,
522+
} = mime_type.unwrap();
523+
mime_type = Some(DataUrlMime {
524+
type_: t.to_string(),
525+
subtype: st.to_string(),
526+
parameters: vec![("charset".to_string(), charset.clone().unwrap())],
527+
})
528+
}
529+
}
530+
},
531+
}
532+
}
533+
534+
return mime_type.map(|m| format!("{}", m).into_bytes());
535+
}

components/script/dom/xmlhttprequest.rs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::dom::document::{Document, HasBrowsingContext, IsHTMLDocument};
2222
use crate::dom::event::{Event, EventBubbles, EventCancelable};
2323
use crate::dom::eventtarget::EventTarget;
2424
use crate::dom::globalscope::GlobalScope;
25-
use crate::dom::headers::is_forbidden_header_name;
25+
use crate::dom::headers::{extract_mime_type, is_forbidden_header_name};
2626
use crate::dom::node::Node;
2727
use crate::dom::performanceresourcetiming::InitiatorType;
2828
use crate::dom::progressevent::ProgressEvent;
@@ -1364,49 +1364,67 @@ impl XMLHttpRequest {
13641364
// Caching: if we have existing response xml, redirect it directly
13651365
let response = self.response_xml.get();
13661366
if response.is_some() {
1367-
return self.response_xml.get();
1367+
return response;
13681368
}
13691369

13701370
// Step 1
13711371
if self.response_status.get().is_err() {
13721372
return None;
13731373
}
13741374

1375+
// Step 2
13751376
let mime_type = self.final_mime_type();
1376-
// TODO: prescan the response to determine encoding if final charset is null
1377+
// Step 5.3, 7
13771378
let charset = self.final_charset().unwrap_or(UTF_8);
13781379
let temp_doc: DomRoot<Document>;
13791380
match mime_type {
13801381
Some(ref mime) if mime.type_() == mime::TEXT && mime.subtype() == mime::HTML => {
1381-
// Step 5
1382+
// Step 4
13821383
if self.response_type.get() == XMLHttpRequestResponseType::_empty {
13831384
return None;
13841385
} else {
1385-
// Step 6
1386+
// TODO Step 5.2 "If charset is null, prescan the first 1024 bytes of xhr’s received bytes"
1387+
// Step 5
13861388
temp_doc = self.document_text_html();
13871389
}
13881390
},
13891391
// Step 7
1392+
None => {
1393+
temp_doc = self.handle_xml();
1394+
// Not sure it the parser should throw an error for this case
1395+
// The specification does not indicates this test,
1396+
// but for now we check the document has no child nodes
1397+
let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none();
1398+
if has_no_child_nodes {
1399+
return None;
1400+
}
1401+
},
13901402
Some(ref mime)
13911403
if (mime.type_() == mime::TEXT && mime.subtype() == mime::XML) ||
1392-
(mime.type_() == mime::APPLICATION && mime.subtype() == mime::XML) =>
1404+
(mime.type_() == mime::APPLICATION && mime.subtype() == mime::XML) ||
1405+
mime.suffix() == Some(mime::XML) =>
13931406
{
13941407
temp_doc = self.handle_xml();
1408+
// Not sure it the parser should throw an error for this case
1409+
// The specification does not indicates this test,
1410+
// but for now we check the document has no child nodes
1411+
let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none();
1412+
if has_no_child_nodes {
1413+
return None;
1414+
}
13951415
}
1396-
None => {
1397-
temp_doc = self.handle_xml();
1398-
},
1399-
Some(ref mime) if mime.suffix() == Some(mime::XML) => {
1400-
temp_doc = self.handle_xml();
1401-
},
1402-
// Step 4
1416+
// Step 3
14031417
_ => {
14041418
return None;
14051419
},
14061420
}
1407-
// Step 9
1421+
// Step 8
14081422
temp_doc.set_encoding(charset);
1409-
// Step 13
1423+
1424+
// Step 9 to 11
1425+
// Done by handle_text_html and handle_xml
1426+
1427+
// Step 12
14101428
self.response_xml.set(Some(&temp_doc));
14111429
return self.response_xml.get();
14121430
}
@@ -1585,14 +1603,25 @@ impl XMLHttpRequest {
15851603
}
15861604
}
15871605

1606+
/// <https://xhr.spec.whatwg.org/#response-mime-type>
1607+
fn response_mime_type(&self) -> Option<Mime> {
1608+
return extract_mime_type(&self.response_headers.borrow())
1609+
.map(|mime_as_bytes| {
1610+
String::from_utf8(mime_as_bytes)
1611+
.unwrap_or_default()
1612+
.parse()
1613+
.ok()
1614+
})
1615+
.flatten()
1616+
.or(Some(mime::TEXT_XML));
1617+
}
1618+
1619+
/// <https://xhr.spec.whatwg.org/#final-mime-type>
15881620
fn final_mime_type(&self) -> Option<Mime> {
15891621
if self.override_mime_type.borrow().is_some() {
15901622
self.override_mime_type.borrow().clone()
15911623
} else {
1592-
match self.response_headers.borrow().typed_get::<ContentType>() {
1593-
Some(ct) => Some(ct.into()),
1594-
None => None,
1595-
}
1624+
return self.response_mime_type();
15961625
}
15971626
}
15981627
}

tests/wpt/metadata/fetch/content-type/response.window.js.ini

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,6 @@
9696
[Request: combined response Content-Type: text/plain;charset=gbk text/plain]
9797
expected: NOTRUN
9898

99-
[fetch(): separate response Content-Type: text/plain]
100-
expected: FAIL
101-
10299
[fetch(): combined response Content-Type: text/html;" \\" text/plain ";charset=GBK]
103100
expected: NOTRUN
104101

@@ -312,27 +309,6 @@
312309
[fetch(): separate response Content-Type: text/plain ]
313310
expected: NOTRUN
314311

315-
[<iframe>: separate response Content-Type: text/html;x=" text/plain]
316-
expected: FAIL
317-
318-
[<iframe>: combined response Content-Type: text/html;" \\" text/plain]
319-
expected: FAIL
320-
321-
[<iframe>: combined response Content-Type: text/html */*]
322-
expected: FAIL
323-
324-
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
325-
expected: FAIL
326-
327-
[<iframe>: combined response Content-Type: text/html;x=" text/plain]
328-
expected: FAIL
329-
330-
[<iframe>: separate response Content-Type: text/html */*;charset=gbk]
331-
expected: FAIL
332-
333312
[<iframe>: separate response Content-Type: text/html */*]
334313
expected: FAIL
335314

336-
[<iframe>: separate response Content-Type: text/html;" \\" text/plain]
337-
expected: FAIL
338-

tests/wpt/metadata/xhr/responsexml-basic.htm.ini

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/wpt/metadata/xhr/responsexml-media-type.htm.ini

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/wpt/metadata/xhr/responsexml-non-well-formed.htm.ini

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
[responsexml-non-well-formed.htm]
22
type: testharness
3-
[XMLHttpRequest: responseXML non well-formed tests]
4-
expected: FAIL
5-
63
[XMLHttpRequest: responseXML non well-formed tests 1]
74
expected: FAIL
85

0 commit comments

Comments
 (0)