diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/bindings/settings_stack.rs | 6 | ||||
-rw-r--r-- | components/script/dom/bindings/str.rs | 45 | ||||
-rw-r--r-- | components/script/dom/document.rs | 33 | ||||
-rwxr-xr-x | components/script/dom/htmlinputelement.rs | 10 | ||||
-rw-r--r-- | components/script/dom/performance.rs | 8 | ||||
-rw-r--r-- | components/script/dom/servoparser/async_html.rs | 31 | ||||
-rw-r--r-- | components/script/dom/servoparser/html.rs | 6 | ||||
-rw-r--r-- | components/script/dom/servoparser/mod.rs | 130 | ||||
-rw-r--r-- | components/script/dom/servoparser/xml.rs | 3 |
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()); |