aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/settings_stack.rs6
-rw-r--r--components/script/dom/bindings/str.rs45
-rw-r--r--components/script/dom/document.rs33
-rwxr-xr-xcomponents/script/dom/htmlinputelement.rs10
-rw-r--r--components/script/dom/performance.rs8
-rw-r--r--components/script/dom/servoparser/async_html.rs31
-rw-r--r--components/script/dom/servoparser/html.rs6
-rw-r--r--components/script/dom/servoparser/mod.rs130
-rw-r--r--components/script/dom/servoparser/xml.rs3
9 files changed, 206 insertions, 66 deletions
diff --git a/components/script/dom/bindings/settings_stack.rs b/components/script/dom/bindings/settings_stack.rs
index b540b989bc4..f9438f17065 100644
--- a/components/script/dom/bindings/settings_stack.rs
+++ b/components/script/dom/bindings/settings_stack.rs
@@ -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>,
diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs
index eaabc29806c..4520fd15a6c 100644
--- a/components/script/dom/bindings/str.rs
+++ b/components/script/dom/bindings/str.rs
@@ -286,31 +286,37 @@ impl DOMString {
/// YYYY must be four or more digits, MM and DD both must be two digits
/// https://html.spec.whatwg.org/multipage/#valid-date-string
pub fn is_valid_date_string(&self) -> bool {
- parse_date_string(&*self.0).is_ok()
+ parse_date_string(&self.0).is_ok()
}
/// A valid month string should be "YYYY-MM"
/// YYYY must be four or more digits, MM both must be two digits
/// https://html.spec.whatwg.org/multipage/#valid-month-string
pub fn is_valid_month_string(&self) -> bool {
- parse_month_string(&*self.0).is_ok()
+ parse_month_string(&self.0).is_ok()
}
/// A valid week string should be like {YYYY}-W{WW}, such as "2017-W52"
/// YYYY must be four or more digits, WW both must be two digits
/// https://html.spec.whatwg.org/multipage/#valid-week-string
pub fn is_valid_week_string(&self) -> bool {
- parse_week_string(&*self.0).is_ok()
+ parse_week_string(&self.0).is_ok()
}
- /// A valid number is the same as what rust considers to be valid,
- /// except for +1., NaN, and Infinity.
/// https://html.spec.whatwg.org/multipage/#valid-floating-point-number
- pub fn is_valid_number_string(&self) -> bool {
- let input = &self.0;
- input.parse::<f64>().ok().map_or(false, |val| {
- !(val.is_infinite() || val.is_nan() || input.ends_with(".") || input.starts_with("+"))
- })
+ pub fn is_valid_floating_point_number_string(&self) -> bool {
+ // for the case that `parse_floating_point_number` cannot handle
+ if self.0.contains(" ") {
+ return false;
+ }
+ parse_floating_point_number(&self.0).is_ok()
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#best-representation-of-the-number-as-a-floating-point-number
+ pub fn set_best_representation_of_the_floating_point_number(&mut self) {
+ if let Ok(val) = parse_floating_point_number(&self.0) {
+ self.0 = val.to_string();
+ }
}
/// A valid normalized local date and time string should be "{date}T{time}"
@@ -617,7 +623,7 @@ fn parse_time_component(value: &str) -> Result<(u32, u32, f32), ()> {
Ok((hour_int, minute_int, second_float))
}
-// https://html.spec.whatwg.org/multipage/#parse-a-local-date-and-time-string
+/// https://html.spec.whatwg.org/multipage/#parse-a-local-date-and-time-string
fn parse_local_date_and_time_string(value: &str) -> Result<((u32, u32, u32), (u32, u32, f32)), ()> {
// Step 1, 2, 4
let mut iterator = if value.contains('T') {
@@ -658,7 +664,7 @@ fn max_day_in_month(year_num: u32, month_num: u32) -> Result<u32, ()> {
}
}
-// https://html.spec.whatwg.org/multipage/#week-number-of-the-last-day
+/// https://html.spec.whatwg.org/multipage/#week-number-of-the-last-day
fn max_week_in_year(year: u32) -> u32 {
match Utc.ymd(year as i32, 1, 1).weekday() {
Weekday::Thu => 53,
@@ -671,3 +677,18 @@ fn max_week_in_year(year: u32) -> u32 {
fn is_leap_year(year: u32) -> bool {
year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
}
+
+/// https://html.spec.whatwg.org/multipage/#rules-for-parsing-floating-point-number-values
+fn parse_floating_point_number(input: &str) -> Result<f64, ()> {
+ match input.trim().parse::<f64>() {
+ Ok(val) if !(
+ // A valid number is the same as what rust considers to be valid,
+ // except for +1., NaN, and Infinity.
+ val.is_infinite() || val.is_nan() || input.ends_with(".") || input.starts_with("+")
+ ) => {
+ // TODO(#19773): need consider `min`, `max`, `step`, when they are implemented
+ Ok(val.round())
+ },
+ _ => Err(())
+ }
+}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 75a54e5c6b9..aecc0b0d0e7 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -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),
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 38f0287d63a..ed153529889 100755
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -1047,11 +1047,17 @@ impl HTMLInputElement {
}
InputType::Number => {
let mut textinput = self.textinput.borrow_mut();
- if !textinput.single_line_content().is_valid_number_string() {
+ if !textinput.single_line_content().is_valid_floating_point_number_string() {
textinput.single_line_content_mut().clear();
}
}
- // TODO: Implement more value sanitization algorithms for different types of inputs
+ // https://html.spec.whatwg.org/multipage/#range-state-(type=range):value-sanitization-algorithm
+ InputType::Range => {
+ self.textinput
+ .borrow_mut()
+ .single_line_content_mut()
+ .set_best_representation_of_the_floating_point_number();
+ }
_ => ()
}
}
diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs
index 3ff1d32b5d6..4a9a9672b4e 100644
--- a/components/script/dom/performance.rs
+++ b/components/script/dom/performance.rs
@@ -184,14 +184,6 @@ impl Performance {
None => return,
};
- if self.pending_notification_observers_task.get() {
- if let Some(o) = observers.iter().nth(index) {
- DOMPerformanceObserver::new(&self.global(),
- o.observer.callback(),
- o.observer.entries()).notify();
- }
- }
-
observers.remove(index);
}
diff --git a/components/script/dom/servoparser/async_html.rs b/components/script/dom/servoparser/async_html.rs
index c918a4b3605..bcf3e25b767 100644
--- a/components/script/dom/servoparser/async_html.rs
+++ b/components/script/dom/servoparser/async_html.rs
@@ -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);
diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs
index 53dcc3fc7c6..78ea868cac1 100644
--- a/components/script/dom/servoparser/html.rs
+++ b/components/script/dom/servoparser/html.rs
@@ -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 {
diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs
index 88dd2a740f5..7cadab84d04 100644
--- a/components/script/dom/servoparser/mod.rs
+++ b/components/script/dom/servoparser/mod.rs
@@ -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
+}
diff --git a/components/script/dom/servoparser/xml.rs b/components/script/dom/servoparser/xml.rs
index d34792b56c3..62ebe351a23 100644
--- a/components/script/dom/servoparser/xml.rs
+++ b/components/script/dom/servoparser/xml.rs
@@ -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());