1use super::*;
4use super::super::regex::{self, RegExpData};
7
8pub(crate) fn text_encode(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
10 let s = arg(a, 0).to_js_string();
11 Ok(make_uint8array(it, s.as_bytes().to_vec()))
12}
13pub(crate) fn text_encode_into(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
20 let source = arg(a, 0).to_js_string();
21 let dest = arg(a, 1);
22 let Value::Object(dest_obj) = &dest else {
23 return Ok(Value::Undefined);
24 };
25 let capacity = match &dest_obj.borrow().kind {
26 ObjKind::Array(items) => items.len(),
27 ObjKind::Bytes(st) => st.len,
28 _ => 0,
29 };
30 let mut read_units = 0usize;
31 let mut written_bytes: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
32 for c in source.chars() {
33 let mut buf = [0u8; 4];
34 let bytes = c.encode_utf8(&mut buf).as_bytes();
35 if written_bytes.len() + bytes.len() > capacity {
36 break;
37 }
38 written_bytes.extend_from_slice(bytes);
39 read_units += c.len_utf16();
40 }
41 if let ObjKind::Bytes(st) = &dest_obj.borrow().kind {
42 for (i, b) in written_bytes.iter().enumerate() {
43 st.set(i, *b as f64);
44 }
45 }
46 if let ObjKind::Array(items) = &mut dest_obj.borrow_mut().kind {
47 for (i, b) in written_bytes.iter().enumerate() {
48 if let Some(slot) = items.get_mut(i) {
49 *slot = Value::Number(*b as f64);
50 }
51 }
52 }
53 let result = Obj::plain();
54 {
55 let mut b = result.borrow_mut();
56 b.props.insert("read".into(), Value::Number(read_units as f64));
57 b.props
58 .insert("written".into(), Value::Number(written_bytes.len() as f64));
59 }
60 Ok(Value::Object(result))
61}
62pub(crate) fn normalize_text_encoding_label(label: &str) -> &'static str {
66 match label.trim().to_lowercase().as_str() {
67 "utf-16le" | "utf-16" => "utf-16le",
68 "utf-16be" => "utf-16be",
69 _ => "utf-8",
70 }
71}
72pub(crate) fn decode_utf16_bytes(bytes: &[u8], little_endian: bool) -> alloc::string::String {
75 let units: alloc::vec::Vec<u16> = bytes
76 .chunks(2)
77 .map(|c| {
78 let lo = *c.first().unwrap_or(&0) as u16;
79 let hi = *c.get(1).unwrap_or(&0) as u16;
80 if little_endian {
81 lo | (hi << 8)
82 } else {
83 (lo << 8) | hi
84 }
85 })
86 .collect();
87 core::char::decode_utf16(units)
88 .map(|r| r.unwrap_or(core::char::REPLACEMENT_CHARACTER))
89 .collect()
90}
91pub(crate) fn utf8_incomplete_trailing_len(bytes: &[u8]) -> usize {
96 let len = bytes.len();
97 for back in 1..=3.min(len) {
98 let b = bytes[len - back];
99 if b & 0b1100_0000 == 0b1000_0000 {
100 continue; }
102 let needed = if b & 0b1000_0000 == 0 {
103 1
104 } else if b & 0b1110_0000 == 0b1100_0000 {
105 2
106 } else if b & 0b1111_0000 == 0b1110_0000 {
107 3
108 } else if b & 0b1111_1000 == 0b1111_0000 {
109 4
110 } else {
111 1 };
113 return if needed > back { back } else { 0 };
114 }
115 0
116}
117pub(crate) fn text_decode(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
126 let mut bytes: Vec<u8> = if let Value::Object(o) = &this {
127 match o.borrow().props.get("_pending") {
128 Some(Value::Object(p)) => match &p.borrow().kind {
129 ObjKind::Array(items) => items.iter().map(|v| v.to_number() as u8).collect(),
130 _ => Vec::new(),
131 },
132 _ => Vec::new(),
133 }
134 } else {
135 Vec::new()
136 };
137 let mut arg0_bytes_f64 = alloc::vec::Vec::new();
146 blob_part_bytes(&arg(a, 0), &mut arg0_bytes_f64);
147 bytes.extend(arg0_bytes_f64.iter().map(|v| *v as i64 as u8));
148 let stream = obj_prop(&arg(a, 1), "stream").map(|v| v.truthy()).unwrap_or(false);
149 let enc = if let Value::Object(o) = &this {
150 o.borrow()
151 .props
152 .get("encoding")
153 .map(|v| v.to_js_string())
154 .unwrap_or_else(|| String::from("utf-8"))
155 } else {
156 String::from("utf-8")
157 };
158 let pending_len = if stream {
159 match enc.as_str() {
160 "utf-16le" | "utf-16be" => bytes.len() % 2,
161 _ => utf8_incomplete_trailing_len(&bytes),
162 }
163 } else {
164 0
165 };
166 let ready_len = bytes.len() - pending_len;
167 let (ready, pending) = bytes.split_at(ready_len);
168 if let Value::Object(o) = &this {
169 let pending_vals: Vec<Value> = pending.iter().map(|&b| Value::Number(b as f64)).collect();
170 o.borrow_mut()
171 .props
172 .insert("_pending".into(), Value::Object(Obj::array(pending_vals)));
173 }
174 let decoded = match enc.as_str() {
175 "utf-16le" => decode_utf16_bytes(ready, true),
176 "utf-16be" => decode_utf16_bytes(ready, false),
177 _ => alloc::string::String::from_utf8_lossy(ready).into_owned(),
178 };
179 Ok(Value::str(decoded))
180}
181pub(crate) fn text_encoder_ctor(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
182 let o = Obj::plain();
183 {
184 let mut b = o.borrow_mut();
185 b.props.insert("encoding".into(), Value::str("utf-8"));
186 b.props.insert("encode".into(), nv("encode", text_encode));
187 b.props
188 .insert("encodeInto".into(), nv("encodeInto", text_encode_into));
189 }
190 Ok(Value::Object(o))
191}
192pub(crate) fn text_decoder_ctor(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
195 let label = arg(a, 0).to_js_string();
196 let enc = if label.is_empty() {
197 "utf-8"
198 } else {
199 normalize_text_encoding_label(&label)
200 };
201 let o = Obj::plain();
202 {
203 let mut b = o.borrow_mut();
204 b.props.insert("encoding".into(), Value::str(enc));
205 b.props.insert("decode".into(), nv("decode", text_decode));
206 }
207 Ok(Value::Object(o))
208}
209
210pub(crate) fn url_create_object_url(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
216 let target = arg(a, 0);
217 let is_media_source = if let Value::Object(o) = &target {
218 let borrow = o.borrow();
219 borrow.props.contains_key("sourceBuffers")
220 || borrow.props.get("_is_media_source").map(|v| v.truthy()).unwrap_or(false)
221 } else {
222 false
223 };
224
225 let id = crypto_random_uuid(it, t, &[])?.to_js_string();
226 let url = if is_media_source {
227 alloc::format!("blob:mediasource-{}", id)
228 } else {
229 alloc::format!("blob:{}", id)
230 };
231
232 if is_media_source {
233 let existing = match it.global.borrow().vars.get("__media_source_urls") {
234 Some(Value::Object(r)) => Some(r.clone()),
235 _ => None,
236 };
237 let registry = match existing {
238 Some(r) => r,
239 None => {
240 let r = Obj::plain();
241 it.global.borrow_mut().vars.insert("__media_source_urls".into(), Value::Object(r.clone()));
242 r
243 }
244 };
245 super::mse::register_media_source_url(&url, &target);
246 registry.borrow_mut().props.insert(url.clone(), target);
247 } else {
248 let is_blob = if let Value::Object(o) = &target {
249 o.borrow().props.contains_key("_blob_bytes")
250 } else {
251 false
252 };
253 if is_blob {
254 let existing = match it.global.borrow().vars.get("__blob_urls") {
255 Some(Value::Object(r)) => Some(r.clone()),
256 _ => None,
257 };
258 let registry = match existing {
259 Some(r) => r,
260 None => {
261 let r = Obj::plain();
262 it.global.borrow_mut().vars.insert("__blob_urls".into(), Value::Object(r.clone()));
263 r
264 }
265 };
266 registry.borrow_mut().props.insert(url.clone(), target);
267 }
268 }
269
270 Ok(Value::str(url))
271}
272pub(crate) fn url_revoke_object_url(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
274 let url = arg(a, 0).to_js_string();
275 if let Some(Value::Object(registry)) = it.global.borrow().vars.get("__media_source_urls") {
276 registry.borrow_mut().props.shift_remove(&url);
277 }
278 if let Some(Value::Object(registry)) = it.global.borrow().vars.get("__blob_urls") {
279 registry.borrow_mut().props.shift_remove(&url);
280 }
281 Ok(Value::Undefined)
282}
283pub(crate) fn crypto_random_uuid(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
284 let mut bytes = [0u8; 16];
285 for b in bytes.iter_mut() {
286 *b = (next_rand_u64() & 0xff) as u8;
287 }
288 bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; let mut s = String::new();
291 for (i, b) in bytes.iter().enumerate() {
292 if i == 4 || i == 6 || i == 8 || i == 10 {
293 s.push('-');
294 }
295 s.push_str(&format!("{:02x}", b));
296 }
297 Ok(Value::str(s))
298}
299pub(crate) fn crypto_get_random_values(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
306 let v = arg(a, 0);
307 if let Value::Object(o) = &v {
308 let kind = o
309 .borrow()
310 .props
311 .get("_ta_kind")
312 .map(|v| v.to_js_string())
313 .map(|tag| TaKind::from_name(tag.as_str()));
314 if let ObjKind::Bytes(st) = &o.borrow().kind {
315 for i in 0..st.len {
316 st.set(i, (next_rand_u64() & 0xff) as f64);
317 }
318 return Ok(v.clone());
319 }
320 let mut b = o.borrow_mut();
321 if let ObjKind::Array(items) = &mut b.kind {
322 for it2 in items.iter_mut() {
323 let raw = Value::Number(next_rand_u64() as f64);
324 *it2 = Value::Number(match kind {
325 Some(k) => k.convert(&raw),
326 None => (next_rand_u64() & 0xff) as f64,
327 });
328 }
329 }
330 }
331 Ok(v)
332}
333
334pub(crate) fn deep_clone_value(
342 it: &mut Interp,
343 v: &Value,
344 seen: &mut Vec<(super::super::value::ObjRef, super::super::value::ObjRef)>,
345) -> Result<Value, Value> {
346 deep_clone_value_depth(it, v, seen, 0)
347}
348
349fn deep_clone_value_depth(
350 it: &mut Interp,
351 v: &Value,
352 seen: &mut Vec<(super::super::value::ObjRef, super::super::value::ObjRef)>,
353 depth: usize,
354) -> Result<Value, Value> {
355 if depth > 30 {
364 return Err(make_dom_exception("DataCloneError", "Exceeded maximum call stack size / nesting depth limit"));
365 }
366 let error_proto = match it.global.borrow().vars.get("Error") {
367 Some(Value::Object(ctor)) => match ctor.borrow().props.get("prototype") {
368 Some(Value::Object(p)) => Some(p.clone()),
369 _ => None,
370 },
371 _ => None,
372 };
373 match v {
374 Value::Object(o) => {
375 if let Some((_, cloned)) = seen.iter().find(|(orig, _)| Rc::ptr_eq(orig, o)) {
376 return Ok(Value::Object(cloned.clone()));
377 }
378 if o.borrow().is_callable() {
387 return Err(make_dom_exception("DataCloneError", "could not be cloned"));
388 }
389 if matches!(&o.borrow().kind, ObjKind::PromiseObj(_)) {
398 return Err(make_dom_exception("DataCloneError", "could not be cloned"));
399 }
400 if matches!(&o.borrow().kind, ObjKind::Proxy { .. }) {
409 let target = unwrap_proxy_target(o);
410 let cloned = deep_clone_value_depth(it, &Value::Object(target), seen, depth + 1)?;
411 if let Value::Object(c) = &cloned {
412 seen.push((o.clone(), c.clone()));
413 }
414 return Ok(cloned);
415 }
416 if let ObjKind::DateObj(ms) = &o.borrow().kind {
423 return Ok(Value::Object(Obj::date_obj(*ms)));
424 }
425 let regex_info = match &o.borrow().kind {
426 ObjKind::RegExpObj(r) => {
427 let d = r.borrow();
428 Some((d.re.source.clone(), d.re.flags.clone(), d.last_index))
429 }
430 _ => None,
431 };
432 if let Some((source, flags, last_index)) = regex_info {
433 let re = regex::Regex::new(&source, &flags);
434 return Ok(Value::Object(Obj::regexp(RegExpData { re, last_index })));
435 }
436 let bytes_ta = match &o.borrow().kind {
438 ObjKind::Bytes(st) => Some(st.clone()),
439 _ => None,
440 };
441 if let Some(st) = bytes_ta {
442 let tag = o.borrow().props.get("_ta_kind").map(|v| v.to_js_string()).unwrap_or_default();
443 let new_val = super::typed_array::make_bytes_ta(
444 it,
445 crate::os_lib::js::value::ByteStore::new(st.to_vec(), st.kind),
446 TaKind::from_name(tag.as_str()),
447 );
448 if let Value::Object(new) = &new_val {
449 seen.push((o.clone(), new.clone()));
450 }
451 return Ok(new_val);
452 }
453 let is_array = matches!(&o.borrow().kind, ObjKind::Array(_));
454 if is_array {
455 let ta_kind_tag = o.borrow().props.get("_ta_kind").map(|v| v.to_js_string());
462 if let Some(tag) = ta_kind_tag {
463 let items = match &o.borrow().kind {
464 ObjKind::Array(items) => items.iter().map(|v| v.to_number()).collect(),
465 _ => unreachable!(),
466 };
467 let new_val = make_typed_array(it, items, TaKind::from_name(tag.as_str()));
468 if let Value::Object(new) = &new_val {
469 seen.push((o.clone(), new.clone()));
470 }
471 return Ok(new_val);
472 }
473 let new = Obj::array(Vec::new());
474 seen.push((o.clone(), new.clone()));
475 let items = match &o.borrow().kind {
476 ObjKind::Array(items) => items.clone(),
477 _ => unreachable!(),
478 };
479 let mut cloned_items: Vec<Value> = Vec::with_capacity(items.len());
480 for x in &items {
481 cloned_items.push(deep_clone_value_depth(it, x, seen, depth + 1)?);
482 }
483 if let ObjKind::Array(v) = &mut new.borrow_mut().kind {
484 *v = cloned_items;
485 }
486 return Ok(Value::Object(new));
487 }
488 let is_map = matches!(&o.borrow().kind, ObjKind::MapObj(_));
492 if is_map {
493 let new = Obj::map_obj(Vec::new());
494 seen.push((o.clone(), new.clone()));
495 let entries = match &o.borrow().kind {
496 ObjKind::MapObj(e) => e.clone(),
497 _ => unreachable!(),
498 };
499 let mut cloned: Vec<(Value, Value)> = Vec::with_capacity(entries.len());
500 for (k, val) in &entries {
501 let ck = deep_clone_value_depth(it, k, seen, depth + 1)?;
502 let cv = deep_clone_value_depth(it, val, seen, depth + 1)?;
503 cloned.push((ck, cv));
504 }
505 if let ObjKind::MapObj(e) = &mut new.borrow_mut().kind {
506 *e = cloned;
507 }
508 return Ok(Value::Object(new));
509 }
510 let is_set = matches!(&o.borrow().kind, ObjKind::SetObj(_));
511 if is_set {
512 let new = Obj::set_obj(Vec::new());
513 seen.push((o.clone(), new.clone()));
514 let items = match &o.borrow().kind {
515 ObjKind::SetObj(s) => s.clone(),
516 _ => unreachable!(),
517 };
518 let mut cloned: Vec<Value> = Vec::with_capacity(items.len());
519 for x in &items {
520 cloned.push(deep_clone_value_depth(it, x, seen, depth + 1)?);
521 }
522 if let ObjKind::SetObj(s) = &mut new.borrow_mut().kind {
523 *s = cloned;
524 }
525 return Ok(Value::Object(new));
526 }
527 if let Some(Value::Object(bytes_obj)) = o.borrow().props.get("_blob_bytes").cloned() {
534 let bytes: alloc::vec::Vec<f64> = match &bytes_obj.borrow().kind {
535 ObjKind::Array(items) => items.iter().map(|v| v.to_number()).collect(),
536 _ => alloc::vec::Vec::new(),
537 };
538 let mime = o.borrow().props.get("type").map(|v| v.to_js_string()).unwrap_or_default();
539 let file_meta = o.borrow().props.get("name").cloned().map(|name| {
549 let last_modified =
550 o.borrow().props.get("lastModified").cloned().unwrap_or(Value::Number(0.0));
551 let webkit_relative_path =
552 o.borrow().props.get("webkitRelativePath").cloned().unwrap_or_else(|| Value::str(""));
553 (name, last_modified, webkit_relative_path)
554 });
555 let new_val = make_blob(bytes, mime);
556 if let (Value::Object(new), Some((name, last_modified, webkit_relative_path))) = (&new_val, file_meta) {
557 new.borrow_mut().props.insert("name".into(), name);
558 new.borrow_mut().props.insert("lastModified".into(), last_modified);
559 new.borrow_mut().props.insert("webkitRelativePath".into(), webkit_relative_path);
560 }
561 if let Value::Object(new) = &new_val {
562 seen.push((o.clone(), new.clone()));
563 }
564 return Ok(new_val);
565 }
566 if o.borrow().props.contains_key("_dv_bytes") {
567 let byte_values: alloc::vec::Vec<Value> = super::typed_array::dv_slice_bytes(o)
568 .unwrap_or_default()
569 .into_iter()
570 .map(Value::Number)
571 .collect();
572 let new_buffer = make_arraybuffer_with_bytes(byte_values.iter().map(|v| v.to_number()).collect());
573 let new_val = make_dataview(new_buffer, 0, byte_values);
574 if let Value::Object(new) = &new_val {
575 seen.push((o.clone(), new.clone()));
576 }
577 return Ok(new_val);
578 }
579 let is_arraybuffer = {
583 let b = o.borrow();
584 b.props.contains_key("byteLength")
585 && matches!(b.props.get("slice"), Some(Value::Object(f)) if f.borrow().is_callable())
586 };
587 if is_arraybuffer {
588 let is_resizable = matches!(o.borrow().props.get("resizable"), Some(Value::Bool(true)));
596 let max_byte_length = o.borrow().props.get("maxByteLength").map(|v| v.to_number() as usize);
597 let bytes_prop = o.borrow().props.get("_bytes").cloned();
598 let byte_length = o
599 .borrow()
600 .props
601 .get("byteLength")
602 .map(|v| v.to_number() as usize)
603 .unwrap_or(0);
604 let new_val = make_arraybuffer_ex(byte_length, if is_resizable { max_byte_length } else { None });
605 if let (Value::Object(new), Some(Value::Object(bytes_obj))) = (&new_val, &bytes_prop) {
606 let bytes = super::typed_array::obj_byte_values(bytes_obj).unwrap_or_default();
607 new.borrow_mut().props.insert(
608 "_bytes".into(),
609 Value::Object(Obj::bytes(crate::os_lib::js::value::ByteStore::new(bytes, 0))),
610 );
611 }
612 if let Value::Object(new) = &new_val {
613 seen.push((o.clone(), new.clone()));
614 }
615 return Ok(new_val);
616 }
617 let is_error = error_proto.as_ref().is_some_and(|ep| {
627 let mut cur = o.borrow().proto.clone();
628 let mut guard = 0;
629 while let Some(c) = cur {
630 if Rc::ptr_eq(&c, ep) {
631 return true;
632 }
633 cur = c.borrow().proto.clone();
634 guard += 1;
635 if guard > 1000 {
636 break;
637 }
638 }
639 false
640 });
641 let new = Obj::plain();
642 if is_error {
643 new.borrow_mut().proto = o.borrow().proto.clone();
644 }
645 seen.push((o.clone(), new.clone()));
646 let props: Vec<(String, Value)> =
647 o.borrow().props.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
648 for (k, val) in props {
649 let cv = deep_clone_value_depth(it, &val, seen, depth + 1)?;
650 new.borrow_mut().props.insert(k, cv);
651 }
652 Ok(Value::Object(new))
653 }
654 other => Ok(other.clone()),
655 }
656}
657pub(crate) fn structured_clone(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
665 let mut seen = Vec::new();
666 let result = deep_clone_value(it, &arg(a, 0), &mut seen)?;
667 if let Value::Object(opts) = arg(a, 1) {
668 let transfer_list = opts.borrow().props.get("transfer").cloned();
669 if let Some(list) = transfer_list {
670 for item in this_items(&list) {
671 if let Value::Object(o) = &item {
672 let is_arraybuffer = {
673 let b = o.borrow();
674 b.props.contains_key("byteLength")
675 && matches!(b.props.get("slice"), Some(Value::Object(f)) if f.borrow().is_callable())
676 };
677 if is_arraybuffer {
678 let mut b = o.borrow_mut();
679 b.props.insert("detached".into(), Value::Bool(true));
680 b.props.shift_remove("_bytes");
681 }
682 }
683 }
684 }
685 }
686 Ok(result)
687}
688