1use super::*;
4
5pub(crate) fn window_confirm(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
14 let _ = console_log(it, this, a);
15 Ok(Value::Bool(true))
16}
17pub(crate) fn window_prompt(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
21 let _ = console_log(it, this, a);
22 match a.get(1) {
23 Some(v) if !matches!(v, Value::Undefined) => Ok(v.clone()),
24 _ => Ok(Value::Null),
25 }
26}
27
28pub(crate) fn window_open(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
38 Ok(Value::Null)
39}
40pub(crate) fn window_close(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
44 Ok(Value::Undefined)
45}
46pub(crate) fn window_stop(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
51 Ok(Value::Undefined)
52}
53pub(crate) fn window_focus(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
58 Ok(Value::Undefined)
59}
60
61pub(crate) fn window_scroll_to(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
70 let y = if let Some(Value::Object(o)) = a.first() {
71 o.borrow()
72 .props
73 .get("top")
74 .map(|v| v.to_number())
75 .unwrap_or(0.0)
76 } else {
77 arg(a, 1).to_number()
78 };
79 it.dom.borrow_mut().pending_scroll_abs_y = Some(y as i32);
80 Ok(Value::Undefined)
81}
82pub(crate) fn window_scroll_by(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
86 let dy = if let Some(Value::Object(o)) = a.first() {
87 o.borrow()
88 .props
89 .get("top")
90 .map(|v| v.to_number())
91 .unwrap_or(0.0)
92 } else {
93 arg(a, 1).to_number()
94 };
95 let mut dom = it.dom.borrow_mut();
96 dom.pending_scroll_by_y = Some(dom.pending_scroll_by_y.unwrap_or(0) + dy as i32);
97 Ok(Value::Undefined)
98}
99pub(crate) fn element_scroll_into_view(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
100 Ok(Value::Undefined) }
102
103pub(crate) fn window_match_media(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
105 let query = arg(a, 0).to_js_string();
106 let matches = crate::os_lib::css::media_query_matches(&query);
113 let mql = Obj::plain();
114 {
115 let mut b = mql.borrow_mut();
116 b.props.insert("matches".into(), Value::Bool(matches));
117 b.props.insert("media".into(), Value::str(query));
118 b.props
119 .insert("addEventListener".into(), nv("addEventListener", dom_noop));
120 b.props
121 .insert("addListener".into(), nv("addListener", dom_noop));
122 b.props
123 .insert("removeListener".into(), nv("removeListener", dom_noop));
124 b.props.insert(
125 "removeEventListener".into(),
126 nv("removeEventListener", dom_noop),
127 );
128 }
129 Ok(Value::Object(mql))
130}
131
132pub(crate) fn push_window_listener(it: &mut Interp, key: &str, cb: Value, signal: Option<Value>) {
142 let w = match it.global.borrow().vars.get("window") {
143 Some(Value::Object(w)) => w.clone(),
144 _ => return,
145 };
146 let entry = Value::Object(Obj::array(alloc::vec![cb, signal.unwrap_or(Value::Undefined)]));
147 let existing = w.borrow().props.get(key).cloned();
148 match existing {
149 Some(Value::Object(arr)) if matches!(arr.borrow().kind, ObjKind::Array(_)) => {
150 if let ObjKind::Array(items) = &mut arr.borrow_mut().kind {
151 items.push(entry);
152 }
153 }
154 _ => {
155 let arr = Obj::array(alloc::vec![entry]);
156 w.borrow_mut().props.insert(key.into(), Value::Object(arr));
157 }
158 }
159}
160pub(crate) fn window_listener_aborted(entry: &Value) -> bool {
163 if let Value::Object(o) = entry {
164 if let ObjKind::Array(items) = &o.borrow().kind {
165 if let Some(sig) = items.get(1) {
166 return super::super::dom_bridge::is_signal_aborted(sig);
167 }
168 }
169 }
170 false
171}
172
173pub(crate) fn window_add_event_listener(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
178 let event = arg(a, 0).to_js_string();
179 let cb = arg(a, 1);
180 let signal = parse_listener_signal(&arg(a, 2));
184 if signal.as_ref().is_some_and(super::super::dom_bridge::is_signal_aborted) {
185 return Ok(Value::Undefined);
186 }
187 match event.as_str() {
188 "load" | "DOMContentLoaded" | "readystatechange" => {
192 it.call_listener(&cb, Value::Undefined, &[], "window load listener");
194 }
195 "scroll" => {
196 push_window_listener(it, "_scroll_listeners", cb, signal);
197 }
198 "popstate" => {
199 push_window_listener(it, "_popstate_listeners", cb, signal);
200 }
201 "hashchange" => {
202 push_window_listener(it, "_hashchange_listeners", cb, signal);
203 }
204 "resize" => {
205 push_window_listener(it, "_resize_listeners", cb, signal);
206 }
207 "online" => {
214 push_window_listener(it, "_online_listeners", cb, signal);
215 }
216 "offline" => {
217 push_window_listener(it, "_offline_listeners", cb, signal);
218 }
219 "beforeunload" => {
225 push_window_listener(it, "_beforeunload_listeners", cb, signal);
226 }
227 "unload" => {
228 push_window_listener(it, "_unload_listeners", cb, signal);
229 }
230 "pageshow" => {
231 push_window_listener(it, "_pageshow_listeners", cb, signal);
232 }
233 "pagehide" => {
234 push_window_listener(it, "_pagehide_listeners", cb, signal);
235 }
236 "visibilitychange" => {
237 push_window_listener(it, "_visibilitychange_listeners", cb, signal);
238 }
239 "languagechange" => {
240 push_window_listener(it, "_languagechange_listeners", cb, signal);
241 }
242 "storage" => {
248 push_window_listener(it, "_storage_listeners", cb, signal);
249 }
250 "beforeprint" => {
255 push_window_listener(it, "_beforeprint_listeners", cb, signal);
256 }
257 "afterprint" => {
258 push_window_listener(it, "_afterprint_listeners", cb, signal);
259 }
260 "message" => {
263 push_window_listener(it, "_message_listeners", cb, signal);
264 }
265 "error" => {
268 push_window_listener(it, "_error_listeners", cb, signal);
269 }
270 _ => {}
271 }
272 Ok(Value::Undefined)
273}
274
275pub(crate) fn window_remove_event_listener(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
282 let event = arg(a, 0).to_js_string();
283 let cb = arg(a, 1);
284 let key = match event.as_str() {
285 "scroll" => "_scroll_listeners",
286 "popstate" => "_popstate_listeners",
287 "hashchange" => "_hashchange_listeners",
288 "resize" => "_resize_listeners",
289 "online" => "_online_listeners",
290 "offline" => "_offline_listeners",
291 "beforeunload" => "_beforeunload_listeners",
292 "unload" => "_unload_listeners",
293 "pageshow" => "_pageshow_listeners",
294 "pagehide" => "_pagehide_listeners",
295 "visibilitychange" => "_visibilitychange_listeners",
296 "languagechange" => "_languagechange_listeners",
297 "storage" => "_storage_listeners",
298 "beforeprint" => "_beforeprint_listeners",
299 "afterprint" => "_afterprint_listeners",
300 "message" => "_message_listeners",
301 "error" => "_error_listeners",
302 _ => return Ok(Value::Undefined),
303 };
304 let w = match it.global.borrow().vars.get("window") {
305 Some(Value::Object(w)) => w.clone(),
306 _ => return Ok(Value::Undefined),
307 };
308 let arr = match w.borrow().props.get(key) {
309 Some(Value::Object(arr)) => arr.clone(),
310 _ => return Ok(Value::Undefined),
311 };
312 if let ObjKind::Array(items) = &mut arr.borrow_mut().kind {
313 items.retain(|entry| {
314 let stored_cb = match entry {
315 Value::Object(o) => match &o.borrow().kind {
316 ObjKind::Array(pair) => pair.first().cloned().unwrap_or(Value::Undefined),
317 _ => entry.clone(),
318 },
319 _ => entry.clone(),
320 };
321 match (&stored_cb, &cb) {
322 (Value::Object(x), Value::Object(y)) => !Rc::ptr_eq(x, y),
323 _ => true,
324 }
325 });
326 }
327 Ok(Value::Undefined)
328}
329pub(crate) fn window_dispatch_event(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
343 let event = arg(a, 0);
344 let event_type = obj_prop(&event, "type").map(|v| v.to_js_string()).unwrap_or_default();
345 let key = match event_type.as_str() {
346 "scroll" => "_scroll_listeners",
347 "popstate" => "_popstate_listeners",
348 "hashchange" => "_hashchange_listeners",
349 "resize" => "_resize_listeners",
350 "online" => "_online_listeners",
351 "offline" => "_offline_listeners",
352 "beforeunload" => "_beforeunload_listeners",
353 "unload" => "_unload_listeners",
354 "pageshow" => "_pageshow_listeners",
355 "pagehide" => "_pagehide_listeners",
356 "visibilitychange" => "_visibilitychange_listeners",
357 "languagechange" => "_languagechange_listeners",
358 "storage" => "_storage_listeners",
359 "beforeprint" => "_beforeprint_listeners",
360 "afterprint" => "_afterprint_listeners",
361 "message" => "_message_listeners",
362 "error" => "_error_listeners",
363 _ => return Ok(Value::Bool(true)),
364 };
365 let w = match it.global.borrow().vars.get("window") {
366 Some(Value::Object(w)) => w.clone(),
367 _ => return Ok(Value::Bool(true)),
368 };
369 let target = if matches!(this, Value::Object(_)) {
375 this.clone()
376 } else {
377 Value::Object(w.clone())
378 };
379 if let Value::Object(eo) = &event {
380 let mut e = eo.borrow_mut();
381 e.props.insert("target".into(), target.clone());
382 e.props.insert("srcElement".into(), target.clone());
383 e.props.insert("currentTarget".into(), target);
384 e.props.insert("eventPhase".into(), Value::Number(2.0));
386 }
387 let on_handler = w.borrow().props.get(&alloc::format!("on{event_type}")).cloned();
388 if let Some(f) = on_handler.and_then(callable_or_none) {
389 if event_type == "error" {
398 let (message, filename, lineno, colno, err) = if let Value::Object(eo) = &event {
399 let b = eo.borrow();
400 (
401 b.props.get("message").cloned().unwrap_or_else(|| Value::str("")),
402 b.props.get("filename").cloned().unwrap_or_else(|| Value::str("")),
403 b.props.get("lineno").cloned().unwrap_or(Value::Number(0.0)),
404 b.props.get("colno").cloned().unwrap_or(Value::Number(0.0)),
405 b.props.get("error").cloned().unwrap_or(Value::Null),
406 )
407 } else {
408 (Value::str(""), Value::str(""), Value::Number(0.0), Value::Number(0.0), Value::Null)
409 };
410 it.call_listener(
411 &f,
412 Value::Undefined,
413 &[message, filename, lineno, colno, err],
414 "window.onerror",
415 );
416 } else {
417 it.call_listener(
418 &f,
419 Value::Undefined,
420 core::slice::from_ref(&event),
421 "window.onerror",
422 );
423 }
424 }
425 let entries = match w.borrow().props.get(key) {
426 Some(Value::Object(arr)) => match &arr.borrow().kind {
427 ObjKind::Array(items) => items.clone(),
428 _ => alloc::vec::Vec::new(),
429 },
430 _ => alloc::vec::Vec::new(),
431 };
432 for entry in entries {
433 let (cb, signal) = match &entry {
434 Value::Object(o) => match &o.borrow().kind {
435 ObjKind::Array(items) => (items.first().cloned().unwrap_or(Value::Undefined), items.get(1).cloned()),
436 _ => (entry.clone(), None),
437 },
438 _ => (entry.clone(), None),
439 };
440 if signal.as_ref().is_some_and(super::super::dom_bridge::is_signal_aborted) {
441 continue;
442 }
443 it.call_listener(
444 &cb,
445 Value::Undefined,
446 core::slice::from_ref(&event),
447 "window listener",
448 );
449 }
450 if let Value::Object(eo) = &event {
453 let mut e = eo.borrow_mut();
454 e.props.insert("currentTarget".into(), Value::Null);
455 e.props.insert("eventPhase".into(), Value::Number(0.0));
456 }
457 let default_prevented = if let Value::Object(eo) = &event {
463 matches!(eo.borrow().props.get("defaultPrevented"), Some(Value::Bool(true)))
464 } else {
465 false
466 };
467 Ok(Value::Bool(!default_prevented))
468}
469
470fn is_window_shared_event_type(t: &str) -> bool {
474 matches!(
475 t,
476 "load"
477 | "DOMContentLoaded"
478 | "readystatechange"
479 | "scroll"
480 | "popstate"
481 | "hashchange"
482 | "resize"
483 | "online"
484 | "offline"
485 | "beforeunload"
486 | "unload"
487 | "pageshow"
488 | "pagehide"
489 | "visibilitychange"
490 | "languagechange"
491 | "storage"
492 | "beforeprint"
493 | "afterprint"
494 | "message"
495 | "error"
496 )
497}
498
499pub(crate) fn document_add_event_listener(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
511 let etype = arg(a, 0).to_js_string();
512 if is_window_shared_event_type(&etype) {
513 return window_add_event_listener(it, this, a);
514 }
515 event_target_add_event_listener(it, this, a)
516}
517pub(crate) fn document_remove_event_listener(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
520 let etype = arg(a, 0).to_js_string();
521 if is_window_shared_event_type(&etype) {
522 return window_remove_event_listener(it, this, a);
523 }
524 event_target_remove_event_listener(it, this, a)
525}
526pub(crate) fn document_dispatch_event(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
532 let event = arg(a, 0);
533 let etype = obj_prop(&event, "type").map(|v| v.to_js_string()).unwrap_or_default();
534 if is_window_shared_event_type(&etype) {
535 return window_dispatch_event(it, this, a);
536 }
537 event_target_dispatch_event(it, this, a)
538}
539
540pub fn dom_request_fullscreen(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
547 if let Some(idx) = this_dom_idx(&this) {
548 if let Some(Value::Object(doc)) = it.global.borrow().vars.get("document").cloned() {
549 doc.borrow_mut().props.insert("fullscreenElement".into(), this.clone());
550 }
551 it.dom.borrow_mut().set_attr(idx, "_fullscreen", "1");
552 }
553 Ok(resolved_promise(it, Value::Undefined))
554}
555
556pub(crate) fn document_exit_fullscreen(it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
558 if let Some(Value::Object(doc)) = it.global.borrow().vars.get("document").cloned() {
559 let prev = doc.borrow().props.get("fullscreenElement").cloned();
560 if let Some(prev_val) = prev {
561 if let Some(idx) = this_dom_idx(&prev_val) {
562 it.dom.borrow_mut().remove_attr(idx, "_fullscreen");
563 }
564 }
565 doc.borrow_mut().props.insert("fullscreenElement".into(), Value::Null);
566 }
567 Ok(resolved_promise(it, Value::Undefined))
568}
569
570pub fn dom_request_pointer_lock(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
575 if this_dom_idx(&this).is_some() {
576 if let Some(Value::Object(doc)) = it.global.borrow().vars.get("document").cloned() {
577 doc.borrow_mut().props.insert("pointerLockElement".into(), this.clone());
578 }
579 }
580 Ok(Value::Undefined)
581}
582pub(crate) fn document_exit_pointer_lock(it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
584 if let Some(Value::Object(doc)) = it.global.borrow().vars.get("document").cloned() {
585 doc.borrow_mut().props.insert("pointerLockElement".into(), Value::Null);
586 }
587 Ok(Value::Undefined)
588}
589
590pub fn dom_request_picture_in_picture(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
595 if this_dom_idx(&this).is_some() {
596 if let Some(Value::Object(doc)) = it.global.borrow().vars.get("document").cloned() {
597 doc.borrow_mut()
598 .props
599 .insert("pictureInPictureElement".into(), this.clone());
600 }
601 }
602 Ok(resolved_promise(it, Value::Object(Obj::plain())))
603}
604pub(crate) fn document_exit_picture_in_picture(it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
607 if let Some(Value::Object(doc)) = it.global.borrow().vars.get("document").cloned() {
608 doc.borrow_mut()
609 .props
610 .insert("pictureInPictureElement".into(), Value::Null);
611 }
612 Ok(resolved_promise(it, Value::Undefined))
613}
614
615pub(crate) fn document_create_tree_walker(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
620 let root = arg(a, 0);
621 let root_idx = this_dom_idx(&root).unwrap_or(0);
622 let tw = Obj::plain();
623 {
624 let mut b = tw.borrow_mut();
625 b.props.insert("root".into(), root.clone());
626 b.props.insert("currentNode".into(), root);
627 b.props.insert("_tw_root".into(), Value::Number(root_idx as f64));
628 b.props.insert(
629 "whatToShow".into(),
630 if a.len() > 1 { arg(a, 1) } else { Value::Number(0xFFFF_FFFFu32 as f64) },
631 );
632 b.props.insert("filter".into(), arg(a, 2));
633 b.props.insert("nextNode".into(), nv("nextNode", tree_walker_next_node));
634 b.props.insert("previousNode".into(), nv("previousNode", tree_walker_previous_node));
635 b.props.insert("parentNode".into(), nv("parentNode", tree_walker_parent_node));
636 b.props.insert("firstChild".into(), nv("firstChild", tree_walker_first_child));
637 b.props.insert("lastChild".into(), nv("lastChild", tree_walker_last_child));
638 b.props.insert("nextSibling".into(), nv("nextSibling", tree_walker_next_sibling));
639 b.props
640 .insert("previousSibling".into(), nv("previousSibling", tree_walker_previous_sibling));
641 }
642 Ok(Value::Object(tw))
643}
644pub(crate) fn tw_current_idx(this: &Value) -> Option<usize> {
645 let Value::Object(o) = this else { return None };
646 let cur = o.borrow().props.get("currentNode").cloned();
647 cur.and_then(|v| this_dom_idx(&v))
648}
649pub(crate) fn tw_root_idx(this: &Value) -> Option<usize> {
650 let Value::Object(o) = this else { return None };
651 o.borrow().props.get("_tw_root").map(|v| v.to_number() as usize)
652}
653pub(crate) fn tw_set_current(this: &Value, idx: usize) {
654 if let Value::Object(o) = this {
655 o.borrow_mut().props.insert("currentNode".into(), Value::Object(Obj::dom(idx)));
656 }
657}
658pub(crate) fn deepest_last_descendant(dom: &super::super::dom_bridge::DomBridge, mut idx: usize) -> usize {
660 while let Some(&c) = dom.nodes.get(idx).and_then(|n| n.children.last()) {
661 idx = c;
662 }
663 idx
664}
665pub(crate) fn tree_walker_next_node(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
668 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
669 let root = tw_root_idx(&this).unwrap_or(cur);
670 let dom = it.dom.borrow();
671 if let Some(&c) = dom.nodes.get(cur).and_then(|n| n.children.first()) {
672 drop(dom);
673 tw_set_current(&this, c);
674 return Ok(Value::Object(Obj::dom(c)));
675 }
676 let mut node = cur;
677 loop {
678 if node == root {
679 return Ok(Value::Null);
680 }
681 let Some(p) = dom.nodes.get(node).and_then(|n| n.parent) else {
682 return Ok(Value::Null);
683 };
684 let kids = dom.nodes.get(p).map(|n| n.children.clone()).unwrap_or_default();
685 if let Some(pos) = kids.iter().position(|&c| c == node) {
686 if let Some(&next) = kids.get(pos + 1) {
687 drop(dom);
688 tw_set_current(&this, next);
689 return Ok(Value::Object(Obj::dom(next)));
690 }
691 }
692 if p == root {
693 return Ok(Value::Null);
694 }
695 node = p;
696 }
697}
698pub(crate) fn tree_walker_previous_node(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
700 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
701 let root = tw_root_idx(&this).unwrap_or(cur);
702 if cur == root {
703 return Ok(Value::Null);
704 }
705 let dom = it.dom.borrow();
706 let Some(p) = dom.nodes.get(cur).and_then(|n| n.parent) else {
707 return Ok(Value::Null);
708 };
709 let kids = dom.nodes.get(p).map(|n| n.children.clone()).unwrap_or_default();
710 let target = match kids.iter().position(|&c| c == cur) {
711 Some(pos) if pos > 0 => Some(deepest_last_descendant(&dom, kids[pos - 1])),
712 Some(_) => Some(p),
713 None => None,
714 };
715 drop(dom);
716 match target {
717 Some(t) => {
718 tw_set_current(&this, t);
719 Ok(Value::Object(Obj::dom(t)))
720 }
721 None => Ok(Value::Null),
722 }
723}
724pub(crate) fn tree_walker_parent_node(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
726 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
727 let root = tw_root_idx(&this).unwrap_or(cur);
728 if cur == root {
729 return Ok(Value::Null);
730 }
731 let parent = it.dom.borrow().nodes.get(cur).and_then(|n| n.parent);
732 match parent {
733 Some(p) => {
734 tw_set_current(&this, p);
735 Ok(Value::Object(Obj::dom(p)))
736 }
737 None => Ok(Value::Null),
738 }
739}
740pub(crate) fn tree_walker_first_child(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
742 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
743 let first = it.dom.borrow().nodes.get(cur).and_then(|n| n.children.first().copied());
744 match first {
745 Some(c) => {
746 tw_set_current(&this, c);
747 Ok(Value::Object(Obj::dom(c)))
748 }
749 None => Ok(Value::Null),
750 }
751}
752pub(crate) fn tree_walker_last_child(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
754 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
755 let last = it.dom.borrow().nodes.get(cur).and_then(|n| n.children.last().copied());
756 match last {
757 Some(c) => {
758 tw_set_current(&this, c);
759 Ok(Value::Object(Obj::dom(c)))
760 }
761 None => Ok(Value::Null),
762 }
763}
764pub(crate) fn tree_walker_next_sibling(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
766 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
767 let root = tw_root_idx(&this).unwrap_or(cur);
768 if cur == root {
769 return Ok(Value::Null);
770 }
771 let dom = it.dom.borrow();
772 let Some(p) = dom.nodes.get(cur).and_then(|n| n.parent) else {
773 return Ok(Value::Null);
774 };
775 let kids = dom.nodes.get(p).map(|n| n.children.clone()).unwrap_or_default();
776 let target = kids.iter().position(|&c| c == cur).and_then(|pos| kids.get(pos + 1).copied());
777 drop(dom);
778 match target {
779 Some(t) => {
780 tw_set_current(&this, t);
781 Ok(Value::Object(Obj::dom(t)))
782 }
783 None => Ok(Value::Null),
784 }
785}
786pub(crate) fn tree_walker_previous_sibling(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
788 let Some(cur) = tw_current_idx(&this) else { return Ok(Value::Null) };
789 let root = tw_root_idx(&this).unwrap_or(cur);
790 if cur == root {
791 return Ok(Value::Null);
792 }
793 let dom = it.dom.borrow();
794 let Some(p) = dom.nodes.get(cur).and_then(|n| n.parent) else {
795 return Ok(Value::Null);
796 };
797 let kids = dom.nodes.get(p).map(|n| n.children.clone()).unwrap_or_default();
798 let target = kids
799 .iter()
800 .position(|&c| c == cur)
801 .filter(|&pos| pos > 0)
802 .and_then(|pos| kids.get(pos - 1).copied());
803 drop(dom);
804 match target {
805 Some(t) => {
806 tw_set_current(&this, t);
807 Ok(Value::Object(Obj::dom(t)))
808 }
809 None => Ok(Value::Null),
810 }
811}
812
813pub(crate) fn document_create_node_iterator(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
815 let root = arg(a, 0);
816 let root_idx = this_dom_idx(&root).unwrap_or(0);
817 let ni = Obj::plain();
818 {
819 let mut b = ni.borrow_mut();
820 b.props.insert("root".into(), root.clone());
821 b.props.insert("referenceNode".into(), root);
822 b.props.insert("pointerBeforeReferenceNode".into(), Value::Bool(true));
823 b.props.insert("_ni_root".into(), Value::Number(root_idx as f64));
824 b.props.insert(
825 "whatToShow".into(),
826 if a.len() > 1 { arg(a, 1) } else { Value::Number(0xFFFF_FFFFu32 as f64) },
827 );
828 b.props.insert("filter".into(), arg(a, 2));
829 b.props
830 .insert("nextNode".into(), nv("nextNode", node_iterator_next_node));
831 b.props.insert(
832 "previousNode".into(),
833 nv("previousNode", node_iterator_previous_node),
834 );
835 b.props.insert("detach".into(), nv("detach", dom_noop));
836 }
837 Ok(Value::Object(ni))
838}
839pub(crate) fn ni_ref_idx(this: &Value) -> Option<usize> {
840 let Value::Object(o) = this else { return None };
841 let r = o.borrow().props.get("referenceNode").cloned();
842 r.and_then(|v| this_dom_idx(&v))
843}
844pub(crate) fn ni_root_idx(this: &Value) -> Option<usize> {
845 let Value::Object(o) = this else { return None };
846 o.borrow()
847 .props
848 .get("_ni_root")
849 .map(|v| v.to_number() as usize)
850}
851pub(crate) fn ni_pointer_before(this: &Value) -> bool {
852 let Value::Object(o) = this else { return false };
853 matches!(
854 o.borrow().props.get("pointerBeforeReferenceNode"),
855 Some(Value::Bool(true))
856 )
857}
858pub(crate) fn ni_set_state(this: &Value, idx: usize, before: bool) {
859 if let Value::Object(o) = this {
860 let mut b = o.borrow_mut();
861 b.props
862 .insert("referenceNode".into(), Value::Object(Obj::dom(idx)));
863 b.props
864 .insert("pointerBeforeReferenceNode".into(), Value::Bool(before));
865 }
866}
867pub(crate) fn node_iterator_step_next(
869 dom: &super::super::dom_bridge::DomBridge,
870 root: usize,
871 idx: usize,
872) -> Option<usize> {
873 if let Some(&c) = dom.nodes.get(idx).and_then(|n| n.children.first()) {
874 return Some(c);
875 }
876 let mut node = idx;
877 loop {
878 if node == root {
879 return None;
880 }
881 let p = dom.nodes.get(node).and_then(|n| n.parent)?;
882 let kids = dom
883 .nodes
884 .get(p)
885 .map(|n| n.children.clone())
886 .unwrap_or_default();
887 if let Some(pos) = kids.iter().position(|&c| c == node) {
888 if let Some(&next) = kids.get(pos + 1) {
889 return Some(next);
890 }
891 }
892 if p == root {
893 return None;
894 }
895 node = p;
896 }
897}
898pub(crate) fn node_iterator_next_node(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
900 let Some(cur) = ni_ref_idx(&this) else {
901 return Ok(Value::Null);
902 };
903 let root = ni_root_idx(&this).unwrap_or(cur);
904 if ni_pointer_before(&this) {
905 ni_set_state(&this, cur, false);
906 return Ok(Value::Object(Obj::dom(cur)));
907 }
908 let dom = it.dom.borrow();
909 let next = node_iterator_step_next(&dom, root, cur);
910 drop(dom);
911 match next {
912 Some(n) => {
913 ni_set_state(&this, n, false);
914 Ok(Value::Object(Obj::dom(n)))
915 }
916 None => Ok(Value::Null),
917 }
918}
919pub(crate) fn node_iterator_previous_node(
921 it: &mut Interp,
922 this: Value,
923 _a: &[Value],
924) -> Result<Value, Value> {
925 let Some(cur) = ni_ref_idx(&this) else {
926 return Ok(Value::Null);
927 };
928 let root = ni_root_idx(&this).unwrap_or(cur);
929 if !ni_pointer_before(&this) {
930 ni_set_state(&this, cur, true);
931 return Ok(Value::Object(Obj::dom(cur)));
932 }
933 if cur == root {
934 return Ok(Value::Null);
935 }
936 let dom = it.dom.borrow();
937 let Some(p) = dom.nodes.get(cur).and_then(|n| n.parent) else {
938 return Ok(Value::Null);
939 };
940 let kids = dom
941 .nodes
942 .get(p)
943 .map(|n| n.children.clone())
944 .unwrap_or_default();
945 let target = match kids.iter().position(|&c| c == cur) {
946 Some(pos) if pos > 0 => Some(deepest_last_descendant(&dom, kids[pos - 1])),
947 Some(_) => Some(p),
948 None => None,
949 };
950 drop(dom);
951 match target {
952 Some(t) => {
953 ni_set_state(&this, t, true);
954 Ok(Value::Object(Obj::dom(t)))
955 }
956 None => Ok(Value::Null),
957 }
958}
959
960pub(crate) fn document_has_storage_access(it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
964 Ok(resolved_promise(it, Value::Bool(true)))
965}
966pub(crate) fn document_request_storage_access(it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
969 Ok(resolved_promise(it, Value::Undefined))
970}
971
972pub(crate) fn screen_orientation_lock(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
977 if let Value::Object(o) = &this {
978 o.borrow_mut().props.insert("type".into(), Value::str(arg(a, 0).to_js_string()));
979 }
980 Ok(resolved_promise(it, Value::Undefined))
981}
982pub(crate) fn screen_orientation_unlock(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
986 if let Value::Object(o) = &this {
987 let vw = super::super::super::layout::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
988 let vh = super::super::super::layout::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
989 let otype = if vw >= vh { "landscape-primary" } else { "portrait-primary" };
990 o.borrow_mut().props.insert("type".into(), Value::str(otype));
991 }
992 Ok(Value::Undefined)
993}
994
995pub(crate) fn window_post_message(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
998 let data = structured_clone(it, Value::Undefined, a)?;
999 let href = location_href(&it.global).unwrap_or_default();
1000 let origin = location_components(&href)
1001 .into_iter()
1002 .find(|(k, _)| *k == "origin")
1003 .map(|(_, v)| v)
1004 .unwrap_or_default();
1005 let ev = Obj::plain();
1006 ev.borrow_mut().props.insert("type".into(), Value::str("message"));
1007 ev.borrow_mut().props.insert("data".into(), data);
1008 ev.borrow_mut().props.insert("origin".into(), Value::str(origin));
1009 ev.borrow_mut().props.insert("source".into(), Value::Null);
1010 let listeners = match it.global.borrow().vars.get("window") {
1011 Some(Value::Object(w)) => w.borrow().props.get("_message_listeners").cloned(),
1012 _ => None,
1013 };
1014 if let Some(Value::Object(arr)) = listeners {
1015 let entries: Vec<Value> = match &arr.borrow().kind {
1016 ObjKind::Array(items) => items.clone(),
1017 _ => Vec::new(),
1018 };
1019 for entry in entries {
1020 if window_listener_aborted(&entry) {
1021 continue;
1022 }
1023 let cb = event_target_listener_cb(&entry);
1024 it.call_listener(
1025 &cb,
1026 Value::Undefined,
1027 &[Value::Object(ev.clone())],
1028 "window listener",
1029 );
1030 }
1031 }
1032 Ok(Value::Undefined)
1033}
1034
1035pub(crate) fn report_error(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1042 let err = arg(a, 0);
1043 let message = if let Value::Object(o) = &err {
1044 o.borrow()
1045 .props
1046 .get("message")
1047 .map(|v| v.to_js_string())
1048 .unwrap_or_else(|| err.to_js_string())
1049 } else {
1050 err.to_js_string()
1051 };
1052 let _ = console_log(it, Value::Undefined, &[Value::str(&message)]);
1053 let ev = Obj::plain();
1054 {
1055 let mut e = ev.borrow_mut();
1056 e.props.insert("type".into(), Value::str("error"));
1057 e.props.insert("message".into(), Value::str(&message));
1058 e.props.insert("error".into(), err.clone());
1059 e.props.insert("filename".into(), Value::str(""));
1060 e.props.insert("lineno".into(), Value::Number(0.0));
1061 e.props.insert("colno".into(), Value::Number(0.0));
1062 }
1063 let window = match it.global.borrow().vars.get("window") {
1064 Some(Value::Object(w)) => Some(w.clone()),
1065 _ => None,
1066 };
1067 if let Some(w) = &window {
1068 let onerror = w.borrow().props.get("onerror").cloned();
1069 if let Some(f) = onerror.and_then(callable_or_none) {
1070 it.call_listener(
1071 &f,
1072 Value::Undefined,
1073 &[
1074 Value::str(&message),
1075 Value::str(""),
1076 Value::Number(0.0),
1077 Value::Number(0.0),
1078 err.clone(),
1079 ],
1080 "window.onerror",
1081 );
1082 }
1083 let listeners = w.borrow().props.get("_error_listeners").cloned();
1084 if let Some(Value::Object(arr)) = listeners {
1085 let entries: Vec<Value> = match &arr.borrow().kind {
1086 ObjKind::Array(items) => items.clone(),
1087 _ => Vec::new(),
1088 };
1089 for entry in entries {
1090 if window_listener_aborted(&entry) {
1091 continue;
1092 }
1093 let cb = event_target_listener_cb(&entry);
1094 it.call_listener(
1095 &cb,
1096 Value::Undefined,
1097 &[Value::Object(ev.clone())],
1098 "window listener",
1099 );
1100 }
1101 }
1102 }
1103 Ok(Value::Undefined)
1104}
1105
1106pub(crate) fn make_abort_error() -> Value {
1108 make_dom_exception("AbortError", "signal is aborted without reason")
1109}
1110pub(crate) fn dom_exception_ctor(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1113 let message = match arg(a, 0) {
1114 Value::Undefined => String::new(),
1115 v => v.to_js_string(),
1116 };
1117 let name = match arg(a, 1) {
1118 Value::Undefined => String::from("Error"),
1119 v => v.to_js_string(),
1120 };
1121 Ok(make_dom_exception(&name, &message))
1122}
1123pub(crate) fn dom_exception_legacy_code(name: &str) -> f64 {
1126 match name {
1127 "IndexSizeError" => 1.0,
1128 "HierarchyRequestError" => 3.0,
1129 "WrongDocumentError" => 4.0,
1130 "InvalidCharacterError" => 5.0,
1131 "NoModificationAllowedError" => 7.0,
1132 "NotFoundError" => 8.0,
1133 "NotSupportedError" => 9.0,
1134 "InUseAttributeError" => 10.0,
1135 "InvalidStateError" => 11.0,
1136 "SyntaxError" => 12.0,
1137 "InvalidModificationError" => 13.0,
1138 "NamespaceError" => 14.0,
1139 "InvalidAccessError" => 15.0,
1140 "SecurityError" => 18.0,
1141 "NetworkError" => 19.0,
1142 "AbortError" => 20.0,
1143 "TimeoutError" => 23.0,
1144 "DataCloneError" => 25.0,
1145 _ => 0.0,
1146 }
1147}
1148pub(crate) fn make_dom_exception(name: &str, message: &str) -> Value {
1152 let o = Obj::plain();
1153 {
1154 let mut b = o.borrow_mut();
1155 b.props.insert("name".into(), Value::str(name));
1156 b.props.insert("message".into(), Value::str(message));
1157 b.props
1158 .insert("code".into(), Value::Number(dom_exception_legacy_code(name)));
1159 b.props.insert("toString".into(), nv("toString", dom_exception_to_string));
1160 }
1161 Value::Object(o)
1162}
1163pub(crate) fn dom_exception_to_string(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1164 if let Value::Object(o) = &this {
1165 let b = o.borrow();
1166 let name = b.props.get("name").map(|v| v.to_js_string()).unwrap_or_default();
1167 let message = b.props.get("message").map(|v| v.to_js_string()).unwrap_or_default();
1168 return Ok(Value::str(if message.is_empty() {
1169 name
1170 } else {
1171 alloc::format!("{}: {}", name, message)
1172 }));
1173 }
1174 Ok(Value::str("DOMException"))
1175}
1176pub(crate) fn abort_controller_abort(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1182 if let Value::Object(o) = &this {
1183 let sig = o.borrow().props.get("signal").cloned();
1184 if let Some(Value::Object(s)) = sig {
1185 let already_aborted = matches!(s.borrow().props.get("aborted"), Some(Value::Bool(true)));
1186 if already_aborted {
1187 return Ok(Value::Undefined);
1188 }
1189 let reason = match arg(a, 0) {
1190 Value::Undefined => make_abort_error(),
1191 r => r,
1192 };
1193 {
1194 let mut b = s.borrow_mut();
1195 b.props.insert("aborted".into(), Value::Bool(true));
1196 b.props.insert("reason".into(), reason);
1197 }
1198 let cb = s.borrow().props.get("onabort").cloned();
1199 if let Some(f) = cb.and_then(callable_or_none) {
1200 it.call_listener(&f, Value::Object(s.clone()), &[], "AbortSignal onabort");
1201 }
1202 }
1203 }
1204 Ok(Value::Undefined)
1205}
1206pub(crate) fn abort_signal_add_event_listener(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1210 if let Value::Object(o) = &this {
1211 o.borrow_mut()
1212 .props
1213 .insert(format!("on{}", arg(a, 0).to_js_string()), arg(a, 1));
1214 }
1215 Ok(Value::Undefined)
1216}
1217pub(crate) fn abort_signal_remove_event_listener(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1224 if let Value::Object(o) = &this {
1225 let key = format!("on{}", arg(a, 0).to_js_string());
1226 let target = arg(a, 1);
1227 let matches = match (o.borrow().props.get(&key), &target) {
1228 (Some(Value::Object(stored)), Value::Object(t)) => Rc::ptr_eq(stored, t),
1229 _ => false,
1230 };
1231 if matches {
1232 o.borrow_mut().props.shift_remove(&key);
1233 }
1234 }
1235 Ok(Value::Undefined)
1236}
1237pub(crate) fn abort_controller_ctor(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
1238 let signal = Obj::plain();
1239 {
1240 let mut s = signal.borrow_mut();
1241 s.props.insert("aborted".into(), Value::Bool(false));
1242 s.props.insert("reason".into(), Value::Undefined);
1243 s.props.insert(
1244 "addEventListener".into(),
1245 nv("addEventListener", abort_signal_add_event_listener),
1246 );
1247 s.props.insert(
1248 "removeEventListener".into(),
1249 nv("removeEventListener", abort_signal_remove_event_listener),
1250 );
1251 s.props.insert(
1252 "throwIfAborted".into(),
1253 nv("throwIfAborted", abort_signal_throw_if_aborted),
1254 );
1255 }
1256 let ctrl = Obj::plain();
1257 {
1258 let mut c = ctrl.borrow_mut();
1259 c.props.insert("signal".into(), Value::Object(signal));
1260 c.props
1261 .insert("abort".into(), nv("abort", abort_controller_abort));
1262 }
1263 Ok(Value::Object(ctrl))
1264}
1265pub(crate) fn abort_signal_throw_if_aborted(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1271 if let Value::Object(o) = &this {
1272 let aborted = matches!(o.borrow().props.get("aborted"), Some(Value::Bool(true)));
1273 if aborted {
1274 let reason = o.borrow().props.get("reason").cloned().unwrap_or(Value::Undefined);
1275 return Err(reason);
1276 }
1277 }
1278 Ok(Value::Undefined)
1279}
1280pub(crate) fn abort_signal_abort_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1284 let signal = Obj::plain();
1285 {
1286 let mut s = signal.borrow_mut();
1287 s.props.insert("aborted".into(), Value::Bool(true));
1288 let reason = match arg(a, 0) {
1289 Value::Undefined => make_abort_error(),
1290 r => r,
1291 };
1292 s.props.insert("reason".into(), reason);
1293 s.props.insert(
1294 "addEventListener".into(),
1295 nv("addEventListener", abort_signal_add_event_listener),
1296 );
1297 s.props.insert(
1298 "removeEventListener".into(),
1299 nv("removeEventListener", abort_signal_remove_event_listener),
1300 );
1301 s.props.insert(
1302 "throwIfAborted".into(),
1303 nv("throwIfAborted", abort_signal_throw_if_aborted),
1304 );
1305 }
1306 Ok(Value::Object(signal))
1307}
1308pub(crate) fn abort_signal_timeout(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1322 let ms = arg(a, 0).to_number();
1323 let reason = make_dom_exception("TimeoutError", "signal timed out");
1324 it.global
1325 .borrow_mut()
1326 .vars
1327 .insert("__abort_signal_timeout_reason".into(), reason);
1328 let src = alloc::format!(
1329 "(function(){{ var c = new AbortController(); var r = __abort_signal_timeout_reason; \
1330 setTimeout(function(){{ c.abort(r); }}, {}); return c.signal; }})()",
1331 ms
1332 );
1333 let result = it.eval_source(&src);
1334 it.global.borrow_mut().vars.remove("__abort_signal_timeout_reason");
1335 result
1336}
1337pub(crate) fn abort_signal_any(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1346 let signals = it.iter_to_vec(&arg(a, 0));
1347 it.global
1348 .borrow_mut()
1349 .vars
1350 .insert("__abort_signal_any_input".into(), Value::Object(Obj::array(signals)));
1351 let src = "(function(){ \
1352 var c = new AbortController(); \
1353 var arr = __abort_signal_any_input; \
1354 for (var i = 0; i < arr.length; i++) { \
1355 if (arr[i].aborted) { c.abort(arr[i].reason); return c.signal; } \
1356 } \
1357 for (var i = 0; i < arr.length; i++) { \
1358 (function(s){ \
1359 s.addEventListener('abort', function(){ if (!c.signal.aborted) c.abort(s.reason); }); \
1360 })(arr[i]); \
1361 } \
1362 return c.signal; \
1363 })()";
1364 let result = it.eval_source(src);
1365 it.global.borrow_mut().vars.remove("__abort_signal_any_input");
1366 result
1367}
1368