1use std::borrow::ToOwned;
7use std::default::Default;
8use std::hash::{Hash, Hasher};
9use std::ops::Deref;
10use std::ptr::NonNull;
11use std::str::FromStr;
12use std::{fmt, ops, slice, str};
13
14use js::context::JSContext;
15use js::conversions::latin1_to_string;
16use js::gc::{HandleObject, HandleValue};
17use js::jsapi::{JS_DeprecatedStringHasLatin1Chars, JSString};
18use js::rust::wrappers2::{JS_GetTwoByteStringCharsAndLength, ToJSON};
19use js::rust::{
20 HandleValue as SafeHandleValue, MutableHandleString as SafeMutableHandleString, ToString,
21};
22
23pub use crate::domstring::DOMString;
24use crate::error::Error;
25
26#[derive(Clone, Debug, Default, Eq, JSTraceable, MallocSizeOf, PartialEq)]
28pub struct ByteString(Vec<u8>);
29
30impl ByteString {
31 pub fn new(value: Vec<u8>) -> ByteString {
33 ByteString(value)
34 }
35
36 pub fn as_str(&self) -> Option<&str> {
39 str::from_utf8(&self.0).ok()
40 }
41
42 pub fn len(&self) -> usize {
44 self.0.len()
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.0.is_empty()
50 }
51
52 pub fn to_lower(&self) -> ByteString {
54 ByteString::new(self.0.to_ascii_lowercase())
55 }
56}
57
58impl From<ByteString> for Vec<u8> {
59 fn from(byte_string: ByteString) -> Vec<u8> {
60 byte_string.0
61 }
62}
63
64impl Hash for ByteString {
65 fn hash<H: Hasher>(&self, state: &mut H) {
66 self.0.hash(state);
67 }
68}
69
70impl FromStr for ByteString {
71 type Err = ();
72 fn from_str(s: &str) -> Result<ByteString, ()> {
73 Ok(ByteString::new(s.to_owned().into_bytes()))
74 }
75}
76
77impl ops::Deref for ByteString {
78 type Target = [u8];
79 fn deref(&self) -> &[u8] {
80 &self.0
81 }
82}
83
84#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
87pub struct USVString(pub String);
88
89impl USVString {
90 pub fn new() -> USVString {
92 USVString(String::new())
93 }
94}
95
96impl Deref for USVString {
97 type Target = str;
98
99 #[inline]
100 fn deref(&self) -> &str {
101 &self.0
102 }
103}
104
105impl AsRef<str> for USVString {
106 fn as_ref(&self) -> &str {
107 &self.0
108 }
109}
110
111impl fmt::Display for USVString {
112 #[inline]
113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 fmt::Display::fmt(&self.0, f)
115 }
116}
117
118impl PartialEq<str> for USVString {
119 fn eq(&self, other: &str) -> bool {
120 self.0 == other
121 }
122}
123
124impl<'a> PartialEq<&'a str> for USVString {
125 fn eq(&self, other: &&'a str) -> bool {
126 self.0 == *other
127 }
128}
129
130impl From<String> for USVString {
131 fn from(contents: String) -> USVString {
132 USVString(contents)
133 }
134}
135
136impl From<USVString> for String {
137 fn from(value: USVString) -> Self {
138 value.0
139 }
140}
141
142impl From<USVString> for DOMString {
143 fn from(value: USVString) -> Self {
144 value.0.into()
145 }
146}
147
148pub fn is_token(s: &[u8]) -> bool {
151 if s.is_empty() {
152 return false; }
154 s.iter().all(|&x| {
155 match x {
157 0..=31 | 127 => false, 40 | 41 | 60 | 62 | 64 | 44 | 59 | 58 | 92 | 34 | 47 | 91 | 93 | 63 | 61 | 123 |
159 125 | 32 => false, x if x > 127 => false, _ => true,
162 }
163 })
164}
165
166pub fn serialize_jsval_to_json_utf8(
172 cx: &mut JSContext,
173 data: HandleValue,
174) -> Result<DOMString, Error> {
175 #[repr(C)]
176 struct ToJSONCallbackData {
177 string: Option<String>,
178 }
179
180 let mut out_str = ToJSONCallbackData { string: None };
181
182 #[expect(unsafe_code)]
183 unsafe extern "C" fn write_callback(
184 string: *const u16,
185 len: u32,
186 data: *mut std::ffi::c_void,
187 ) -> bool {
188 let data = data as *mut ToJSONCallbackData;
189 let string_chars = unsafe { slice::from_raw_parts(string, len as usize) };
190 unsafe { &mut *data }
191 .string
192 .get_or_insert_with(Default::default)
193 .push_str(&String::from_utf16_lossy(string_chars));
194 true
195 }
196
197 unsafe {
199 let stringify_result = ToJSON(
200 cx,
201 data,
202 HandleObject::null(),
203 HandleValue::null(),
204 Some(write_callback),
205 &mut out_str as *mut ToJSONCallbackData as *mut _,
206 );
207 if !stringify_result {
210 return Err(Error::JSFailed);
211 }
212 }
213
214 out_str
219 .string
220 .map(Into::into)
221 .ok_or_else(|| Error::Type(c"unable to serialize JSON".to_owned()))
222}
223
224fn has_latin1_chars(jsstr: *mut JSString) -> bool {
225 unsafe { JS_DeprecatedStringHasLatin1Chars(jsstr) }
226}
227
228pub fn to_js_string(
230 cx: &mut JSContext,
231 source: SafeHandleValue,
232 target: &mut SafeMutableHandleString,
233) -> Result<(), Error> {
234 rooted!(&in(cx) let jsstr = unsafe { ToString(cx, source) });
235 if jsstr.is_null() {
236 return Err(Error::JSFailed);
237 }
238 target.set(*jsstr);
239 Ok(())
240}
241
242pub struct CodeUnits(pub Vec<u16>);
244
245pub enum ConversionResult {
248 CodeUnits(CodeUnits),
249 String(String),
250}
251
252pub fn js_string_to_code_units(
256 cx: &mut JSContext,
257 data: SafeHandleValue,
258 mut target: SafeMutableHandleString,
259) -> Result<ConversionResult, Error> {
260 to_js_string(cx, data, &mut target)?;
269
270 if has_latin1_chars(*target) {
274 let string =
275 unsafe { latin1_to_string(cx, NonNull::new(*target).expect("jsstr cannot be null")) };
276 Ok(ConversionResult::String(string))
277 } else {
278 let maybe_ill_formed_code_units = unsafe {
279 let mut len = 0;
280 let data = JS_GetTwoByteStringCharsAndLength(cx, *target, &mut len);
281
282 std::slice::from_raw_parts(data, len).to_vec()
286 };
287 Ok(ConversionResult::CodeUnits(CodeUnits(
288 maybe_ill_formed_code_units,
289 )))
290 }
291}