script/dom/characterdata/
characterdata.rs1use std::cell::LazyCell;
7
8use atomic_refcell::{AtomicRef, AtomicRefCell};
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId, TextTypeId};
12use servo_base::text::{RangeAny, Utf16CodeUnits, Utf32CodeUnits};
13
14use crate::dom::bindings::cell::AtomicSafeBorrowMut;
15use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
16use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
17use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
18use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
19use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
20use crate::dom::bindings::inheritance::Castable;
21use crate::dom::bindings::root::{DomRoot, LayoutDom};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::cdatasection::CDATASection;
24use crate::dom::comment::Comment;
25use crate::dom::document::Document;
26use crate::dom::element::Element;
27use crate::dom::live_range_replace_data_steps;
28use crate::dom::mutationobserver::{Mutation, MutationObserver};
29use crate::dom::node::virtualmethods::vtable_for;
30use crate::dom::node::{ChildrenMutation, Node, NodeDamage};
31use crate::dom::processinginstruction::ProcessingInstruction;
32use crate::dom::text::Text;
33
34#[dom_struct]
36pub(crate) struct CharacterData {
37 node: Node,
38 #[no_trace]
39 data: AtomicRefCell<String>,
40}
41
42impl CharacterData {
43 pub(crate) fn new_inherited(data: DOMString, document: &Document) -> CharacterData {
44 CharacterData {
45 node: Node::new_inherited(document),
46 data: AtomicRefCell::new(String::from(data)),
47 }
48 }
49
50 pub(crate) fn clone_with_data(
51 &self,
52 cx: &mut js::context::JSContext,
53 data: DOMString,
54 document: &Document,
55 ) -> DomRoot<Node> {
56 match self.upcast::<Node>().type_id() {
57 NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => {
58 DomRoot::upcast(Comment::new(cx, data, document, None))
59 },
60 NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
61 let pi = self.downcast::<ProcessingInstruction>().unwrap();
62 DomRoot::upcast(ProcessingInstruction::new(cx, pi.Target(), data, document))
63 },
64 NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::CDATASection)) => {
65 DomRoot::upcast(CDATASection::new(cx, data, document))
66 },
67 NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
68 DomRoot::upcast(Text::new(cx, data, document))
69 },
70 _ => unreachable!(),
71 }
72 }
73
74 #[inline]
75 pub(crate) fn data(&self) -> AtomicRef<'_, String> {
76 self.data.borrow()
77 }
78
79 #[inline]
80 pub(crate) fn append_data(&self, cx: &mut JSContext, data: &str) {
81 self.queue_mutation_record(cx);
82 self.data.safe_borrow_mut(cx.no_gc()).push_str(data);
83 self.content_changed(cx);
84 }
85
86 fn content_changed(&self, cx: &mut JSContext) {
87 let node = self.upcast::<Node>();
88 node.dirty(cx.no_gc(), NodeDamage::Other);
89
90 if self.is::<Text>() &&
94 let Some(parent_node) = node.GetParentNode()
95 {
96 let mutation = ChildrenMutation::ChangeText;
97 vtable_for(&parent_node).children_changed(cx, &mutation);
98 }
99 }
100
101 fn queue_mutation_record(&self, cx: &mut JSContext) {
103 let mutation = LazyCell::new(|| Mutation::CharacterData {
104 old_value: self.data.borrow().clone(),
105 });
106 MutationObserver::queue_a_mutation_record(cx, self.upcast::<Node>(), mutation);
107 }
108
109 pub(crate) fn set_text_run_selection(
111 &self,
112 new_range: Option<RangeAny<Utf32CodeUnits>>,
113 ) -> bool {
114 self.upcast::<Node>()
115 .layout_data()
116 .borrow()
117 .as_ref()
118 .is_some_and(|layout_data| layout_data.set_text_run_selection(new_range))
119 }
120}
121
122impl CharacterDataMethods<crate::DomTypeHolder> for CharacterData {
123 fn Data(&self) -> DOMString {
125 DOMString::from(self.data.borrow().clone())
126 }
127
128 fn SetData(&self, cx: &mut JSContext, data: DOMString) {
130 self.queue_mutation_record(cx);
131 let old_length = self.Length();
132 *self.data.safe_borrow_mut(cx.no_gc()) = String::from(data.str());
133 self.content_changed(cx);
134
135 let mut utf16_length = None;
136 let mut lazy_length = move || {
137 *utf16_length.get_or_insert_with(|| Utf16CodeUnits::length_of(&data.str()).0 as u32)
138 };
139
140 let node: &Node = self.upcast();
141 if let Some(selection) = node.owner_doc_unrooted(cx.no_gc()).selection() {
142 selection.replace_data_steps(node, 0, old_length, &mut lazy_length);
143 }
144 live_range_replace_data_steps(node, 0, old_length, &mut lazy_length);
145 }
146
147 fn Length(&self) -> u32 {
149 Utf16CodeUnits::length_of(&self.data.borrow()).0 as u32
150 }
151
152 fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> {
154 let data = self.data.borrow();
155 let mut substring = String::new();
157 let remaining = match split_at_utf16_code_unit_offset(&data, offset) {
158 Ok((_, astral, s)) => {
159 if astral.is_some() {
163 substring += "\u{FFFD}";
164 }
165 s
166 },
167 Err(()) => return Err(Error::IndexSize(None)),
169 };
170 match split_at_utf16_code_unit_offset(remaining, count) {
171 Err(()) => substring += remaining,
173 Ok((s, astral, _)) => {
175 substring += s;
176 if astral.is_some() {
180 substring += "\u{FFFD}";
181 }
182 },
183 };
184 Ok(DOMString::from(substring))
185 }
186
187 fn AppendData(&self, cx: &mut JSContext, data: DOMString) {
189 self.append_data(cx, &data.str());
193 }
194
195 fn InsertData(&self, cx: &mut JSContext, offset: u32, arg: DOMString) -> ErrorResult {
197 self.ReplaceData(cx, offset, 0, arg)
199 }
200
201 fn DeleteData(&self, cx: &mut JSContext, offset: u32, count: u32) -> ErrorResult {
203 self.ReplaceData(cx, offset, count, DOMString::new())
205 }
206
207 fn ReplaceData(
209 &self,
210 cx: &mut JSContext,
211 offset: u32,
212 count: u32,
213 arg: DOMString,
214 ) -> ErrorResult {
215 let mut new_data;
216 {
217 let data = self.data.borrow();
218 let prefix;
219 let replacement_before;
220 let remaining;
221 match split_at_utf16_code_unit_offset(&data, offset) {
222 Ok((p, astral, r)) => {
223 prefix = p;
224 replacement_before = if astral.is_some() { "\u{FFFD}" } else { "" };
228 remaining = r;
229 },
230 Err(()) => return Err(Error::IndexSize(None)),
232 };
233 let replacement_after;
234 let suffix;
235 match split_at_utf16_code_unit_offset(remaining, count) {
236 Err(()) => {
238 replacement_after = "";
239 suffix = "";
240 },
241 Ok((_, astral, s)) => {
242 replacement_after = if astral.is_some() { "\u{FFFD}" } else { "" };
246 suffix = s;
247 },
248 };
249 self.queue_mutation_record(cx);
251
252 new_data = String::with_capacity(
254 prefix.len() +
255 replacement_before.len() +
256 arg.len() +
257 replacement_after.len() +
258 suffix.len(),
259 );
260 new_data.push_str(prefix);
261 new_data.push_str(replacement_before);
262 new_data.push_str(&arg.str());
263 new_data.push_str(replacement_after);
264 new_data.push_str(suffix);
265 }
266 *self.data.safe_borrow_mut(cx.no_gc()) = new_data;
267 self.content_changed(cx);
268
269 let node = self.upcast::<Node>();
270
271 let mut utf16_length = None;
272 let mut lazy_length = move || {
273 *utf16_length.get_or_insert_with(|| Utf16CodeUnits::length_of(&arg.str()).0 as u32)
274 };
275
276 if let Some(selection) = node.owner_doc_unrooted(cx.no_gc()).selection() {
277 selection.replace_data_steps(node, offset, count, &mut lazy_length);
278 }
279
280 if node.has_live_ranges() {
281 live_range_replace_data_steps(node, offset, count, &mut lazy_length);
282 }
283
284 Ok(())
293 }
294
295 fn Before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
297 self.upcast::<Node>().before(cx, nodes)
298 }
299
300 fn After(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
302 self.upcast::<Node>().after(cx, nodes)
303 }
304
305 fn ReplaceWith(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
307 self.upcast::<Node>().replace_with(cx, nodes)
308 }
309
310 fn Remove(&self, cx: &mut JSContext) {
312 self.upcast::<Node>().remove_self(cx);
313 }
314
315 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
317 self.upcast::<Node>()
318 .preceding_siblings()
319 .find_map(DomRoot::downcast)
320 }
321
322 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
324 self.upcast::<Node>()
325 .following_siblings()
326 .find_map(DomRoot::downcast)
327 }
328}
329
330impl<'dom> LayoutDom<'dom, CharacterData> {
331 #[inline]
332 pub(crate) fn data_for_layout(self) -> AtomicRef<'dom, str> {
333 AtomicRef::map(self.unsafe_get().data.borrow(), |data| &**data)
334 }
335}
336
337fn split_at_utf16_code_unit_offset(s: &str, offset: u32) -> Result<(&str, Option<char>, &str), ()> {
352 let mut code_units = 0;
353 for (i, c) in s.char_indices() {
354 if code_units == offset {
355 let (a, b) = s.split_at(i);
356 return Ok((a, None, b));
357 }
358 code_units += 1;
359 if c > '\u{FFFF}' {
360 if code_units == offset {
361 debug_assert_eq!(c.len_utf8(), 4);
362 warn!("Splitting a surrogate pair in CharacterData API.");
363 return Ok((&s[..i], Some(c), &s[i + c.len_utf8()..]));
364 }
365 code_units += 1;
366 }
367 }
368 if code_units == offset {
369 Ok((s, None, ""))
370 } else {
371 Err(())
372 }
373}