Ensure parsers initiated from DOMParser always complete. (#33056)

* Ensure parsers initiated from DOMParser always complete.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* Add test for parseFromString with async parser.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* Add expected failure.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2024-08-15 19:45:00 -04:00 committed by GitHub
parent 3cc91e655f
commit 69185c4af1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 36 additions and 34 deletions

View file

@ -128,12 +128,6 @@ pub struct ServoParser {
prefetch_input: DomRefCell<BufferQueue>, prefetch_input: DomRefCell<BufferQueue>,
} }
#[derive(PartialEq)]
enum LastChunkState {
Received,
NotReceived,
}
pub struct ElementAttribute { pub struct ElementAttribute {
name: QualName, name: QualName,
value: DOMString, value: DOMString,
@ -161,7 +155,6 @@ impl ServoParser {
ServoParser::new( ServoParser::new(
document, document,
Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None)), Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None)),
LastChunkState::NotReceived,
ParserKind::Normal, ParserKind::Normal,
) )
} else { } else {
@ -173,14 +166,13 @@ impl ServoParser {
None, None,
ParsingAlgorithm::Normal, ParsingAlgorithm::Normal,
)), )),
LastChunkState::NotReceived,
ParserKind::Normal, ParserKind::Normal,
) )
}; };
// Set as the document's current parser and initialize with `input`, if given. // Set as the document's current parser and initialize with `input`, if given.
if let Some(input) = input { if let Some(input) = input {
parser.parse_string_chunk(String::from(input)); parser.parse_complete_string_chunk(String::from(input));
} else { } else {
parser.document.set_current_parser(Some(&parser)); parser.document.set_current_parser(Some(&parser));
} }
@ -239,10 +231,9 @@ impl ServoParser {
Some(fragment_context), Some(fragment_context),
ParsingAlgorithm::Fragment, ParsingAlgorithm::Fragment,
)), )),
LastChunkState::Received,
ParserKind::Normal, ParserKind::Normal,
); );
parser.parse_string_chunk(String::from(input)); parser.parse_complete_string_chunk(String::from(input));
// Step 14. // Step 14.
let root_element = document.GetDocumentElement().expect("no document element"); let root_element = document.GetDocumentElement().expect("no document element");
@ -260,7 +251,6 @@ impl ServoParser {
None, None,
ParsingAlgorithm::Normal, ParsingAlgorithm::Normal,
)), )),
LastChunkState::NotReceived,
ParserKind::ScriptCreated, ParserKind::ScriptCreated,
); );
*parser.bom_sniff.borrow_mut() = None; *parser.bom_sniff.borrow_mut() = None;
@ -271,13 +261,12 @@ impl ServoParser {
let parser = ServoParser::new( let parser = ServoParser::new(
document, document,
Tokenizer::Xml(self::xml::Tokenizer::new(document, url)), Tokenizer::Xml(self::xml::Tokenizer::new(document, url)),
LastChunkState::NotReceived,
ParserKind::Normal, ParserKind::Normal,
); );
// Set as the document's current parser and initialize with `input`, if given. // Set as the document's current parser and initialize with `input`, if given.
if let Some(input) = input { if let Some(input) = input {
parser.parse_string_chunk(String::from(input)); parser.parse_complete_string_chunk(String::from(input));
} else { } else {
parser.document.set_current_parser(Some(&parser)); parser.document.set_current_parser(Some(&parser));
} }
@ -421,12 +410,7 @@ impl ServoParser {
} }
#[allow(crown::unrooted_must_root)] #[allow(crown::unrooted_must_root)]
fn new_inherited( fn new_inherited(document: &Document, tokenizer: Tokenizer, kind: ParserKind) -> Self {
document: &Document,
tokenizer: Tokenizer,
last_chunk_state: LastChunkState,
kind: ParserKind,
) -> Self {
ServoParser { ServoParser {
reflector: Reflector::new(), reflector: Reflector::new(),
document: Dom::from_ref(document), document: Dom::from_ref(document),
@ -435,7 +419,7 @@ impl ServoParser {
network_input: DomRefCell::new(BufferQueue::default()), network_input: DomRefCell::new(BufferQueue::default()),
script_input: DomRefCell::new(BufferQueue::default()), script_input: DomRefCell::new(BufferQueue::default()),
tokenizer: DomRefCell::new(tokenizer), tokenizer: DomRefCell::new(tokenizer),
last_chunk_received: Cell::new(last_chunk_state == LastChunkState::Received), last_chunk_received: Cell::new(false),
suspended: Default::default(), suspended: Default::default(),
script_nesting_level: Default::default(), script_nesting_level: Default::default(),
aborted: Default::default(), aborted: Default::default(),
@ -446,19 +430,9 @@ impl ServoParser {
} }
#[allow(crown::unrooted_must_root)] #[allow(crown::unrooted_must_root)]
fn new( fn new(document: &Document, tokenizer: Tokenizer, kind: ParserKind) -> DomRoot<Self> {
document: &Document,
tokenizer: Tokenizer,
last_chunk_state: LastChunkState,
kind: ParserKind,
) -> DomRoot<Self> {
reflect_dom_object( reflect_dom_object(
Box::new(ServoParser::new_inherited( Box::new(ServoParser::new_inherited(document, tokenizer, kind)),
document,
tokenizer,
last_chunk_state,
kind,
)),
document.window(), document.window(),
) )
} }
@ -579,9 +553,10 @@ impl ServoParser {
} }
} }
fn parse_string_chunk(&self, input: String) { fn parse_complete_string_chunk(&self, input: String) {
self.document.set_current_parser(Some(self)); self.document.set_current_parser(Some(self));
self.push_string_input_chunk(input); self.push_string_input_chunk(input);
self.last_chunk_received.set(true);
if !self.suspended.get() { if !self.suspended.get() {
self.parse_sync(); self.parse_sync();
} }

View file

@ -1,4 +1,7 @@
[responsexml-non-well-formed.htm] [responsexml-non-well-formed.htm]
[XMLHttpRequest: responseXML non well-formed tests]
expected: FAIL
[XMLHttpRequest: responseXML non well-formed tests 1] [XMLHttpRequest: responseXML non well-formed tests 1]
expected: FAIL expected: FAIL

View file

@ -12632,6 +12632,13 @@
} }
] ]
], ],
"DOMParser-parseFromString.html": [
"38715db5d923e4f153e2c4a2679f644fe1e6d14d",
[
null,
{}
]
],
"DOMParser.html": [ "DOMParser.html": [
"f386a3e0191af2c70dcb05790ce7db15dd5ccbf1", "f386a3e0191af2c70dcb05790ce7db15dd5ccbf1",
[ [

View file

@ -0,0 +1,2 @@
[DOMParser-parseFromString.html]
prefs: ["dom.servoparser.async_html_tokenizer.enabled:true"]

View file

@ -0,0 +1,15 @@
<!doctype html>
<head>
<title>Verify that parseFromString does not panic</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe src="missing"></iframe>
<script>
test(() => {
{
const parser = new DOMParser();
const doc = parser.parseFromString("", "text/html");
assert_equals(doc.URL, document.URL);
}
});
</script>