Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the create an element for token algorithm #19397

Merged
merged 4 commits into from Jan 17, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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.
pub struct AutoEntryScript {
global: DomRoot<GlobalScope>,
@@ -364,6 +364,8 @@ pub struct Document {
tti_window: DomRefCell<InteractiveWindow>,
/// RAII canceller for Fetch
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)]
@@ -1894,7 +1896,12 @@ impl Document {

pub fn can_invoke_script(&self) -> bool {
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,
}
}
@@ -2053,6 +2060,16 @@ impl Document {
let global_scope = self.window.upcast::<GlobalScope>();
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)]
@@ -2294,6 +2311,7 @@ impl Document {
interactive_time: DomRefCell::new(interactive_time),
tti_window: DomRefCell::new(InteractiveWindow::new()),
canceller: canceller,
throw_on_dynamic_markup_insertion_counter: Cell::new(0),
}
}

@@ -3717,7 +3735,9 @@ impl DocumentMethods for Document {
}

// 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() {
// Step 3.
@@ -3863,7 +3883,10 @@ impl DocumentMethods for Document {
}

// 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() {
// Step 3.
return Ok(());
@@ -3910,7 +3933,9 @@ impl DocumentMethods for Document {
}

// 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() {
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::document::Document;
use dom::documenttype::DocumentType;
use dom::element::{CustomElementCreationMode, Element, ElementCreator};
use dom::element::{Element, ElementCreator};
use dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmltemplateelement::HTMLTemplateElement;
use dom::node::Node;
use dom::processinginstruction::ProcessingInstruction;
use dom::servoparser::{ElementAttribute, create_element_for_token, ParsingAlgorithm};
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::tendril::{SendTendril, StrTendril, Tendril};
use html5ever::tendril::fmt::UTF8;
@@ -335,20 +336,18 @@ impl Tokenizer {
self.insert_node(contents, Dom::from_ref(template.Content().upcast()));
}
ParseOperation::CreateElement { node, name, attrs, current_line } => {
let is = attrs.iter()
.find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
.map(|attr| LocalName::from(&*attr.value));

let elem = Element::create(name,
is,
&*self.document,
ElementCreator::ParserCreated(current_line),
CustomElementCreationMode::Synchronous);
for attr in attrs {
elem.set_attribute_from_parser(attr.name, DOMString::from(attr.value), None);
}

self.insert_node(node, Dom::from_ref(elem.upcast()));
let attrs = attrs
.into_iter()
.map(|attr| ElementAttribute::new(attr.name, DOMString::from(attr.value)))
.collect();
let element = create_element_for_token(
name,
attrs,
&*self.document,
ElementCreator::ParserCreated(current_line),
ParsingAlgorithm::Normal
);
self.insert_node(node, Dom::from_ref(element.upcast()));
}
ParseOperation::CreateComment { text, node } => {
let comment = Comment::new(DOMString::from(text), document);
@@ -16,7 +16,7 @@ use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmltemplateelement::HTMLTemplateElement;
use dom::node::Node;
use dom::processinginstruction::ProcessingInstruction;
use dom::servoparser::Sink;
use dom::servoparser::{ParsingAlgorithm, Sink};
use html5ever::QualName;
use html5ever::buffer_queue::BufferQueue;
use html5ever::serialize::{AttrRef, Serialize, Serializer};
@@ -39,13 +39,15 @@ impl Tokenizer {
pub fn new(
document: &Document,
url: ServoUrl,
fragment_context: Option<super::FragmentContext>)
fragment_context: Option<super::FragmentContext>,
parsing_algorithm: ParsingAlgorithm)
-> Self {
let sink = Sink {
base_url: url,
document: Dom::from_ref(document),
current_line: 1,
script: Default::default(),
parsing_algorithm: parsing_algorithm,
};

let options = TreeBuilderOpts {
@@ -13,6 +13,7 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference};
use dom::bindings::settings_stack::is_execution_stack_empty;
use dom::bindings::str::DOMString;
use dom::characterdata::CharacterData;
use dom::comment::Comment;
@@ -101,6 +102,26 @@ enum LastChunkState {
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 {
pub fn parser_is_not_active(&self) -> bool {
self.can_write() || self.tokenizer.try_borrow_mut().is_ok()
@@ -114,7 +135,7 @@ impl ServoParser {
ParserKind::Normal)
} else {
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,
ParserKind::Normal)
};
@@ -160,7 +181,8 @@ impl ServoParser {
let parser = ServoParser::new(&document,
Tokenizer::Html(self::html::Tokenizer::new(&document,
url,
Some(fragment_context))),
Some(fragment_context),
ParsingAlgorithm::Fragment)),
LastChunkState::Received,
ParserKind::Normal);
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) {
let parser = ServoParser::new(document,
Tokenizer::Html(self::html::Tokenizer::new(document, url, None)),
LastChunkState::NotReceived,
ParserKind::ScriptCreated);
let parser = ServoParser::new(
document,
Tokenizer::Html(self::html::Tokenizer::new(
document,
url,
None,
ParsingAlgorithm::Normal,
)),
LastChunkState::NotReceived,
ParserKind::ScriptCreated,
);
document.set_current_parser(Some(&parser));
if !type_.eq_ignore_ascii_case("text/html") {
parser.parse_string_chunk("<pre>\n".to_owned());
@@ -748,6 +777,7 @@ pub struct Sink {
document: Dom<Document>,
current_line: u64,
script: MutNullableDom<HTMLScriptElement>,
parsing_algorithm: ParsingAlgorithm,
}

impl Sink {
@@ -795,21 +825,18 @@ impl TreeSink for Sink {

fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>, _flags: ElementFlags)
-> Dom<Node> {
let is = attrs.iter()
.find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
.map(|attr| LocalName::from(&*attr.value));

let elem = Element::create(name,
is,
&*self.document,
ElementCreator::ParserCreated(self.current_line),
CustomElementCreationMode::Synchronous);

for attr in attrs {
elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
}

Dom::from_ref(elem.upcast())
let attrs = attrs
.into_iter()
.map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
.collect();
let element = create_element_for_token(
name,
attrs,
&*self.document,
ElementCreator::ParserCreated(self.current_line),
self.parsing_algorithm,
);
Dom::from_ref(element.upcast())
}

fn create_comment(&mut self, text: StrTendril) -> Dom<Node> {
@@ -950,3 +977,64 @@ impl TreeSink for Sink {
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::htmlscriptelement::HTMLScriptElement;
use dom::node::Node;
use dom::servoparser::Sink;
use dom::servoparser::{ParsingAlgorithm, Sink};
use js::jsapi::JSTracer;
use servo_url::ServoUrl;
use xml5ever::buffer_queue::BufferQueue;
@@ -30,6 +30,7 @@ impl Tokenizer {
document: Dom::from_ref(document),
current_line: 1,
script: Default::default(),
parsing_algorithm: ParsingAlgorithm::Normal,
};

let tb = XmlTreeBuilder::new(sink, Default::default());

This file was deleted.

This file was deleted.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.