1#![allow(clippy::non_canonical_partial_ord_impl)]
6use std::borrow::{Cow, ToOwned};
7use std::cell::{Ref, RefCell, RefMut};
8use std::default::Default;
9use std::ops::Deref;
10use std::ptr::{self, NonNull};
11use std::str::FromStr;
12use std::sync::LazyLock;
13use std::{fmt, slice, str};
14
15use html5ever::{LocalName, Namespace};
16use js::context::JSContext;
17use js::conversions::{ToJSValConvertible, jsstr_to_string};
18use js::gc::{HandleValue, MutableHandleValue};
19use js::jsapi::{Heap, JS_GetLatin1StringCharsAndLength, JSString};
20use js::jsval::StringValue;
21use js::rust::{Runtime, Trace};
22use malloc_size_of::MallocSizeOfOps;
23use num_traits::{ToPrimitive, Zero};
24use regex::Regex;
25use servo_base::text::{AssumeUnder4GB, Utf8CodeUnits, Utf16CodeUnits};
26use style::Atom;
27use style::str::HTML_SPACE_CHARACTERS;
28use zeroize::Zeroize;
29
30use crate::trace::RootedTraceableBox;
31
32const ASCII_END: u8 = 0x7E;
33const ASCII_CAPITAL_A: u8 = 0x41;
34const ASCII_CAPITAL_Z: u8 = 0x5A;
35const ASCII_LOWERCASE_A: u8 = 0x61;
36const ASCII_LOWERCASE_Z: u8 = 0x7A;
37const ASCII_TAB: u8 = 0x09;
38const ASCII_NEWLINE: u8 = 0x0A;
39const ASCII_FORMFEED: u8 = 0x0C;
40const ASCII_CR: u8 = 0x0D;
41const ASCII_SPACE: u8 = 0x20;
42
43unsafe fn get_latin1_string_bytes(
46 rooted_traceable_box: &RootedTraceableBox<Heap<*mut JSString>>,
47) -> &[u8] {
48 debug_assert!(!rooted_traceable_box.get().is_null());
49 let mut length = 0;
50 unsafe {
51 let chars = JS_GetLatin1StringCharsAndLength(
52 Runtime::get().expect("JS runtime has shut down").as_ptr(),
53 ptr::null(),
54 rooted_traceable_box.get(),
55 &mut length,
56 );
57 assert!(!chars.is_null());
58 slice::from_raw_parts(chars, length)
59 }
60}
61
62#[derive(Debug)]
64pub enum EncodedBytes<'a> {
65 Latin1(Ref<'a, [u8]>),
67 Utf8(Ref<'a, [u8]>),
69}
70
71impl EncodedBytes<'_> {
72 pub fn bytes(&self) -> &[u8] {
75 match self {
76 Self::Latin1(bytes) => bytes,
77 Self::Utf8(bytes) => bytes,
78 }
79 }
80
81 pub fn is_empty(&self) -> bool {
83 self.bytes().is_empty()
84 }
85}
86
87#[derive(Zeroize)]
88enum DOMStringType {
89 Rust(String),
91 #[zeroize(skip)]
93 JSString(RootedTraceableBox<Heap<*mut JSString>>),
94 #[cfg(test)]
95 Latin1Vec(Vec<u8>),
98 #[zeroize(skip)] RustStatic(&'static str),
100}
101
102impl Default for DOMStringType {
103 fn default() -> Self {
104 Self::Rust(Default::default())
105 }
106}
107
108impl DOMStringType {
109 fn as_raw_bytes(&self) -> &[u8] {
114 match self {
115 DOMStringType::Rust(s) => s.as_bytes(),
116 DOMStringType::JSString(rooted_traceable_box) => unsafe {
117 get_latin1_string_bytes(rooted_traceable_box)
118 },
119 #[cfg(test)]
120 DOMStringType::Latin1Vec(items) => items,
121 DOMStringType::RustStatic(s) => s.as_bytes(),
122 }
123 }
124
125 fn ensure_rust_string(&mut self) -> &mut String {
126 let new_string = match self {
127 DOMStringType::Rust(string) => return string,
128 DOMStringType::JSString(rooted_traceable_box) => {
129 let cx = unsafe { JSContext::get_from_thread() };
130 let cx = cx.as_ref().expect("JS runtime has shut down");
131 unsafe { jsstr_to_string(cx, NonNull::new(rooted_traceable_box.get()).unwrap()) }
132 },
133 #[cfg(test)]
134 DOMStringType::Latin1Vec(items) => {
135 let mut v = vec![0; items.len() * 2];
136 let real_size =
137 encoding_rs::mem::convert_latin1_to_utf8(items.as_slice(), v.as_mut_slice());
138 v.truncate(real_size);
139
140 unsafe { String::from_utf8_unchecked(v) }
143 },
144 DOMStringType::RustStatic(s) => s.to_owned(),
146 };
147 *self = DOMStringType::Rust(new_string);
148 self.ensure_rust_string()
149 }
150}
151
152#[derive(Debug)]
155pub struct StringView<'a>(Ref<'a, str>);
156
157impl StringView<'_> {
158 pub fn split_html_space_characters(&self) -> impl Iterator<Item = &str> {
159 self.split(HTML_SPACE_CHARACTERS)
160 .filter(|string| !string.is_empty())
161 }
162}
163
164impl From<StringView<'_>> for String {
165 fn from(string_view: StringView<'_>) -> Self {
166 string_view.0.to_string()
167 }
168}
169
170impl Deref for StringView<'_> {
171 type Target = str;
172 fn deref(&self) -> &str {
173 &(self.0)
174 }
175}
176
177impl AsRef<str> for StringView<'_> {
178 fn as_ref(&self) -> &str {
179 &(self.0)
180 }
181}
182
183impl PartialEq for StringView<'_> {
184 fn eq(&self, other: &Self) -> bool {
185 self.0.eq(&*(other.0))
186 }
187}
188
189impl PartialEq<&str> for StringView<'_> {
190 fn eq(&self, other: &&str) -> bool {
191 self.0.eq(*other)
192 }
193}
194
195impl Eq for StringView<'_> {}
196
197impl PartialOrd for StringView<'_> {
198 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
199 self.0.partial_cmp(&**other)
200 }
201}
202
203impl Ord for StringView<'_> {
204 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
205 self.0.cmp(other)
206 }
207}
208
209unsafe impl Trace for DOMStringType {
215 unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
216 unsafe {
217 match self {
218 DOMStringType::Rust(_s) => {},
219 DOMStringType::JSString(rooted_traceable_box) => rooted_traceable_box.trace(tracer),
220 #[cfg(test)]
221 DOMStringType::Latin1Vec(_s) => {},
222 DOMStringType::RustStatic(_) => {},
223 }
224 }
225 }
226}
227
228impl malloc_size_of::MallocSizeOf for DOMStringType {
229 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
230 match self {
231 DOMStringType::Rust(s) => s.size_of(ops),
232 DOMStringType::JSString(_rooted_traceable_box) => {
233 0
235 },
236 #[cfg(test)]
237 DOMStringType::Latin1Vec(s) => s.size_of(ops),
238 DOMStringType::RustStatic(_s) => 0,
239 }
240 }
241}
242
243impl std::fmt::Debug for DOMStringType {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 match self {
246 DOMStringType::Rust(s) => f.debug_struct("DOMString").field("rust_string", s).finish(),
247 DOMStringType::JSString(_rooted_traceable_box) => f.debug_struct("DOMString").finish(),
248 #[cfg(test)]
249 DOMStringType::Latin1Vec(s) => f
250 .debug_struct("DOMString")
251 .field("latin1_string", s)
252 .finish(),
253 DOMStringType::RustStatic(s) => f
254 .debug_struct("DOMString")
255 .field("static_string", s)
256 .finish(),
257 }
258 }
259}
260
261#[repr(transparent)]
297#[derive(Debug, Default, MallocSizeOf, JSTraceable)]
298pub struct DOMString(RefCell<DOMStringType>);
299
300impl Clone for DOMString {
301 fn clone(&self) -> Self {
302 self.ensure_rust_string().clone().into()
303 }
304}
305
306pub enum DOMStringErrorType {
307 JSConversionError,
308}
309
310impl DOMString {
311 pub fn new() -> DOMString {
313 Default::default()
314 }
315
316 pub fn from_js_string(
319 cx: &mut JSContext,
320 value: HandleValue,
321 ) -> Result<DOMString, DOMStringErrorType> {
322 let string_ptr = unsafe { js::rust::ToString(cx, value) };
323 if string_ptr.is_null() {
324 debug!("ToString failed");
325 Err(DOMStringErrorType::JSConversionError)
326 } else {
327 let latin1 = unsafe { js::jsapi::JS_DeprecatedStringHasLatin1Chars(string_ptr) };
328 let inner = if latin1 {
329 let h = RootedTraceableBox::from_box(Heap::boxed(string_ptr));
330 DOMStringType::JSString(h)
331 } else {
332 DOMStringType::Rust(unsafe {
334 jsstr_to_string(cx, NonNull::new(string_ptr).unwrap())
335 })
336 };
337 Ok(DOMString(RefCell::new(inner)))
338 }
339 }
340
341 pub fn from_static(s: &'static str) -> DOMString {
343 DOMString(RefCell::new(DOMStringType::RustStatic(s)))
344 }
345
346 fn ensure_rust_string(&self) -> RefMut<'_, String> {
349 let inner = self.0.borrow_mut();
350 RefMut::map(inner, |inner| inner.ensure_rust_string())
351 }
352
353 #[expect(unused)]
355 fn debug_js(&self, cx: &JSContext) {
356 match *self.0.borrow() {
357 DOMStringType::Rust(ref s) => info!("Rust String ({})", s),
358 DOMStringType::JSString(ref rooted_traceable_box) => {
359 let s = unsafe {
360 jsstr_to_string(cx, NonNull::new(rooted_traceable_box.get()).unwrap())
361 };
362 info!("JSString ({})", s);
363 },
364 #[cfg(test)]
365 DOMStringType::Latin1Vec(ref items) => info!("Latin1 string"),
366 DOMStringType::RustStatic(s) => info!("Static Rust String ({})", s),
367 }
368 }
369
370 pub fn str(&self) -> StringView<'_> {
372 {
373 let inner = self.0.borrow();
374 if matches!(&*inner, DOMStringType::Rust(..)) {
375 return StringView(Ref::map(inner, |inner| match inner {
376 DOMStringType::Rust(string) => string.as_str(),
377 _ => unreachable!("Guaranteed by condition above"),
378 }));
379 }
380 }
381
382 self.ensure_rust_string();
383 self.str()
384 }
385
386 pub fn encoded_bytes(&self) -> EncodedBytes<'_> {
389 let inner = self.0.borrow();
390 match &*inner {
391 DOMStringType::Rust(..) | DOMStringType::RustStatic(..) => {
392 EncodedBytes::Utf8(Ref::map(inner, |inner| inner.as_raw_bytes()))
393 },
394 DOMStringType::JSString(..) => {
395 EncodedBytes::Latin1(Ref::map(inner, |inner| inner.as_raw_bytes()))
396 },
397 #[cfg(test)]
398 DOMStringType::Latin1Vec(..) => {
399 EncodedBytes::Latin1(Ref::map(inner, |inner| inner.as_raw_bytes()))
400 },
401 }
402 }
403
404 pub fn clear(&mut self) {
405 let mut inner = self.0.borrow_mut();
406 let DOMStringType::Rust(string) = &mut *inner else {
407 *inner = DOMStringType::Rust(String::new());
408 return;
409 };
410 string.clear();
411 }
412
413 pub fn is_empty(&self) -> bool {
414 self.encoded_bytes().is_empty()
415 }
416
417 pub fn len_utf8(&self) -> Utf8CodeUnits {
424 Utf8CodeUnits(match self.encoded_bytes() {
426 EncodedBytes::Utf8(bytes) => bytes.len() as u32,
427 EncodedBytes::Latin1(bytes) => bytes
428 .iter()
429 .map(|&byte| if byte < 128 { 1 } else { 2 })
432 .sum::<u32>(),
433 })
434 }
435
436 pub fn len_utf8_or_latin1(&self) -> usize {
438 match self.encoded_bytes() {
439 EncodedBytes::Utf8(bytes) => bytes.len(),
440 EncodedBytes::Latin1(bytes) => bytes.len(),
441 }
442 }
443
444 pub fn len_utf16(&self) -> Utf16CodeUnits {
449 match self.encoded_bytes() {
450 EncodedBytes::Latin1(bytes) => Utf16CodeUnits(bytes.len() as u32),
452 EncodedBytes::Utf8(bytes) => Utf16CodeUnits::length_of(AssumeUnder4GB, unsafe {
455 str::from_utf8_unchecked(&bytes)
456 }),
457 }
458 }
459
460 pub fn make_ascii_lowercase(&mut self) {
463 self.0
464 .borrow_mut()
465 .ensure_rust_string()
466 .make_ascii_lowercase();
467 }
468
469 pub fn push_str(&mut self, string_to_push: &str) {
470 self.0
471 .borrow_mut()
472 .ensure_rust_string()
473 .push_str(string_to_push);
474 }
475
476 pub fn strip_leading_and_trailing_ascii_whitespace(&mut self) {
478 if self.is_empty() {
479 return;
480 }
481
482 let mut inner = self.0.borrow_mut();
483 let string = inner.ensure_rust_string();
484 let trailing_whitespace_len = string
485 .trim_end_matches(|character: char| character.is_ascii_whitespace())
486 .len();
487 string.truncate(trailing_whitespace_len);
488 if string.is_empty() {
489 return;
490 }
491
492 let first_non_whitespace = string
493 .find(|character: char| !character.is_ascii_whitespace())
494 .unwrap();
495 string.replace_range(0..first_non_whitespace, "");
496 }
497
498 pub fn is_valid_floating_point_number_string(&self) -> bool {
500 static RE: LazyLock<Regex> = LazyLock::new(|| {
501 Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap()
502 });
503
504 RE.is_match(self.0.borrow_mut().ensure_rust_string()) &&
505 self.parse_floating_point_number().is_some()
506 }
507
508 pub fn parse<T: FromStr>(&self) -> Result<T, <T as FromStr>::Err> {
509 self.str().parse::<T>()
510 }
511
512 pub fn parse_floating_point_number(&self) -> Option<f64> {
514 parse_floating_point_number(&self.str())
515 }
516
517 pub fn set_best_representation_of_the_floating_point_number(&mut self) {
519 if let Some(val) = self.parse_floating_point_number() {
520 let parsed_value = if val.is_zero() { 0.0_f64 } else { val };
522
523 *self.0.borrow_mut() = DOMStringType::Rust(parsed_value.to_string());
524 }
525 }
526
527 pub fn to_lowercase(&self) -> String {
528 self.str().to_lowercase()
529 }
530
531 pub fn to_uppercase(&self) -> String {
532 self.str().to_uppercase()
533 }
534
535 pub fn strip_newlines(&mut self) {
536 self.0
539 .borrow_mut()
540 .ensure_rust_string()
541 .retain(|character| character != '\r' && character != '\n');
542 }
543
544 pub fn normalize_newlines(&mut self) {
546 let mut inner = self.0.borrow_mut();
550 let string = inner.ensure_rust_string();
551 *string = string.replace("\r\n", "\n").replace("\r", "\n")
552 }
553
554 pub fn replace(self, needle: &str, replace_char: &str) -> DOMString {
555 let new_string = self.str().to_owned();
556 DOMString(RefCell::new(DOMStringType::Rust(
557 new_string.replace(needle, replace_char),
558 )))
559 }
560
561 pub fn starts_with(&self, c: char) -> bool {
563 if !c.is_ascii() {
564 self.str().starts_with(c)
565 } else {
566 self.encoded_bytes().bytes().starts_with(&[c as u8])
569 }
570 }
571
572 pub fn starts_with_str(&self, needle: &str) -> bool {
573 self.str().starts_with(needle)
574 }
575
576 pub fn ends_with_str(&self, needle: &str) -> bool {
577 self.str().ends_with(needle)
578 }
579
580 pub fn contains(&self, needle: &str) -> bool {
581 self.str().contains(needle)
582 }
583
584 pub fn eq_ignore_ascii_case(&self, other: &str) -> bool {
589 if other.is_ascii() {
590 self.encoded_bytes()
591 .bytes()
592 .eq_ignore_ascii_case(other.as_bytes())
593 } else {
594 self.str().eq_ignore_ascii_case(other)
595 }
596 }
597
598 pub fn to_ascii_lowercase(&self) -> String {
599 let conversion = match self.encoded_bytes() {
600 EncodedBytes::Latin1(bytes) => {
601 if bytes.iter().all(|c| *c <= ASCII_END) {
602 Some(unsafe {
604 String::from_utf8_unchecked(
605 bytes
606 .iter()
607 .map(|c| {
608 if *c >= ASCII_CAPITAL_A && *c <= ASCII_CAPITAL_Z {
609 c + 32
610 } else {
611 *c
612 }
613 })
614 .collect(),
615 )
616 })
617 } else {
618 None
619 }
620 },
621 EncodedBytes::Utf8(bytes) => unsafe {
622 Some(str::from_utf8_unchecked(&bytes).to_ascii_lowercase())
624 },
625 };
626 if let Some(conversion) = conversion {
628 conversion
629 } else {
630 self.str().to_ascii_lowercase()
631 }
632 }
633
634 fn contains_space_characters(
635 &self,
636 latin1_characters: &'static [u8],
637 utf8_characters: &'static [char],
638 ) -> bool {
639 match self.encoded_bytes() {
640 EncodedBytes::Latin1(items) => {
641 latin1_characters.iter().any(|byte| items.contains(byte))
642 },
643 EncodedBytes::Utf8(bytes) => {
644 let s = unsafe { str::from_utf8_unchecked(&bytes) };
646 s.contains(utf8_characters)
647 },
648 }
649 }
650
651 pub fn contains_tab_or_newline(&self) -> bool {
653 const LATIN_TAB_OR_NEWLINE: [u8; 3] = [ASCII_TAB, ASCII_NEWLINE, ASCII_CR];
654 const UTF8_TAB_OR_NEWLINE: [char; 3] = ['\u{0009}', '\u{000a}', '\u{000d}'];
655
656 self.contains_space_characters(&LATIN_TAB_OR_NEWLINE, &UTF8_TAB_OR_NEWLINE)
657 }
658
659 pub fn contains_html_space_characters(&self) -> bool {
661 const SPACE_BYTES: [u8; 5] = [
662 ASCII_TAB,
663 ASCII_NEWLINE,
664 ASCII_FORMFEED,
665 ASCII_CR,
666 ASCII_SPACE,
667 ];
668 self.contains_space_characters(&SPACE_BYTES, HTML_SPACE_CHARACTERS)
669 }
670
671 pub fn as_bytes(&self) -> BytesView<'_> {
673 if self.is_ascii() {
677 BytesView(self.0.borrow())
678 } else {
679 self.ensure_rust_string();
680 BytesView(self.0.borrow())
681 }
682 }
683
684 pub fn is_ascii_lowercase(&self) -> bool {
686 match self.encoded_bytes() {
687 EncodedBytes::Latin1(items) => items
688 .iter()
689 .all(|c| (ASCII_LOWERCASE_A..=ASCII_LOWERCASE_Z).contains(c)),
690 EncodedBytes::Utf8(s) => s
691 .iter()
692 .map(|c| c.to_u8().unwrap_or(ASCII_LOWERCASE_A - 1))
693 .all(|c| (ASCII_LOWERCASE_A..=ASCII_LOWERCASE_Z).contains(&c)),
694 }
695 }
696
697 pub fn is_ascii(&self) -> bool {
699 self.encoded_bytes().bytes().is_ascii()
700 }
701
702 pub fn is_valid_for_cookie(&self) -> bool {
706 match self.encoded_bytes() {
707 EncodedBytes::Latin1(items) | EncodedBytes::Utf8(items) => !items
708 .iter()
709 .any(|c| *c == 0x7f || (*c <= 0x1f && *c != 0x09)),
710 }
711 }
712
713 fn with_str_reference<Result>(&self, callback: fn(&str) -> Result) -> Result {
716 match self.encoded_bytes() {
717 EncodedBytes::Latin1(latin1_bytes) => {
719 if latin1_bytes.iter().all(|character| character.is_ascii()) {
720 return callback(unsafe { str::from_utf8_unchecked(&latin1_bytes) });
723 }
724 },
725 EncodedBytes::Utf8(utf8_bytes) => {
726 return callback(unsafe { str::from_utf8_unchecked(&utf8_bytes) });
729 },
730 };
731 callback(self.str().deref())
732 }
733
734 pub fn normalize_crlf(&self) -> String {
742 let s = self.str();
743 let mut buf = String::new();
744 let mut prev = ' ';
745 for ch in s.chars() {
746 match ch {
747 '\n' if prev != '\r' => {
748 buf.push('\r');
749 buf.push('\n');
750 },
751 '\n' => {
752 buf.push('\n');
753 },
754 _ if prev == '\r' => {
757 buf.push('\n');
758 buf.push(ch);
759 },
760 _ => buf.push(ch),
761 };
762 prev = ch;
763 }
764 if prev == '\r' {
766 buf.push('\n');
767 }
768 buf
769 }
770}
771
772pub fn parse_floating_point_number(input: &str) -> Option<f64> {
774 input.trim().parse::<f64>().ok().filter(|value| {
780 !(value.is_infinite() || value.is_nan() || input.ends_with('.') || input.starts_with('+'))
783 })
784}
785
786pub struct BytesView<'a>(Ref<'a, DOMStringType>);
787
788impl Deref for BytesView<'_> {
789 type Target = [u8];
790
791 fn deref(&self) -> &Self::Target {
792 self.0.as_raw_bytes()
794 }
795}
796
797impl Ord for DOMString {
798 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
799 self.str().cmp(&other.str())
800 }
801}
802
803impl PartialOrd for DOMString {
804 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
805 self.str().partial_cmp(&other.str())
806 }
807}
808
809impl Extend<char> for DOMString {
810 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
811 self.0.borrow_mut().ensure_rust_string().extend(iter)
812 }
813}
814
815impl ToJSValConvertible for DOMString {
816 fn to_jsval(&self, cx: &mut JSContext, mut rval: MutableHandleValue) {
817 let val = self.0.borrow();
818 match *val {
819 DOMStringType::Rust(ref s) => s.to_jsval(cx, rval),
820 DOMStringType::JSString(ref rooted_traceable_box) => unsafe {
821 rval.set(StringValue(&*rooted_traceable_box.get()));
822 },
823 #[cfg(test)]
824 DOMStringType::Latin1Vec(ref items) => {
825 let mut v = vec![0; items.len() * 2];
826 let real_size =
827 encoding_rs::mem::convert_latin1_to_utf8(items.as_slice(), v.as_mut_slice());
828 v.truncate(real_size);
829
830 String::from_utf8(v)
831 .expect("Error in constructin test string")
832 .to_jsval(cx, rval);
833 },
834 DOMStringType::RustStatic(s) => s.to_jsval(cx, rval),
835 };
836 }
837}
838
839impl std::hash::Hash for DOMString {
840 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
841 self.str().hash(state);
842 }
843}
844
845impl std::fmt::Display for DOMString {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 fmt::Display::fmt(self.str().deref(), f)
848 }
849}
850
851impl std::cmp::PartialEq<str> for DOMString {
852 fn eq(&self, other: &str) -> bool {
853 if other.is_ascii() {
854 *other.as_bytes() == *self.encoded_bytes().bytes()
855 } else {
856 self.str().deref() == other
857 }
858 }
859}
860
861impl std::cmp::PartialEq<&str> for DOMString {
862 fn eq(&self, other: &&str) -> bool {
863 self.eq(*other)
864 }
865}
866
867impl std::cmp::PartialEq<String> for DOMString {
868 fn eq(&self, other: &String) -> bool {
869 self.eq(other.as_str())
870 }
871}
872
873impl std::cmp::PartialEq<DOMString> for String {
874 fn eq(&self, other: &DOMString) -> bool {
875 other.eq(self)
876 }
877}
878
879impl std::cmp::PartialEq<DOMString> for str {
880 fn eq(&self, other: &DOMString) -> bool {
881 other.eq(self)
882 }
883}
884
885impl std::cmp::PartialEq for DOMString {
886 fn eq(&self, other: &DOMString) -> bool {
887 let result = match (self.encoded_bytes(), other.encoded_bytes()) {
888 (EncodedBytes::Latin1(bytes), EncodedBytes::Latin1(other_bytes)) => {
889 Some(*bytes == *other_bytes)
890 },
891 (EncodedBytes::Latin1(bytes), EncodedBytes::Utf8(other_bytes))
892 if other_bytes.is_ascii() =>
893 {
894 Some(*bytes == *other_bytes)
895 },
896 (EncodedBytes::Utf8(bytes), EncodedBytes::Latin1(other_bytes)) if bytes.is_ascii() => {
897 Some(*bytes == *other_bytes)
898 },
899 (EncodedBytes::Utf8(bytes), EncodedBytes::Utf8(other_bytes)) => {
900 Some(*bytes == *other_bytes)
901 },
902 _ => None,
903 };
904
905 if let Some(eq_result) = result {
906 return eq_result;
907 }
908
909 *self.str() == *other.str()
910 }
911}
912
913impl std::cmp::Eq for DOMString {}
914
915impl From<std::string::String> for DOMString {
916 fn from(string: String) -> Self {
917 DOMString(RefCell::new(DOMStringType::Rust(string)))
918 }
919}
920
921impl From<&str> for DOMString {
923 fn from(string: &str) -> Self {
924 String::from(string).into()
925 }
926}
927
928impl From<DOMString> for LocalName {
929 fn from(dom_string: DOMString) -> LocalName {
930 dom_string.with_str_reference(|string| LocalName::from(string))
931 }
932}
933
934impl From<&DOMString> for LocalName {
935 fn from(dom_string: &DOMString) -> LocalName {
936 dom_string.with_str_reference(|string| LocalName::from(string))
937 }
938}
939
940impl From<DOMString> for Namespace {
941 fn from(dom_string: DOMString) -> Namespace {
942 dom_string.with_str_reference(|string| Namespace::from(string))
943 }
944}
945
946impl From<DOMString> for Atom {
947 fn from(dom_string: DOMString) -> Atom {
948 dom_string.with_str_reference(|string| Atom::from(string))
949 }
950}
951
952impl From<DOMString> for String {
953 fn from(val: DOMString) -> Self {
954 val.ensure_rust_string();
955 let inner = val.0.take();
956 match inner {
957 DOMStringType::Rust(s) => s,
958 DOMStringType::JSString(_) => unreachable!(),
959 #[cfg(test)]
960 DOMStringType::Latin1Vec(items) => String::from_utf8(items).expect("Not valid latin1"),
961 DOMStringType::RustStatic(s) => s.to_owned(),
962 }
963 }
964}
965
966impl From<DOMString> for Vec<u8> {
967 fn from(value: DOMString) -> Self {
968 value.ensure_rust_string();
969 let inner = value.0.take();
970 match inner {
971 DOMStringType::Rust(s) => s.into_bytes(),
972 DOMStringType::JSString(_) => unreachable!(),
973 #[cfg(test)]
974 DOMStringType::Latin1Vec(items) => items,
975 DOMStringType::RustStatic(_) => unreachable!(),
976 }
977 }
978}
979
980impl From<Cow<'_, str>> for DOMString {
981 fn from(value: Cow<'_, str>) -> Self {
982 DOMString(RefCell::new(DOMStringType::Rust(value.into_owned())))
983 }
984}
985
986impl Zeroize for DOMString {
987 fn zeroize(&mut self) {
988 self.0.get_mut().zeroize();
989 }
990}
991
992#[macro_export]
993macro_rules! match_domstring_ascii_inner {
994 ($variant: expr, $input: expr, $ascii_literal: literal => $then: expr, $($rest:tt)*) => {
995 if {
996 debug_assert!(($ascii_literal).is_ascii());
997 $ascii_literal.as_bytes()
998 } == $input.bytes() {
999 $then
1000 } else {
1001 $crate::match_domstring_ascii_inner!($variant, $input, $($rest)*)
1002 }
1003
1004 };
1005 ($variant: expr, $input: expr, $p: pat => $then: expr,) => {
1006 match $input {
1007 $p => $then
1008 }
1009 }
1010}
1011
1012#[macro_export]
1029macro_rules! match_domstring_ascii {
1030 ($input:expr, $($tail:tt)*) => {
1031 {
1032 use $crate::domstring::EncodedBytes;
1033
1034 let encoded_bytes = $input.encoded_bytes();
1035 match encoded_bytes {
1036 EncodedBytes::Latin1(_) => {
1037 $crate::match_domstring_ascii_inner!(EncodedBytes::Latin1, encoded_bytes, $($tail)*)
1038 }
1039 EncodedBytes::Utf8(_) => {
1040 $crate::match_domstring_ascii_inner!(EncodedBytes::Utf8, encoded_bytes, $($tail)*)
1041 }
1042
1043 }
1044 }
1045 };
1046}
1047
1048#[cfg(test)]
1049mod tests {
1050 use super::*;
1051
1052 const LATIN1_PILLCROW: u8 = 0xB6;
1053 const UTF8_PILLCROW: [u8; 2] = [194, 182];
1054 const LATIN1_POWER2: u8 = 0xB2;
1055
1056 fn from_latin1(l1vec: Vec<u8>) -> DOMString {
1057 DOMString(RefCell::new(DOMStringType::Latin1Vec(l1vec)))
1058 }
1059
1060 #[test]
1061 fn string_functions() {
1062 let s = DOMString::from("AbBcC❤&%$#");
1063 let s_copy = s.clone();
1064 assert_eq!(s.to_ascii_lowercase(), "abbcc❤&%$#");
1065 assert_eq!(s, s_copy);
1066 assert_eq!(s.len_utf8().0, 12);
1067 assert_eq!(s_copy.len_utf8().0, 12);
1068 assert!(s.starts_with('A'));
1069 let s2 = DOMString::from("");
1070 assert!(s2.is_empty());
1071 }
1072
1073 #[test]
1074 fn string_functions_latin1() {
1075 {
1076 let s = from_latin1(vec![
1077 b'A', b'b', b'B', b'c', b'C', b'&', b'%', b'$', b'#', 0xB2,
1078 ]);
1079 assert_eq!(s.to_ascii_lowercase(), "abbcc&%$#²");
1080 }
1081 {
1082 let s = from_latin1(vec![b'A', b'b', b'B', b'c', b'C']);
1083 assert_eq!(s.to_ascii_lowercase(), "abbcc");
1084 }
1085 {
1086 let s = from_latin1(vec![
1087 b'A', b'b', b'B', b'c', b'C', b'&', b'%', b'$', b'#', 0xB2,
1088 ]);
1089 assert_eq!(s.len_utf8().0, 11);
1090 assert!(s.starts_with('A'));
1091 }
1092 {
1093 let s = from_latin1(vec![]);
1094 assert!(s.is_empty());
1095 }
1096 }
1097
1098 #[test]
1099 fn test_length() {
1100 let s1 = from_latin1(vec![
1101 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
1102 0xAE, 0xAF,
1103 ]);
1104 let s2 = from_latin1(vec![
1105 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
1106 0xBE, 0xBF,
1107 ]);
1108 let s3 = from_latin1(vec![
1109 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
1110 0xCE, 0xCF,
1111 ]);
1112 let s4 = from_latin1(vec![
1113 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
1114 0xDE, 0xDF,
1115 ]);
1116 let s5 = from_latin1(vec![
1117 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
1118 0xEE, 0xEF,
1119 ]);
1120 let s6 = from_latin1(vec![
1121 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
1122 0xFE, 0xFF,
1123 ]);
1124
1125 let s1_utf8 = String::from("\u{00A0}¡¢£¤¥¦§¨©ª«¬\u{00AD}®¯");
1126 let s2_utf8 = String::from("°±²³´µ¶·¸¹º»¼½¾¿");
1127 let s3_utf8 = String::from("ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ");
1128 let s4_utf8 = String::from("ÐÑÒÓÔÕÖרÙÚÛÜÝÞß");
1129 let s5_utf8 = String::from("àáâãäåæçèéêëìíîï");
1130 let s6_utf8 = String::from("ðñòóôõö÷øùúûüýþÿ");
1131
1132 assert_eq!(usize::from(s1.len_utf8()), s1_utf8.len());
1133 assert_eq!(usize::from(s2.len_utf8()), s2_utf8.len());
1134 assert_eq!(usize::from(s3.len_utf8()), s3_utf8.len());
1135 assert_eq!(usize::from(s4.len_utf8()), s4_utf8.len());
1136 assert_eq!(usize::from(s5.len_utf8()), s5_utf8.len());
1137 assert_eq!(usize::from(s6.len_utf8()), s6_utf8.len());
1138
1139 s1.ensure_rust_string();
1140 s2.ensure_rust_string();
1141 s3.ensure_rust_string();
1142 s4.ensure_rust_string();
1143 s5.ensure_rust_string();
1144 s6.ensure_rust_string();
1145 assert_eq!(usize::from(s1.len_utf8()), s1_utf8.len());
1146 assert_eq!(usize::from(s2.len_utf8()), s2_utf8.len());
1147 assert_eq!(usize::from(s3.len_utf8()), s3_utf8.len());
1148 assert_eq!(usize::from(s4.len_utf8()), s4_utf8.len());
1149 assert_eq!(usize::from(s5.len_utf8()), s5_utf8.len());
1150 assert_eq!(usize::from(s6.len_utf8()), s6_utf8.len());
1151 }
1152
1153 #[test]
1154 fn test_convert() {
1155 let s = from_latin1(vec![b'a', b'b', b'c', b'%', b'$']);
1156 s.ensure_rust_string();
1157 assert_eq!(&*s.str(), "abc%$");
1158 }
1159
1160 #[test]
1161 fn partial_eq() {
1162 let s = from_latin1(vec![b'a', b'b', b'c', b'%', b'$']);
1163 let string = String::from("abc%$");
1164 let s2 = DOMString::from(string.clone());
1165 let s3 = DOMString::from_static("abc%$");
1166 assert_eq!(s, s2);
1167 assert_eq!(s, string);
1168 assert_eq!(s, s3);
1169 }
1170
1171 #[test]
1172 fn encoded_latin1_bytes() {
1173 let original_latin1_bytes = vec![b'a', b'b', b'c', b'%', b'$', 0xB2];
1174 let dom_string = from_latin1(original_latin1_bytes.clone());
1175 let string_latin1_bytes = match dom_string.encoded_bytes() {
1176 EncodedBytes::Latin1(bytes) => bytes,
1177 _ => unreachable!("Expected Latin1 encoded bytes"),
1178 };
1179 assert_eq!(*original_latin1_bytes, *string_latin1_bytes);
1180 }
1181
1182 #[test]
1183 fn testing_stringview() {
1184 let s = from_latin1(vec![b'a', b'b', b'c', b'%', b'$', 0xB2]);
1185
1186 assert_eq!(
1187 s.str().chars().collect::<Vec<char>>(),
1188 vec!['a', 'b', 'c', '%', '$', '²']
1189 );
1190 assert_eq!(s.str().as_bytes(), String::from("abc%$²").as_bytes());
1191 }
1192
1193 #[test]
1198 fn test_hash() {
1199 use std::hash::{DefaultHasher, Hash, Hasher};
1200 fn hash_value(d: &DOMString) -> u64 {
1201 let mut hasher = DefaultHasher::new();
1202 d.hash(&mut hasher);
1203 hasher.finish()
1204 }
1205
1206 let s = from_latin1(vec![b'a', b'b', b'c', b'%', b'$', 0xB2]);
1207 let s_converted = from_latin1(vec![b'a', b'b', b'c', b'%', b'$', 0xB2]);
1208 s_converted.ensure_rust_string();
1209 let s2 = DOMString::from("abc%$²");
1210 let s3 = DOMString::from_static("abc%$²");
1211
1212 let hash_s = hash_value(&s);
1213 let hash_s_converted = hash_value(&s_converted);
1214 let hash_s2 = hash_value(&s2);
1215 let hash_s3 = hash_value(&s3);
1216
1217 assert_eq!(hash_s, hash_s2);
1218 assert_eq!(hash_s, hash_s_converted);
1219 assert_eq!(hash_s, hash_s3);
1220 }
1221
1222 #[test]
1224 fn test_match_executing() {
1225 {
1227 let s = from_latin1(vec![b'a', b'b', b'c']);
1228 match_domstring_ascii!( s,
1229 "abc" => assert!(true),
1230 "bcd" => assert!(false),
1231 _ => (),
1232 );
1233 }
1234
1235 {
1236 let s = from_latin1(vec![b'a', b'b', b'c', b'/']);
1237 match_domstring_ascii!( s,
1238 "abc/" => assert!(true),
1239 "bcd" => assert!(false),
1240 _ => (),
1241 );
1242 }
1243
1244 {
1245 let s = from_latin1(vec![b'a', b'b', b'c', b'%', b'$']);
1246 match_domstring_ascii!( s,
1247 "bcd" => assert!(false),
1248 "abc%$" => assert!(true),
1249 _ => (),
1250 );
1251 }
1252
1253 {
1254 let s = DOMString::from("abcde");
1255 match_domstring_ascii!( s,
1256 "abc" => assert!(false),
1257 "bcd" => assert!(false),
1258 _ => assert!(true),
1259 );
1260 }
1261 {
1262 let s = DOMString::from("abc%$");
1263 match_domstring_ascii!( s,
1264 "bcd" => assert!(false),
1265 "abc%$" => assert!(true),
1266 _ => (),
1267 );
1268 }
1269 {
1270 let s = from_latin1(vec![b'a', b'b', b'c']);
1271 match_domstring_ascii!( s,
1272 "abcdd" => assert!(false),
1273 "bcd" => assert!(false),
1274 _ => (),
1275 );
1276 }
1277 {
1278 let s = DOMString::from_static("abc");
1279 match_domstring_ascii!( s,
1280 "abc" => assert!(true),
1281 "bcd" => assert!(false),
1282 _ => (),
1283 );
1284 }
1285 }
1286
1287 #[test]
1289 fn test_match_returning_result() {
1290 {
1291 let s = from_latin1(vec![b'a', b'b', b'c']);
1292 let res = match_domstring_ascii!( s,
1293 "abc" => true,
1294 "bcd" => false,
1295 _ => false,
1296 );
1297 assert_eq!(res, true);
1298 }
1299 {
1300 let s = from_latin1(vec![b'a', b'b', b'c', b'/']);
1301 let res = match_domstring_ascii!( s,
1302 "abc/" => true,
1303 "bcd" => false,
1304 _ => false,
1305 );
1306 assert_eq!(res, true);
1307 }
1308 {
1309 let s = from_latin1(vec![b'a', b'b', b'c', b'%', b'$']);
1310 let res = match_domstring_ascii!( s,
1311 "bcd" => false,
1312 "abc%$" => true,
1313 _ => false,
1314 );
1315 assert_eq!(res, true);
1316 }
1317
1318 {
1319 let s = DOMString::from("abcde");
1320 let res = match_domstring_ascii!( s,
1321 "abc" => false,
1322 "bcd" => false,
1323 _ => true,
1324 );
1325 assert_eq!(res, true);
1326 }
1327 {
1328 let s = DOMString::from("abc%$");
1329 let res = match_domstring_ascii!( s,
1330 "bcd" => false,
1331 "abc%$" => true,
1332 _ => false,
1333 );
1334 assert_eq!(res, true);
1335 }
1336 {
1337 let s = from_latin1(vec![b'a', b'b', b'c']);
1338 let res = match_domstring_ascii!( s,
1339 "abcdd" => false,
1340 "bcd" => false,
1341 _ => true,
1342 );
1343 assert_eq!(res, true);
1344 }
1345 }
1346
1347 #[test]
1348 #[cfg(debug_assertions)]
1349 #[should_panic]
1350 fn test_match_panic() {
1351 let s = DOMString::from("abcd");
1352 let _res = match_domstring_ascii!(s,
1353 "❤" => true,
1354 _ => false,);
1355 }
1356
1357 #[test]
1358 #[cfg(debug_assertions)]
1359 #[should_panic]
1360 fn test_match_panic2() {
1361 let s = DOMString::from("abcd");
1362 let _res = match_domstring_ascii!(s,
1363 "abc" => false,
1364 "❤" => true,
1365 _ => false,
1366 );
1367 }
1368
1369 #[test]
1370 fn test_strip_whitespace() {
1371 {
1372 let mut s = from_latin1(vec![
1373 b' ', b' ', b' ', b'\n', b' ', b'a', b'b', b'c', b'%', b'$', 0xB2, b' ',
1374 ]);
1375
1376 s.strip_leading_and_trailing_ascii_whitespace();
1377 s.ensure_rust_string();
1378 assert_eq!(&*s.str(), "abc%$²");
1379 }
1380 {
1381 let mut s = DOMString::from(" \n abc%$ ");
1382
1383 s.strip_leading_and_trailing_ascii_whitespace();
1384 s.ensure_rust_string();
1385 assert_eq!(&*s.str(), "abc%$");
1386 }
1387 {
1388 let mut s = DOMString::from_static(" \n abc%$ ");
1389
1390 s.strip_leading_and_trailing_ascii_whitespace();
1391 s.ensure_rust_string();
1392 assert_eq!(&*s.str(), "abc%$");
1393 }
1394 }
1395
1396 #[test]
1398 fn contains_html_space_characters() {
1399 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_TAB, b'a', b'a']); assert!(s.contains_html_space_characters());
1401 s.ensure_rust_string();
1402 assert!(s.contains_html_space_characters());
1403
1404 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_NEWLINE, b'a', b'a']); assert!(s.contains_html_space_characters());
1406 s.ensure_rust_string();
1407 assert!(s.contains_html_space_characters());
1408
1409 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_FORMFEED, b'a', b'a']); assert!(s.contains_html_space_characters());
1411 s.ensure_rust_string();
1412 assert!(s.contains_html_space_characters());
1413
1414 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_CR, b'a', b'a']); assert!(s.contains_html_space_characters());
1416 s.ensure_rust_string();
1417 assert!(s.contains_html_space_characters());
1418
1419 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_SPACE, b'a', b'a']); assert!(s.contains_html_space_characters());
1421 s.ensure_rust_string();
1422 assert!(s.contains_html_space_characters());
1423
1424 let s = from_latin1(vec![b'a', b'a', b'a', b'a', b'a']);
1425 assert!(!s.contains_html_space_characters());
1426 s.ensure_rust_string();
1427 assert!(!s.contains_html_space_characters());
1428
1429 let s = DOMString::from_static("aba aaa");
1430 assert!(s.contains_html_space_characters());
1431 s.ensure_rust_string();
1432 assert!(s.contains_html_space_characters());
1433 }
1434
1435 #[test]
1436 fn atom() {
1437 let s = from_latin1(vec![b'a', b'a', b'a', 0x20, b'a', b'a']);
1438 let atom1 = Atom::from(s);
1439 let s2 = DOMString::from("aaa aa");
1440 let atom2 = Atom::from(s2);
1441 assert_eq!(atom1, atom2);
1442 let s3 = from_latin1(vec![b'a', b'a', b'a', 0xB2, b'a', b'a']);
1443 let atom3 = Atom::from(s3);
1444 assert_ne!(atom1, atom3);
1445 let s3 = DOMString::from_static("aaa\u{03B1}aa");
1446 let atom3 = Atom::from(s3);
1447 assert_ne!(atom1, atom3);
1448 let s4 = DOMString::from_static("aaa aa");
1449 let atom4 = Atom::from(s4);
1450 assert_eq!(atom2, atom4);
1451 }
1452
1453 #[test]
1454 fn namespace() {
1455 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_SPACE, b'a', b'a']);
1456 let atom1 = Namespace::from(s);
1457 let s2 = DOMString::from("aaa aa");
1458 let atom2 = Namespace::from(s2);
1459 assert_eq!(atom1, atom2);
1460 let s3 = from_latin1(vec![b'a', b'a', b'a', LATIN1_POWER2, b'a', b'a']);
1461 let atom3 = Namespace::from(s3);
1462 assert_ne!(atom1, atom3);
1463 let s4 = DOMString::from_static("aaa aa");
1464 let atom4 = Namespace::from(s4);
1465 assert_eq!(atom2, atom4);
1466 }
1467
1468 #[test]
1469 fn localname() {
1470 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_SPACE, b'a', b'a']);
1471 let atom1 = LocalName::from(s);
1472 let s2 = DOMString::from("aaa aa");
1473 let atom2 = LocalName::from(s2);
1474 assert_eq!(atom1, atom2);
1475 let s3 = from_latin1(vec![b'a', b'a', b'a', LATIN1_POWER2, b'a', b'a']);
1476 let atom3 = LocalName::from(s3);
1477 assert_ne!(atom1, atom3);
1478 let s4 = DOMString::from_static("aaa aa");
1479 let atom4 = LocalName::from(s4);
1480 assert_eq!(atom2, atom4);
1481 }
1482
1483 #[test]
1484 fn is_ascii_lowercase() {
1485 let s = from_latin1(vec![b'a', b'a', b'a', ASCII_SPACE, b'a', b'a']);
1486 assert!(!s.is_ascii_lowercase());
1487 let s = from_latin1(vec![b'a', b'a', b'a', LATIN1_PILLCROW, b'a', b'a']);
1488 assert!(!s.is_ascii_lowercase());
1489 let s = from_latin1(vec![b'a', b'a', b'a', b'a', b'z']);
1490 assert!(s.is_ascii_lowercase());
1491 let s = from_latin1(vec![b'`', b'a', b'a', b'a', b'z']);
1492 assert!(!s.is_ascii_lowercase());
1493 let s = DOMString::from("`aaaz");
1494 assert!(!s.is_ascii_lowercase());
1495 let s = DOMString::from("aaaz");
1496 assert!(s.is_ascii_lowercase());
1497 let s = DOMString::from_static("aaaz");
1498 assert!(s.is_ascii_lowercase());
1499 }
1500
1501 #[test]
1502 fn test_as_bytes() {
1503 const ASCII_SMALL_A: u8 = b'a';
1504 const ASCII_SMALL_Z: u8 = b'z';
1505
1506 let v1 = vec![b'a', b'a', b'a', LATIN1_PILLCROW, b'a', b'a'];
1507 let s = from_latin1(v1.clone());
1508 assert_eq!(
1509 *s.as_bytes(),
1510 [
1511 ASCII_SMALL_A,
1512 ASCII_SMALL_A,
1513 ASCII_SMALL_A,
1514 UTF8_PILLCROW[0],
1515 UTF8_PILLCROW[1],
1516 ASCII_SMALL_A,
1517 ASCII_SMALL_A
1518 ]
1519 );
1520
1521 let v2 = vec![b'a', b'a', b'a', b'a', b'z'];
1522 let s = from_latin1(v2.clone());
1523 assert_eq!(
1524 *s.as_bytes(),
1525 [
1526 ASCII_SMALL_A,
1527 ASCII_SMALL_A,
1528 ASCII_SMALL_A,
1529 ASCII_SMALL_A,
1530 ASCII_SMALL_Z
1531 ]
1532 );
1533
1534 let str = "abc%$²".to_owned();
1535 let s = DOMString::from(str.clone());
1536 assert_eq!(&*s.as_bytes(), str.as_bytes());
1537 let str = "AbBcC❤&%$#".to_owned();
1538 let s = DOMString::from(str.clone());
1539 assert_eq!(&*s.as_bytes(), str.as_bytes());
1540 let s = DOMString::from_static("AbBcC❤&%$#");
1541 assert_eq!(&*s.as_bytes(), str.as_bytes());
1542 }
1543}