Implement the create an element for token algorithm

This commit is contained in:
Connor Brewster 2017-11-27 13:55:46 -06:00 committed by Josh Matthews
parent 9b3fc43f5a
commit 87475b11d3
4 changed files with 127 additions and 40 deletions

View file

@ -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,
&*self.document, attrs,
ElementCreator::ParserCreated(current_line), &*self.document,
CustomElementCreationMode::Synchronous); ElementCreator::ParserCreated(current_line),
for attr in attrs { ParsingAlgorithm::Normal
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);

View file

@ -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 {

View file

@ -101,6 +101,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 +134,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 +180,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 +194,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,
LastChunkState::NotReceived, Tokenizer::Html(self::html::Tokenizer::new(
ParserKind::ScriptCreated); document,
url,
None,
ParsingAlgorithm::Normal,
)),
LastChunkState::NotReceived,
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 +776,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 +824,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,
&*self.document, attrs,
ElementCreator::ParserCreated(self.current_line), &*self.document,
CustomElementCreationMode::Synchronous); ElementCreator::ParserCreated(self.current_line),
self.parsing_algorithm,
for attr in attrs { );
elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None); Dom::from_ref(element.upcast())
}
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 +976,62 @@ 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.
// TODO: handle throw-on-dynamic-markup-insertion counter.
// Step 6.2
// TODO: If the JavaScript execution context stack is empty, then 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.
// TODO: handle throw-on-dynamic-markup-insertion counter.
}
// TODO: Step 10.
// TODO: Step 11.
// Step 12 is handled in `associate_with_form`.
// Step 13.
element
}

View file

@ -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());