mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #19397 - cbrewster:create_element_for_token, r=jdm,nox
Implement the create an element for token algorithm <!-- 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 #19392 and fix #19393 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19397) <!-- Reviewable:end -->
This commit is contained in:
commit
fa82a6bbce
8 changed files with 165 additions and 50 deletions
|
@ -35,6 +35,12 @@ pub unsafe fn trace(tracer: *mut JSTracer) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_execution_stack_empty() -> bool {
|
||||||
|
STACK.with(|stack| {
|
||||||
|
stack.borrow().is_empty()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// RAII struct that pushes and pops entries from the script settings stack.
|
/// RAII struct that pushes and pops entries from the script settings stack.
|
||||||
pub struct AutoEntryScript {
|
pub struct AutoEntryScript {
|
||||||
global: DomRoot<GlobalScope>,
|
global: DomRoot<GlobalScope>,
|
||||||
|
|
|
@ -364,6 +364,8 @@ pub struct Document {
|
||||||
tti_window: DomRefCell<InteractiveWindow>,
|
tti_window: DomRefCell<InteractiveWindow>,
|
||||||
/// RAII canceller for Fetch
|
/// RAII canceller for Fetch
|
||||||
canceller: FetchCanceller,
|
canceller: FetchCanceller,
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#throw-on-dynamic-markup-insertion-counter
|
||||||
|
throw_on_dynamic_markup_insertion_counter: Cell<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
@ -1894,7 +1896,12 @@ impl Document {
|
||||||
|
|
||||||
pub fn can_invoke_script(&self) -> bool {
|
pub fn can_invoke_script(&self) -> bool {
|
||||||
match self.get_current_parser() {
|
match self.get_current_parser() {
|
||||||
Some(parser) => parser.parser_is_not_active(),
|
Some(parser) => {
|
||||||
|
// It is safe to run script if the parser is not actively parsing,
|
||||||
|
// or if it is impossible to interact with the token stream.
|
||||||
|
parser.parser_is_not_active() ||
|
||||||
|
self.throw_on_dynamic_markup_insertion_counter.get() > 0
|
||||||
|
}
|
||||||
None => true,
|
None => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2053,6 +2060,16 @@ impl Document {
|
||||||
let global_scope = self.window.upcast::<GlobalScope>();
|
let global_scope = self.window.upcast::<GlobalScope>();
|
||||||
global_scope.script_to_constellation_chan().send(msg).unwrap();
|
global_scope.script_to_constellation_chan().send(msg).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
|
||||||
|
let counter = self.throw_on_dynamic_markup_insertion_counter.get();
|
||||||
|
self.throw_on_dynamic_markup_insertion_counter.set(counter + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
|
||||||
|
let counter = self.throw_on_dynamic_markup_insertion_counter.get();
|
||||||
|
self.throw_on_dynamic_markup_insertion_counter.set(counter - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(MallocSizeOf, PartialEq)]
|
#[derive(MallocSizeOf, PartialEq)]
|
||||||
|
@ -2294,6 +2311,7 @@ impl Document {
|
||||||
interactive_time: DomRefCell::new(interactive_time),
|
interactive_time: DomRefCell::new(interactive_time),
|
||||||
tti_window: DomRefCell::new(InteractiveWindow::new()),
|
tti_window: DomRefCell::new(InteractiveWindow::new()),
|
||||||
canceller: canceller,
|
canceller: canceller,
|
||||||
|
throw_on_dynamic_markup_insertion_counter: Cell::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3717,7 +3735,9 @@ impl DocumentMethods for Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
// TODO: handle throw-on-dynamic-markup-insertion counter.
|
if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
|
||||||
|
return Err(Error::InvalidState);
|
||||||
|
}
|
||||||
|
|
||||||
if !self.is_active() {
|
if !self.is_active() {
|
||||||
// Step 3.
|
// Step 3.
|
||||||
|
@ -3863,7 +3883,10 @@ impl DocumentMethods for Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
// TODO: handle throw-on-dynamic-markup-insertion counter.
|
if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
|
||||||
|
return Err(Error::InvalidState);
|
||||||
|
}
|
||||||
|
|
||||||
if !self.is_active() {
|
if !self.is_active() {
|
||||||
// Step 3.
|
// Step 3.
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -3910,7 +3933,9 @@ impl DocumentMethods for Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
// TODO: handle throw-on-dynamic-markup-insertion counter.
|
if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
|
||||||
|
return Err(Error::InvalidState);
|
||||||
|
}
|
||||||
|
|
||||||
let parser = match self.get_current_parser() {
|
let parser = match self.get_current_parser() {
|
||||||
Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
|
Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
|
||||||
|
|
|
@ -12,14 +12,15 @@ use dom::bindings::str::DOMString;
|
||||||
use dom::comment::Comment;
|
use dom::comment::Comment;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::documenttype::DocumentType;
|
use dom::documenttype::DocumentType;
|
||||||
use dom::element::{CustomElementCreationMode, Element, ElementCreator};
|
use dom::element::{Element, ElementCreator};
|
||||||
use dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
|
use dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
|
||||||
use dom::htmlscriptelement::HTMLScriptElement;
|
use dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use dom::htmltemplateelement::HTMLTemplateElement;
|
use dom::htmltemplateelement::HTMLTemplateElement;
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::processinginstruction::ProcessingInstruction;
|
use dom::processinginstruction::ProcessingInstruction;
|
||||||
|
use dom::servoparser::{ElementAttribute, create_element_for_token, ParsingAlgorithm};
|
||||||
use dom::virtualmethods::vtable_for;
|
use dom::virtualmethods::vtable_for;
|
||||||
use html5ever::{Attribute as HtmlAttribute, ExpandedName, LocalName, QualName};
|
use html5ever::{Attribute as HtmlAttribute, ExpandedName, QualName};
|
||||||
use html5ever::buffer_queue::BufferQueue;
|
use html5ever::buffer_queue::BufferQueue;
|
||||||
use html5ever::tendril::{SendTendril, StrTendril, Tendril};
|
use html5ever::tendril::{SendTendril, StrTendril, Tendril};
|
||||||
use html5ever::tendril::fmt::UTF8;
|
use html5ever::tendril::fmt::UTF8;
|
||||||
|
@ -335,20 +336,18 @@ impl Tokenizer {
|
||||||
self.insert_node(contents, Dom::from_ref(template.Content().upcast()));
|
self.insert_node(contents, Dom::from_ref(template.Content().upcast()));
|
||||||
}
|
}
|
||||||
ParseOperation::CreateElement { node, name, attrs, current_line } => {
|
ParseOperation::CreateElement { node, name, attrs, current_line } => {
|
||||||
let is = attrs.iter()
|
let attrs = attrs
|
||||||
.find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
|
.into_iter()
|
||||||
.map(|attr| LocalName::from(&*attr.value));
|
.map(|attr| ElementAttribute::new(attr.name, DOMString::from(attr.value)))
|
||||||
|
.collect();
|
||||||
let elem = Element::create(name,
|
let element = create_element_for_token(
|
||||||
is,
|
name,
|
||||||
|
attrs,
|
||||||
&*self.document,
|
&*self.document,
|
||||||
ElementCreator::ParserCreated(current_line),
|
ElementCreator::ParserCreated(current_line),
|
||||||
CustomElementCreationMode::Synchronous);
|
ParsingAlgorithm::Normal
|
||||||
for attr in attrs {
|
);
|
||||||
elem.set_attribute_from_parser(attr.name, DOMString::from(attr.value), None);
|
self.insert_node(node, Dom::from_ref(element.upcast()));
|
||||||
}
|
|
||||||
|
|
||||||
self.insert_node(node, Dom::from_ref(elem.upcast()));
|
|
||||||
}
|
}
|
||||||
ParseOperation::CreateComment { text, node } => {
|
ParseOperation::CreateComment { text, node } => {
|
||||||
let comment = Comment::new(DOMString::from(text), document);
|
let comment = Comment::new(DOMString::from(text), document);
|
||||||
|
|
|
@ -16,7 +16,7 @@ use dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use dom::htmltemplateelement::HTMLTemplateElement;
|
use dom::htmltemplateelement::HTMLTemplateElement;
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::processinginstruction::ProcessingInstruction;
|
use dom::processinginstruction::ProcessingInstruction;
|
||||||
use dom::servoparser::Sink;
|
use dom::servoparser::{ParsingAlgorithm, Sink};
|
||||||
use html5ever::QualName;
|
use html5ever::QualName;
|
||||||
use html5ever::buffer_queue::BufferQueue;
|
use html5ever::buffer_queue::BufferQueue;
|
||||||
use html5ever::serialize::{AttrRef, Serialize, Serializer};
|
use html5ever::serialize::{AttrRef, Serialize, Serializer};
|
||||||
|
@ -39,13 +39,15 @@ impl Tokenizer {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
document: &Document,
|
document: &Document,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
fragment_context: Option<super::FragmentContext>)
|
fragment_context: Option<super::FragmentContext>,
|
||||||
|
parsing_algorithm: ParsingAlgorithm)
|
||||||
-> Self {
|
-> Self {
|
||||||
let sink = Sink {
|
let sink = Sink {
|
||||||
base_url: url,
|
base_url: url,
|
||||||
document: Dom::from_ref(document),
|
document: Dom::from_ref(document),
|
||||||
current_line: 1,
|
current_line: 1,
|
||||||
script: Default::default(),
|
script: Default::default(),
|
||||||
|
parsing_algorithm: parsing_algorithm,
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = TreeBuilderOpts {
|
let options = TreeBuilderOpts {
|
||||||
|
|
|
@ -13,6 +13,7 @@ use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||||
use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference};
|
use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference};
|
||||||
|
use dom::bindings::settings_stack::is_execution_stack_empty;
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::characterdata::CharacterData;
|
use dom::characterdata::CharacterData;
|
||||||
use dom::comment::Comment;
|
use dom::comment::Comment;
|
||||||
|
@ -101,6 +102,26 @@ enum LastChunkState {
|
||||||
NotReceived,
|
NotReceived,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ElementAttribute {
|
||||||
|
name: QualName,
|
||||||
|
value: DOMString
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
|
||||||
|
pub enum ParsingAlgorithm {
|
||||||
|
Normal,
|
||||||
|
Fragment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementAttribute {
|
||||||
|
pub fn new(name: QualName, value: DOMString) -> ElementAttribute {
|
||||||
|
ElementAttribute {
|
||||||
|
name: name,
|
||||||
|
value: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ServoParser {
|
impl ServoParser {
|
||||||
pub fn parser_is_not_active(&self) -> bool {
|
pub fn parser_is_not_active(&self) -> bool {
|
||||||
self.can_write() || self.tokenizer.try_borrow_mut().is_ok()
|
self.can_write() || self.tokenizer.try_borrow_mut().is_ok()
|
||||||
|
@ -114,7 +135,7 @@ impl ServoParser {
|
||||||
ParserKind::Normal)
|
ParserKind::Normal)
|
||||||
} else {
|
} else {
|
||||||
ServoParser::new(document,
|
ServoParser::new(document,
|
||||||
Tokenizer::Html(self::html::Tokenizer::new(document, url, None)),
|
Tokenizer::Html(self::html::Tokenizer::new(document, url, None, ParsingAlgorithm::Normal)),
|
||||||
LastChunkState::NotReceived,
|
LastChunkState::NotReceived,
|
||||||
ParserKind::Normal)
|
ParserKind::Normal)
|
||||||
};
|
};
|
||||||
|
@ -160,7 +181,8 @@ impl ServoParser {
|
||||||
let parser = ServoParser::new(&document,
|
let parser = ServoParser::new(&document,
|
||||||
Tokenizer::Html(self::html::Tokenizer::new(&document,
|
Tokenizer::Html(self::html::Tokenizer::new(&document,
|
||||||
url,
|
url,
|
||||||
Some(fragment_context))),
|
Some(fragment_context),
|
||||||
|
ParsingAlgorithm::Fragment)),
|
||||||
LastChunkState::Received,
|
LastChunkState::Received,
|
||||||
ParserKind::Normal);
|
ParserKind::Normal);
|
||||||
parser.parse_string_chunk(String::from(input));
|
parser.parse_string_chunk(String::from(input));
|
||||||
|
@ -173,10 +195,17 @@ impl ServoParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_html_script_input(document: &Document, url: ServoUrl, type_: &str) {
|
pub fn parse_html_script_input(document: &Document, url: ServoUrl, type_: &str) {
|
||||||
let parser = ServoParser::new(document,
|
let parser = ServoParser::new(
|
||||||
Tokenizer::Html(self::html::Tokenizer::new(document, url, None)),
|
document,
|
||||||
|
Tokenizer::Html(self::html::Tokenizer::new(
|
||||||
|
document,
|
||||||
|
url,
|
||||||
|
None,
|
||||||
|
ParsingAlgorithm::Normal,
|
||||||
|
)),
|
||||||
LastChunkState::NotReceived,
|
LastChunkState::NotReceived,
|
||||||
ParserKind::ScriptCreated);
|
ParserKind::ScriptCreated,
|
||||||
|
);
|
||||||
document.set_current_parser(Some(&parser));
|
document.set_current_parser(Some(&parser));
|
||||||
if !type_.eq_ignore_ascii_case("text/html") {
|
if !type_.eq_ignore_ascii_case("text/html") {
|
||||||
parser.parse_string_chunk("<pre>\n".to_owned());
|
parser.parse_string_chunk("<pre>\n".to_owned());
|
||||||
|
@ -748,6 +777,7 @@ pub struct Sink {
|
||||||
document: Dom<Document>,
|
document: Dom<Document>,
|
||||||
current_line: u64,
|
current_line: u64,
|
||||||
script: MutNullableDom<HTMLScriptElement>,
|
script: MutNullableDom<HTMLScriptElement>,
|
||||||
|
parsing_algorithm: ParsingAlgorithm,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sink {
|
impl Sink {
|
||||||
|
@ -795,21 +825,18 @@ impl TreeSink for Sink {
|
||||||
|
|
||||||
fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>, _flags: ElementFlags)
|
fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>, _flags: ElementFlags)
|
||||||
-> Dom<Node> {
|
-> Dom<Node> {
|
||||||
let is = attrs.iter()
|
let attrs = attrs
|
||||||
.find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
|
.into_iter()
|
||||||
.map(|attr| LocalName::from(&*attr.value));
|
.map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
|
||||||
|
.collect();
|
||||||
let elem = Element::create(name,
|
let element = create_element_for_token(
|
||||||
is,
|
name,
|
||||||
|
attrs,
|
||||||
&*self.document,
|
&*self.document,
|
||||||
ElementCreator::ParserCreated(self.current_line),
|
ElementCreator::ParserCreated(self.current_line),
|
||||||
CustomElementCreationMode::Synchronous);
|
self.parsing_algorithm,
|
||||||
|
);
|
||||||
for attr in attrs {
|
Dom::from_ref(element.upcast())
|
||||||
elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dom::from_ref(elem.upcast())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_comment(&mut self, text: StrTendril) -> Dom<Node> {
|
fn create_comment(&mut self, text: StrTendril) -> Dom<Node> {
|
||||||
|
@ -950,3 +977,64 @@ impl TreeSink for Sink {
|
||||||
vtable_for(&node).pop();
|
vtable_for(&node).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#create-an-element-for-the-token
|
||||||
|
fn create_element_for_token(
|
||||||
|
name: QualName,
|
||||||
|
attrs: Vec<ElementAttribute>,
|
||||||
|
document: &Document,
|
||||||
|
creator: ElementCreator,
|
||||||
|
parsing_algorithm: ParsingAlgorithm,
|
||||||
|
) -> DomRoot<Element> {
|
||||||
|
// Step 3.
|
||||||
|
let is = attrs.iter()
|
||||||
|
.find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
|
||||||
|
.map(|attr| LocalName::from(&*attr.value));
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
|
let definition = document.lookup_custom_element_definition(&name.ns, &name.local, is.as_ref());
|
||||||
|
|
||||||
|
// Step 5.
|
||||||
|
let will_execute_script = definition.is_some() && parsing_algorithm != ParsingAlgorithm::Fragment;
|
||||||
|
|
||||||
|
// Step 6.
|
||||||
|
if will_execute_script {
|
||||||
|
// Step 6.1.
|
||||||
|
document.increment_throw_on_dynamic_markup_insertion_counter();
|
||||||
|
// Step 6.2
|
||||||
|
if is_execution_stack_empty() {
|
||||||
|
document.window().upcast::<GlobalScope>().perform_a_microtask_checkpoint();
|
||||||
|
}
|
||||||
|
// Step 6.3
|
||||||
|
ScriptThread::push_new_element_queue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7.
|
||||||
|
let creation_mode = if will_execute_script {
|
||||||
|
CustomElementCreationMode::Synchronous
|
||||||
|
} else {
|
||||||
|
CustomElementCreationMode::Asynchronous
|
||||||
|
};
|
||||||
|
let element = Element::create(name, is, document, creator, creation_mode);
|
||||||
|
|
||||||
|
// Step 8.
|
||||||
|
for attr in attrs {
|
||||||
|
element.set_attribute_from_parser(attr.name, attr.value, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9.
|
||||||
|
if will_execute_script {
|
||||||
|
// Steps 9.1 - 9.2.
|
||||||
|
ScriptThread::pop_current_element_queue();
|
||||||
|
// Step 9.3.
|
||||||
|
document.decrement_throw_on_dynamic_markup_insertion_counter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Step 10.
|
||||||
|
// TODO: Step 11.
|
||||||
|
|
||||||
|
// Step 12 is handled in `associate_with_form`.
|
||||||
|
|
||||||
|
// Step 13.
|
||||||
|
element
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use dom::bindings::trace::JSTraceable;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::htmlscriptelement::HTMLScriptElement;
|
use dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::servoparser::Sink;
|
use dom::servoparser::{ParsingAlgorithm, Sink};
|
||||||
use js::jsapi::JSTracer;
|
use js::jsapi::JSTracer;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use xml5ever::buffer_queue::BufferQueue;
|
use xml5ever::buffer_queue::BufferQueue;
|
||||||
|
@ -30,6 +30,7 @@ impl Tokenizer {
|
||||||
document: Dom::from_ref(document),
|
document: Dom::from_ref(document),
|
||||||
current_line: 1,
|
current_line: 1,
|
||||||
script: Default::default(),
|
script: Default::default(),
|
||||||
|
parsing_algorithm: ParsingAlgorithm::Normal,
|
||||||
};
|
};
|
||||||
|
|
||||||
let tb = XmlTreeBuilder::new(sink, Default::default());
|
let tb = XmlTreeBuilder::new(sink, Default::default());
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[microtasks-and-constructors.html]
|
|
||||||
expected: CRASH
|
|
||||||
bug: https://github.com/servo/servo/issues/19392
|
|
|
@ -1,3 +0,0 @@
|
||||||
[parser-fallsback-to-unknown-element.html]
|
|
||||||
expected: CRASH
|
|
||||||
bug: https://github.com/servo/servo/issues/19392
|
|
Loading…
Add table
Add a link
Reference in a new issue