diff options
Diffstat (limited to 'components/script/dom/characterdata.rs')
-rw-r--r-- | components/script/dom/characterdata.rs | 174 |
1 files changed, 112 insertions, 62 deletions
diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index be4fc4d0ac1..f8c5acb5a8f 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -1,54 +1,59 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! DOM bindings for `CharacterData`. -use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; -use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods; -use dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId}; -use dom::bindings::codegen::UnionTypes::NodeOrString; -use dom::bindings::error::{Error, ErrorResult, Fallible}; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{LayoutJS, Root}; -use dom::bindings::str::DOMString; -use dom::comment::Comment; -use dom::document::Document; -use dom::element::Element; -use dom::node::{Node, NodeDamage}; -use dom::processinginstruction::ProcessingInstruction; -use dom::text::Text; +use crate::dom::bindings::cell::{DomRefCell, Ref}; +use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; +use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods; +use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods; +use crate::dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId, TextTypeId}; +use crate::dom::bindings::codegen::UnionTypes::NodeOrString; +use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::{DomRoot, LayoutDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::cdatasection::CDATASection; +use crate::dom::comment::Comment; +use crate::dom::document::Document; +use crate::dom::element::Element; +use crate::dom::mutationobserver::{Mutation, MutationObserver}; +use crate::dom::node::{ChildrenMutation, Node, NodeDamage}; +use crate::dom::processinginstruction::ProcessingInstruction; +use crate::dom::text::Text; +use crate::dom::virtualmethods::vtable_for; use dom_struct::dom_struct; -use servo_config::opts; -use std::cell::Ref; // https://dom.spec.whatwg.org/#characterdata #[dom_struct] pub struct CharacterData { node: Node, - data: DOMRefCell<DOMString>, + data: DomRefCell<DOMString>, } impl CharacterData { pub fn new_inherited(data: DOMString, document: &Document) -> CharacterData { CharacterData { node: Node::new_inherited(document), - data: DOMRefCell::new(data), + data: DomRefCell::new(data), } } - pub fn clone_with_data(&self, data: DOMString, document: &Document) -> Root<Node> { + pub fn clone_with_data(&self, data: DOMString, document: &Document) -> DomRoot<Node> { match self.upcast::<Node>().type_id() { NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => { - Root::upcast(Comment::new(data, &document)) - } + DomRoot::upcast(Comment::new(data, &document)) + }, NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => { let pi = self.downcast::<ProcessingInstruction>().unwrap(); - Root::upcast(ProcessingInstruction::new(pi.Target(), data, &document)) + DomRoot::upcast(ProcessingInstruction::new(pi.Target(), data, &document)) + }, + NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::CDATASection)) => { + DomRoot::upcast(CDATASection::new(data, &document)) }, - NodeTypeId::CharacterData(CharacterDataTypeId::Text) => { - Root::upcast(Text::new(data, &document)) + NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => { + DomRoot::upcast(Text::new(data, &document)) }, _ => unreachable!(), } @@ -61,6 +66,7 @@ impl CharacterData { #[inline] pub fn append_data(&self, data: &str) { + self.queue_mutation_record(); self.data.borrow_mut().push_str(data); self.content_changed(); } @@ -68,6 +74,24 @@ impl CharacterData { fn content_changed(&self) { let node = self.upcast::<Node>(); node.dirty(NodeDamage::OtherNodeDamage); + + // If this is a Text node, we might need to re-parse (say, if our parent + // is a <style> element.) We don't need to if this is a Comment or + // ProcessingInstruction. + if self.is::<Text>() { + if let Some(parent_node) = node.GetParentNode() { + let mutation = ChildrenMutation::ChangeText; + vtable_for(&parent_node).children_changed(&mutation); + } + } + } + + // Queue a MutationObserver record before changing the content. + fn queue_mutation_record(&self) { + let mutation = Mutation::CharacterData { + old_value: self.data.borrow().clone(), + }; + MutationObserver::queue_a_mutation_record(self.upcast::<Node>(), mutation); } } @@ -79,12 +103,14 @@ impl CharacterDataMethods for CharacterData { // https://dom.spec.whatwg.org/#dom-characterdata-data fn SetData(&self, data: DOMString) { + self.queue_mutation_record(); let old_length = self.Length(); let new_length = data.encode_utf16().count() as u32; *self.data.borrow_mut() = data; self.content_changed(); let node = self.upcast::<Node>(); - node.ranges().replace_code_units(node, 0, old_length, new_length); + node.ranges() + .replace_code_units(node, 0, old_length, new_length); } // https://dom.spec.whatwg.org/#dom-characterdata-length @@ -94,11 +120,16 @@ impl CharacterDataMethods for CharacterData { // https://dom.spec.whatwg.org/#dom-characterdata-substringdata fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> { + let replace_surrogates = self + .upcast::<Node>() + .owner_doc() + .window() + .replace_surrogates(); let data = self.data.borrow(); // Step 1. let mut substring = String::new(); let remaining; - match split_at_utf16_code_unit_offset(&data, offset) { + match split_at_utf16_code_unit_offset(&data, offset, replace_surrogates) { Ok((_, astral, s)) => { // As if we had split the UTF-16 surrogate pair in half // and then transcoded that to UTF-8 lossily, @@ -107,11 +138,11 @@ impl CharacterDataMethods for CharacterData { substring = substring + "\u{FFFD}"; } remaining = s; - } + }, // Step 2. Err(()) => return Err(Error::IndexSize), } - match split_at_utf16_code_unit_offset(remaining, count) { + match split_at_utf16_code_unit_offset(remaining, count, replace_surrogates) { // Steps 3. Err(()) => substring = substring + remaining, // Steps 4. @@ -123,7 +154,7 @@ impl CharacterDataMethods for CharacterData { if astral.is_some() { substring = substring + "\u{FFFD}"; } - } + }, }; Ok(DOMString::from(substring)) } @@ -148,11 +179,16 @@ impl CharacterDataMethods for CharacterData { fn ReplaceData(&self, offset: u32, count: u32, arg: DOMString) -> ErrorResult { let mut new_data; { + let replace_surrogates = self + .upcast::<Node>() + .owner_doc() + .window() + .replace_surrogates(); let data = self.data.borrow(); let prefix; let replacement_before; let remaining; - match split_at_utf16_code_unit_offset(&data, offset) { + match split_at_utf16_code_unit_offset(&data, offset, replace_surrogates) { Ok((p, astral, r)) => { prefix = p; // As if we had split the UTF-16 surrogate pair in half @@ -160,34 +196,37 @@ impl CharacterDataMethods for CharacterData { // since our DOMString is currently strict UTF-8. replacement_before = if astral.is_some() { "\u{FFFD}" } else { "" }; remaining = r; - } + }, // Step 2. Err(()) => return Err(Error::IndexSize), }; let replacement_after; let suffix; - match split_at_utf16_code_unit_offset(remaining, count) { + match split_at_utf16_code_unit_offset(remaining, count, replace_surrogates) { // Steps 3. Err(()) => { replacement_after = ""; suffix = ""; - } + }, Ok((_, astral, s)) => { // As if we had split the UTF-16 surrogate pair in half // and then transcoded that to UTF-8 lossily, // since our DOMString is currently strict UTF-8. replacement_after = if astral.is_some() { "\u{FFFD}" } else { "" }; suffix = s; - } + }, }; // Step 4: Mutation observers. + self.queue_mutation_record(); + // Step 5 to 7. new_data = String::with_capacity( prefix.len() + - replacement_before.len() + - arg.len() + - replacement_after.len() + - suffix.len()); + replacement_before.len() + + arg.len() + + replacement_after.len() + + suffix.len(), + ); new_data.push_str(prefix); new_data.push_str(replacement_before); new_data.push_str(&arg); @@ -198,8 +237,8 @@ impl CharacterDataMethods for CharacterData { self.content_changed(); // Steps 8-11. let node = self.upcast::<Node>(); - node.ranges().replace_code_units( - node, offset, count, arg.encode_utf16().count() as u32); + node.ranges() + .replace_code_units(node, offset, count, arg.encode_utf16().count() as u32); Ok(()) } @@ -225,26 +264,31 @@ impl CharacterDataMethods for CharacterData { } // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling - fn GetPreviousElementSibling(&self) -> Option<Root<Element>> { - self.upcast::<Node>().preceding_siblings().filter_map(Root::downcast).next() + fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> { + self.upcast::<Node>() + .preceding_siblings() + .filter_map(DomRoot::downcast) + .next() } // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling - fn GetNextElementSibling(&self) -> Option<Root<Element>> { - self.upcast::<Node>().following_siblings().filter_map(Root::downcast).next() + fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> { + self.upcast::<Node>() + .following_siblings() + .filter_map(DomRoot::downcast) + .next() } } -#[allow(unsafe_code)] -pub trait LayoutCharacterDataHelpers { - unsafe fn data_for_layout(&self) -> &str; +pub trait LayoutCharacterDataHelpers<'dom> { + fn data_for_layout(self) -> &'dom str; } -#[allow(unsafe_code)] -impl LayoutCharacterDataHelpers for LayoutJS<CharacterData> { +impl<'dom> LayoutCharacterDataHelpers<'dom> for LayoutDom<'dom, CharacterData> { + #[allow(unsafe_code)] #[inline] - unsafe fn data_for_layout(&self) -> &str { - &(*self.unsafe_get()).data.borrow_for_layout() + fn data_for_layout(self) -> &'dom str { + unsafe { self.unsafe_get().data.borrow_for_layout() } } } @@ -268,7 +312,11 @@ impl LayoutCharacterDataHelpers for LayoutJS<CharacterData> { /// Note that the third variant is only ever returned when the `-Z replace-surrogates` /// command-line option is specified. /// When it *would* be returned but the option is *not* specified, this function panics. -fn split_at_utf16_code_unit_offset(s: &str, offset: u32) -> Result<(&str, Option<char>, &str), ()> { +fn split_at_utf16_code_unit_offset( + s: &str, + offset: u32, + replace_surrogates: bool, +) -> Result<(&str, Option<char>, &str), ()> { let mut code_units = 0; for (i, c) in s.char_indices() { if code_units == offset { @@ -278,15 +326,17 @@ fn split_at_utf16_code_unit_offset(s: &str, offset: u32) -> Result<(&str, Option code_units += 1; if c > '\u{FFFF}' { if code_units == offset { - if opts::get().replace_surrogates { - debug_assert!(c.len_utf8() == 4); - return Ok((&s[..i], Some(c), &s[i + c.len_utf8()..])) + if replace_surrogates { + debug_assert_eq!(c.len_utf8(), 4); + return Ok((&s[..i], Some(c), &s[i + c.len_utf8()..])); } - panic!("\n\n\ - Would split a surrogate pair in CharacterData API.\n\ - If you see this in real content, please comment with the URL\n\ - on https://github.com/servo/servo/issues/6873\n\ - \n"); + panic!( + "\n\n\ + Would split a surrogate pair in CharacterData API.\n\ + If you see this in real content, please comment with the URL\n\ + on https://github.com/servo/servo/issues/6873\n\ + \n" + ); } code_units += 1; } |