1use super::*;
4
5pub fn dom_noop(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
9 Ok(Value::Undefined)
10}
11
12pub(crate) fn xml_serializer_ctor(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
15 let o = Obj::plain();
16 o.borrow_mut().props.insert(
17 "serializeToString".into(),
18 nv("serializeToString", xml_serializer_serialize_to_string),
19 );
20 Ok(Value::Object(o))
21}
22pub(crate) fn xml_serializer_serialize_to_string(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
27 let node = arg(a, 0);
28 match this_dom_idx(&node) {
29 Some(idx) => Ok(Value::str(it.dom.borrow().get_outer_html(idx))),
30 None => Ok(Value::str("")),
31 }
32}
33
34pub(crate) fn this_dom_idx(this: &Value) -> Option<usize> {
36 if let Value::Object(o) = this {
37 if let ObjKind::DomElement(i) = o.borrow().kind {
38 return Some(i);
39 }
40 }
41 None
42}
43
44pub fn dom_style_set_property(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
52 if let Some(idx) = this_host_idx(&this, "style:") {
53 let name = arg(a, 0).to_js_string();
54 let value = arg(a, 1).to_js_string();
55 let priority = arg(a, 2).to_js_string();
61 let stored = if !value.is_empty() && priority.eq_ignore_ascii_case("important") {
62 alloc::format!("{} !important", value)
63 } else {
64 value
65 };
66 it.dom.borrow_mut().set_style(idx, &name, &stored);
67 }
68 Ok(Value::Undefined)
69}
70pub fn dom_style_get_property_priority(
71 it: &mut Interp,
72 this: Value,
73 a: &[Value],
74) -> Result<Value, Value> {
75 if let Some(idx) = this_host_idx(&this, "style:") {
76 let name = arg(a, 0).to_js_string();
77 return Ok(Value::str(it.dom.borrow().get_style_priority(idx, &name)));
78 }
79 Ok(Value::str(""))
80}
81pub fn dom_style_get_property_value(
82 it: &mut Interp,
83 this: Value,
84 a: &[Value],
85) -> Result<Value, Value> {
86 if let Some(idx) = this_host_idx(&this, "style:") {
87 let name = arg(a, 0).to_js_string();
88 return Ok(Value::str(it.dom.borrow().get_style(idx, &name)));
89 }
90 Ok(Value::str(""))
91}
92pub fn dom_style_remove_property(
93 it: &mut Interp,
94 this: Value,
95 a: &[Value],
96) -> Result<Value, Value> {
97 if let Some(idx) = this_host_idx(&this, "style:") {
98 let name = arg(a, 0).to_js_string();
99 let prev = it.dom.borrow().get_style(idx, &name);
100 it.dom.borrow_mut().set_style(idx, &name, "");
101 return Ok(Value::str(prev));
102 }
103 Ok(Value::str(""))
104}
105pub fn dom_style_item(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
109 if let Some(idx) = this_host_idx(&this, "style:") {
110 let names: alloc::vec::Vec<String> = it
111 .dom
112 .borrow()
113 .get_css_text(idx)
114 .split(';')
115 .filter_map(|d| d.split_once(':').map(|(k, _)| k.trim().to_string()))
116 .filter(|k| !k.is_empty())
117 .collect();
118 let i = arg(a, 0).to_number();
119 if i.is_finite() && i >= 0.0 {
120 if let Some(name) = names.get(i as usize) {
121 return Ok(Value::str(name.clone()));
122 }
123 }
124 }
125 Ok(Value::str(""))
126}
127pub(crate) fn this_host_idx(this: &Value, prefix: &str) -> Option<usize> {
128 if let Value::Object(o) = this {
129 if let ObjKind::Host(t) = &o.borrow().kind {
130 if let Some(rest) = t.strip_prefix(prefix) {
131 return rest.parse().ok();
132 }
133 }
134 }
135 None
136}
137
138pub(crate) fn dom_handle(idx: Option<usize>) -> Value {
140 match idx {
141 Some(i) => Value::Object(Obj::dom(i)),
142 None => Value::Null,
143 }
144}
145
146pub(crate) fn document_get_element_by_id(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
147 let id = arg(a, 0).to_js_string();
148 let idx = it.dom.borrow().get_element_by_id(&id);
149 Ok(dom_handle(idx))
150}
151
152pub(crate) fn document_query_selector(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
153 let sel = arg(a, 0).to_js_string();
154 let idx = it.dom.borrow().query(&sel);
155 Ok(dom_handle(idx))
156}
157
158pub(crate) fn document_query_selector_all(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
159 let sel = arg(a, 0).to_js_string();
160 let indices = it.dom.borrow().query_all(&sel);
161 let items: Vec<Value> = indices
162 .into_iter()
163 .map(|i| Value::Object(Obj::dom(i)))
164 .collect();
165 Ok(Value::Object(Obj::array(items)))
166}
167pub(crate) fn dom_hit_test(it: &Interp, x: f64, y: f64) -> alloc::vec::Vec<(usize, i64)> {
170 let dom = it.dom.borrow();
171 let mut matches: alloc::vec::Vec<(usize, i64)> = alloc::vec::Vec::new();
172 for (&idx, &(l, t, w, h)) in dom.rects.iter() {
173 let (lf, tf, wf, hf) = (l as f64, t as f64, w as f64, h as f64);
174 if x >= lf && x < lf + wf && y >= tf && y < tf + hf {
175 matches.push((idx, (w as i64) * (h as i64)));
176 }
177 }
178 matches.sort_by_key(|&(_, area)| area);
179 matches
180}
181pub(crate) fn document_element_from_point(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
182 let x = arg(a, 0).to_number();
183 let y = arg(a, 1).to_number();
184 let matches = dom_hit_test(it, x, y);
185 Ok(match matches.first() {
186 Some(&(idx, _)) => Value::Object(Obj::dom(idx)),
187 None => Value::Null,
188 })
189}
190pub(crate) fn document_elements_from_point(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
191 let x = arg(a, 0).to_number();
192 let y = arg(a, 1).to_number();
193 let items: alloc::vec::Vec<Value> = dom_hit_test(it, x, y)
194 .into_iter()
195 .map(|(idx, _)| Value::Object(Obj::dom(idx)))
196 .collect();
197 Ok(Value::Object(Obj::array(items)))
198}
199
200pub fn dom_add_event_listener(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
201 if let Some(idx) = this_dom_idx(&this) {
202 let event = arg(a, 0).to_js_string();
203 let func = arg(a, 1);
204 let (capture, once) = parse_listener_opts(&arg(a, 2));
205 let signal = parse_listener_signal(&arg(a, 2));
206 if matches!(func, Value::Object(_)) {
207 it.dom
208 .borrow_mut()
209 .add_listener_opts_signal(idx, &event, func, capture, once, signal);
210 }
211 }
212 Ok(Value::Undefined)
213}
214
215pub(crate) fn parse_listener_opts(opt: &Value) -> (bool, bool) {
218 match opt {
219 Value::Bool(b) => (*b, false),
220 Value::Object(o) => {
221 let b = o.borrow();
222 let capture = b.props.get("capture").map(|v| v.truthy()).unwrap_or(false);
223 let once = b.props.get("once").map(|v| v.truthy()).unwrap_or(false);
224 (capture, once)
225 }
226 _ => (false, false),
227 }
228}
229pub(crate) fn parse_listener_signal(opt: &Value) -> Option<Value> {
233 if let Value::Object(o) = opt {
234 let s = o.borrow().props.get("signal").cloned();
235 if let Some(sig @ Value::Object(so)) = &s {
236 if so.borrow().props.contains_key("aborted") {
237 return Some(sig.clone());
238 }
239 }
240 }
241 None
242}
243
244pub fn dom_remove_event_listener(
246 it: &mut Interp,
247 this: Value,
248 a: &[Value],
249) -> Result<Value, Value> {
250 if let Some(idx) = this_dom_idx(&this) {
251 let event = arg(a, 0).to_js_string();
252 let func = arg(a, 1);
253 let (capture, _once) = parse_listener_opts(&arg(a, 2));
254 if matches!(func, Value::Object(_)) {
255 it.dom
256 .borrow_mut()
257 .remove_listener(idx, &event, &func, capture);
258 }
259 }
260 Ok(Value::Undefined)
261}
262
263pub fn dom_dispatch_event(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
266 let idx = match this_dom_idx(&this) {
267 Some(i) => i,
268 None => return Ok(Value::Bool(true)),
269 };
270 let ev = arg(a, 0);
271 let (etype, extra) = if let Value::Object(o) = &ev {
273 let b = o.borrow();
274 let t = b
275 .props
276 .get("type")
277 .map(|v| v.to_js_string())
278 .unwrap_or_default();
279 let mut extra: Vec<(String, Value)> = Vec::new();
285 for (k, v) in b.props.iter() {
286 if matches!(
287 k.as_str(),
288 "type"
289 | "target"
290 | "currentTarget"
291 | "eventPhase"
292 | "defaultPrevented"
293 | "preventDefault"
294 | "stopPropagation"
295 | "stopImmediatePropagation"
296 | "composedPath"
297 | "_composedPath"
298 | "_stop"
299 | "_stopImmediate"
300 ) {
301 continue;
302 }
303 extra.push((k.clone(), v.clone()));
304 }
305 (t, extra)
306 } else {
307 (ev.to_js_string(), Vec::new())
308 };
309 if etype.is_empty() {
310 return Ok(Value::Bool(true));
311 }
312 let (_fired, prevented) = it.dispatch_event_in_interp(idx, &etype, &extra);
313 Ok(Value::Bool(!prevented))
314}
315
316pub fn dom_has_child_nodes(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
319 if let Some(idx) = this_dom_idx(&this) {
320 let has_kids = it
321 .dom
322 .borrow()
323 .nodes
324 .get(idx)
325 .map(|n| !n.children.is_empty())
326 .unwrap_or(false);
327 return Ok(Value::Bool(has_kids));
328 }
329 Ok(Value::Bool(false))
330}
331pub fn dom_get_attribute(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
332 if let Some(idx) = this_dom_idx(&this) {
333 let name = arg(a, 0).to_js_string();
334 return Ok(match it.dom.borrow().get_attr(idx, &name) {
335 Some(v) => Value::str(v),
336 None => Value::Null,
337 });
338 }
339 Ok(Value::Null)
340}
341
342pub fn dom_has_attribute(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
343 if let Some(idx) = this_dom_idx(&this) {
344 let name = arg(a, 0).to_js_string();
345 return Ok(Value::Bool(it.dom.borrow().has_attr(idx, &name)));
346 }
347 Ok(Value::Bool(false))
348}
349
350pub(crate) fn make_attr_node(name: &str, value: &str, owner: &Value) -> Value {
353 let o = Obj::plain();
354 let mut b = o.borrow_mut();
355 b.props.insert("name".into(), Value::str(name));
356 b.props.insert("value".into(), Value::str(value));
357 b.props.insert("ownerElement".into(), owner.clone());
358 drop(b);
359 Value::Object(o)
360}
361pub fn document_create_attribute(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
366 let name = arg(a, 0).to_js_string().to_lowercase();
367 Ok(make_attr_node(&name, "", &Value::Null))
368}
369pub fn dom_get_attribute_node(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
374 if let Some(idx) = this_dom_idx(&this) {
375 let name = arg(a, 0).to_js_string();
376 if let Some(v) = it.dom.borrow().get_attr(idx, &name) {
377 return Ok(make_attr_node(&name, &v, &this));
378 }
379 }
380 Ok(Value::Null)
381}
382pub fn dom_set_attribute_node(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
386 if let Some(idx) = this_dom_idx(&this) {
387 let attr = arg(a, 0);
388 if matches!(attr, Value::Object(_)) {
389 let name = obj_prop(&attr, "name").map(|v| v.to_js_string()).unwrap_or_default();
390 let value = obj_prop(&attr, "value").map(|v| v.to_js_string()).unwrap_or_default();
391 let old = it.dom.borrow().get_attr(idx, &name);
392 it.dom.borrow_mut().set_attr(idx, &name, &value);
393 if let Some(old_v) = old {
394 return Ok(make_attr_node(&name, &old_v, &this));
395 }
396 }
397 }
398 Ok(Value::Null)
399}
400pub fn dom_get_attribute_node_ns(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
403 if let Some(idx) = this_dom_idx(&this) {
404 let name = if a.len() >= 2 {
405 arg(a, 1).to_js_string()
406 } else {
407 arg(a, 0).to_js_string()
408 };
409 if let Some(v) = it.dom.borrow().get_attr(idx, &name) {
410 return Ok(make_attr_node(&name, &v, &this));
411 }
412 }
413 Ok(Value::Null)
414}
415pub fn dom_remove_attribute_node(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
419 if let Some(idx) = this_dom_idx(&this) {
420 let name = obj_prop(&arg(a, 0), "name").map(|v| v.to_js_string()).unwrap_or_default();
421 let old = it.dom.borrow().get_attr(idx, &name);
427 if let Some(v) = old {
428 it.dom.borrow_mut().remove_attr(idx, &name);
429 return Ok(make_attr_node(&name, &v, &this));
430 }
431 }
432 Ok(Value::Null)
433}
434
435pub fn dom_remove_attribute(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
436 if let Some(idx) = this_dom_idx(&this) {
437 let name = arg(a, 0).to_js_string();
438 it.dom.borrow_mut().remove_attr(idx, &name);
439 }
440 Ok(Value::Undefined)
441}
442
443pub fn dom_get_attribute_ns(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
449 dom_get_attribute(it, this, &a[1.min(a.len())..])
450}
451pub fn dom_set_attribute_ns(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
452 dom_set_attribute(it, this, &a[1.min(a.len())..])
453}
454pub fn dom_has_attribute_ns(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
455 dom_has_attribute(it, this, &a[1.min(a.len())..])
456}
457pub fn dom_remove_attribute_ns(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
458 dom_remove_attribute(it, this, &a[1.min(a.len())..])
459}
460
461pub fn dom_get_attribute_names(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
462 if let Some(idx) = this_dom_idx(&this) {
463 let names: Vec<Value> = it
464 .dom
465 .borrow()
466 .attr_names(idx)
467 .into_iter()
468 .map(Value::str)
469 .collect();
470 return Ok(Value::Object(Obj::array(names)));
471 }
472 Ok(Value::Object(Obj::array(Vec::new())))
473}
474pub fn dom_has_attributes(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
477 if let Some(idx) = this_dom_idx(&this) {
478 return Ok(Value::Bool(!it.dom.borrow().attr_names(idx).is_empty()));
479 }
480 Ok(Value::Bool(false))
481}
482pub fn dom_normalize(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
485 if let Some(idx) = this_dom_idx(&this) {
486 it.dom.borrow_mut().normalize_node(idx);
487 }
488 Ok(Value::Undefined)
489}
490fn char_data_chars(it: &Interp, idx: usize) -> alloc::vec::Vec<char> {
497 it.dom.borrow().get_text_content(idx).chars().collect()
498}
499pub fn dom_char_data_append(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
500 if let Some(idx) = this_dom_idx(&this) {
501 let mut s = it.dom.borrow().get_text_content(idx);
502 s.push_str(&arg(a, 0).to_js_string());
503 it.dom.borrow_mut().set_text_content(idx, &s);
504 }
505 Ok(Value::Undefined)
506}
507fn char_data_check_offset(offset_arg: Value, len: usize) -> Result<usize, Value> {
515 let raw = offset_arg.to_number();
516 let offset = if raw.is_finite() { raw.max(0.0) as usize } else { 0 };
517 if offset > len {
518 return Err(make_dom_exception(
519 "IndexSizeError",
520 "The offset is greater than the length of the data",
521 ));
522 }
523 Ok(offset)
524}
525pub fn dom_char_data_delete(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
526 if let Some(idx) = this_dom_idx(&this) {
527 let chars = char_data_chars(it, idx);
528 let len = chars.len();
529 let offset = char_data_check_offset(arg(a, 0), len)?;
530 let count = (arg(a, 1).to_number().max(0.0) as usize).min(len - offset);
531 let out: String = chars[..offset].iter().chain(chars[offset + count..].iter()).collect();
532 it.dom.borrow_mut().set_text_content(idx, &out);
533 }
534 Ok(Value::Undefined)
535}
536pub fn dom_char_data_insert(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
537 if let Some(idx) = this_dom_idx(&this) {
538 let chars = char_data_chars(it, idx);
539 let len = chars.len();
540 let offset = char_data_check_offset(arg(a, 0), len)?;
541 let insert = arg(a, 1).to_js_string();
542 let out: String = chars[..offset]
543 .iter()
544 .collect::<String>()
545 + &insert
546 + &chars[offset..].iter().collect::<String>();
547 it.dom.borrow_mut().set_text_content(idx, &out);
548 }
549 Ok(Value::Undefined)
550}
551pub fn dom_char_data_replace(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
552 if let Some(idx) = this_dom_idx(&this) {
553 let chars = char_data_chars(it, idx);
554 let len = chars.len();
555 let offset = char_data_check_offset(arg(a, 0), len)?;
556 let count = (arg(a, 1).to_number().max(0.0) as usize).min(len - offset);
557 let replacement = arg(a, 2).to_js_string();
558 let out: String = chars[..offset]
559 .iter()
560 .collect::<String>()
561 + &replacement
562 + &chars[offset + count..].iter().collect::<String>();
563 it.dom.borrow_mut().set_text_content(idx, &out);
564 }
565 Ok(Value::Undefined)
566}
567pub fn dom_char_data_substring(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
568 if let Some(idx) = this_dom_idx(&this) {
569 let chars = char_data_chars(it, idx);
570 let len = chars.len();
571 let offset = char_data_check_offset(arg(a, 0), len)?;
572 let count = (arg(a, 1).to_number().max(0.0) as usize).min(len - offset);
573 return Ok(Value::str(chars[offset..offset + count].iter().collect::<String>()));
574 }
575 Ok(Value::str(""))
576}
577pub fn dom_split_text(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
582 let Some(idx) = this_dom_idx(&this) else {
583 return Ok(Value::Undefined);
584 };
585 let chars = char_data_chars(it, idx);
586 let len = chars.len();
587 let offset = char_data_check_offset(arg(a, 0), len)?;
590 let before: String = chars[..offset].iter().collect();
591 let after: String = chars[offset..].iter().collect();
592 it.dom.borrow_mut().set_text_content(idx, &before);
593 let new_idx = it.dom.borrow_mut().create_text_node(&after);
594 let (parent, next_sibling) = {
595 let dom = it.dom.borrow();
596 match dom.nodes.get(idx).and_then(|n| n.parent) {
597 Some(p) => {
598 let pos = dom.nodes[p].children.iter().position(|&c| c == idx);
599 let next = pos.and_then(|i| dom.nodes[p].children.get(i + 1)).copied();
600 (Some(p), next)
601 }
602 None => (None, None),
603 }
604 };
605 if let Some(p) = parent {
606 it.dom.borrow_mut().insert_before(p, new_idx, next_sibling);
607 }
608 Ok(Value::Object(Obj::dom(new_idx)))
609}
610
611pub fn dom_table_insert_row(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
618 let Some(table_idx) = this_dom_idx(&this) else {
619 return Ok(Value::Undefined);
620 };
621 let rows = it.dom.borrow().descendants_by_tags(table_idx, &["tr"]);
622 let len = rows.len();
623 let pos = arg(a, 0).to_number();
624 let pos = if pos.is_nan() { -1 } else { pos as i64 };
625 if pos < -1 || pos > len as i64 {
632 return Err(make_dom_exception(
633 "IndexSizeError",
634 "The index provided is outside the range of rows represented by this table",
635 ));
636 }
637 let (parent, ref_child) = if pos >= 0 && (pos as usize) < len {
638 let r = rows[pos as usize];
639 (it.dom.borrow().nodes.get(r).and_then(|n| n.parent).unwrap_or(table_idx), Some(r))
640 } else if let Some(&last) = rows.last() {
641 (it.dom.borrow().nodes.get(last).and_then(|n| n.parent).unwrap_or(table_idx), None)
642 } else {
643 (table_idx, None)
644 };
645 let new_row = it.dom.borrow_mut().create_element("tr");
646 it.dom.borrow_mut().insert_before(parent, new_row, ref_child);
647 Ok(Value::Object(Obj::dom(new_row)))
648}
649pub fn dom_table_delete_row(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
650 let Some(table_idx) = this_dom_idx(&this) else {
651 return Ok(Value::Undefined);
652 };
653 let rows = it.dom.borrow().descendants_by_tags(table_idx, &["tr"]);
654 let pos = arg(a, 0).to_number();
655 let pos = if pos.is_nan() { -1 } else { pos as i64 };
656 if pos < -1 || pos >= rows.len() as i64 {
661 return Err(make_dom_exception(
662 "IndexSizeError",
663 "The index provided is outside the range of rows represented by this table",
664 ));
665 }
666 let target = if pos >= 0 {
667 Some(rows[pos as usize])
668 } else {
669 rows.last().copied()
670 };
671 if let Some(r) = target {
672 it.dom.borrow_mut().remove_node(r);
673 }
674 Ok(Value::Undefined)
675}
676pub fn dom_table_insert_cell(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
677 let Some(row_idx) = this_dom_idx(&this) else {
678 return Ok(Value::Undefined);
679 };
680 let cells = it.dom.borrow().descendants_by_tags(row_idx, &["td", "th"]);
681 let len = cells.len();
682 let pos = arg(a, 0).to_number();
683 let pos = if pos.is_nan() { -1 } else { pos as i64 };
684 if pos < -1 || pos > len as i64 {
687 return Err(make_dom_exception(
688 "IndexSizeError",
689 "The index provided is outside the range of cells represented by this row",
690 ));
691 }
692 let ref_child = if pos >= 0 && (pos as usize) < len { Some(cells[pos as usize]) } else { None };
693 let new_cell = it.dom.borrow_mut().create_element("td");
694 it.dom.borrow_mut().insert_before(row_idx, new_cell, ref_child);
695 Ok(Value::Object(Obj::dom(new_cell)))
696}
697pub fn dom_table_delete_cell(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
698 let Some(row_idx) = this_dom_idx(&this) else {
699 return Ok(Value::Undefined);
700 };
701 let cells = it.dom.borrow().descendants_by_tags(row_idx, &["td", "th"]);
702 let pos = arg(a, 0).to_number();
703 let pos = if pos.is_nan() { -1 } else { pos as i64 };
704 if pos < -1 || pos >= cells.len() as i64 {
707 return Err(make_dom_exception(
708 "IndexSizeError",
709 "The index provided is outside the range of cells represented by this row",
710 ));
711 }
712 let target = if pos >= 0 {
713 Some(cells[pos as usize])
714 } else {
715 cells.last().copied()
716 };
717 if let Some(c) = target {
718 it.dom.borrow_mut().remove_node(c);
719 }
720 Ok(Value::Undefined)
721}
722
723fn table_create_section(it: &mut Interp, table_idx: usize, tag: &str, append: bool) -> Value {
730 let existing = {
731 let dom = it.dom.borrow();
732 dom.nodes[table_idx].children.iter().find(|&&c| dom.nodes[c].tag == tag).copied()
733 };
734 if let Some(i) = existing {
735 return Value::Object(Obj::dom(i));
736 }
737 let new_idx = it.dom.borrow_mut().create_element(tag);
738 let ref_child = if append {
739 None
740 } else {
741 it.dom.borrow().nodes[table_idx].children.first().copied()
742 };
743 it.dom.borrow_mut().insert_before(table_idx, new_idx, ref_child);
744 Value::Object(Obj::dom(new_idx))
745}
746fn table_delete_section(it: &mut Interp, table_idx: usize, tag: &str) {
747 let existing = {
748 let dom = it.dom.borrow();
749 dom.nodes[table_idx].children.iter().find(|&&c| dom.nodes[c].tag == tag).copied()
750 };
751 if let Some(i) = existing {
752 it.dom.borrow_mut().remove_node(i);
753 }
754}
755pub fn dom_table_create_thead(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
756 Ok(match this_dom_idx(&this) {
757 Some(idx) => table_create_section(it, idx, "thead", false),
758 None => Value::Undefined,
759 })
760}
761pub fn dom_table_create_tfoot(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
762 Ok(match this_dom_idx(&this) {
763 Some(idx) => table_create_section(it, idx, "tfoot", true),
764 None => Value::Undefined,
765 })
766}
767pub fn dom_table_create_caption(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
768 Ok(match this_dom_idx(&this) {
769 Some(idx) => table_create_section(it, idx, "caption", false),
770 None => Value::Undefined,
771 })
772}
773pub fn dom_table_delete_thead(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
774 if let Some(idx) = this_dom_idx(&this) {
775 table_delete_section(it, idx, "thead");
776 }
777 Ok(Value::Undefined)
778}
779pub fn dom_table_delete_tfoot(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
780 if let Some(idx) = this_dom_idx(&this) {
781 table_delete_section(it, idx, "tfoot");
782 }
783 Ok(Value::Undefined)
784}
785pub fn dom_table_delete_caption(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
786 if let Some(idx) = this_dom_idx(&this) {
787 table_delete_section(it, idx, "caption");
788 }
789 Ok(Value::Undefined)
790}
791
792pub fn dom_img_decode(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
797 let has_src = this_dom_idx(&this)
798 .map(|idx| it.dom.borrow().get_attr(idx, "src").map(|s| !s.is_empty()).unwrap_or(false))
799 .unwrap_or(false);
800 if has_src {
801 Ok(resolved_promise(it, Value::Undefined))
802 } else {
803 let reason = make_dom_exception("EncodingError", "The source image cannot be decoded");
804 Ok(rejected_promise(it, reason))
805 }
806}
807
808fn current_field_value(it: &Interp, idx: usize) -> String {
812 let dom = it.dom.borrow();
813 let tag = dom.nodes.get(idx).map(|n| n.tag.as_str()).unwrap_or("");
814 if tag == "textarea" {
815 dom.get_attr(idx, "_live_value")
816 .or_else(|| dom.get_attr(idx, "value"))
817 .unwrap_or_else(|| dom.nodes.get(idx).map(|n| n.initial_text.clone()).unwrap_or_default())
818 } else {
819 dom.get_attr(idx, "_live_value")
820 .or_else(|| dom.get_attr(idx, "value"))
821 .unwrap_or_default()
822 }
823}
824pub fn dom_set_selection_range(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
829 if let Some(idx) = this_dom_idx(&this) {
830 let start = arg(a, 0).to_number();
831 let end = arg(a, 1).to_number();
832 let direction = if matches!(arg(a, 2), Value::Undefined) {
833 String::from("none")
834 } else {
835 arg(a, 2).to_js_string()
836 };
837 let mut dom = it.dom.borrow_mut();
838 dom.set_attr(idx, "_selection_start", &Value::Number(start).to_js_string());
839 dom.set_attr(idx, "_selection_end", &Value::Number(end).to_js_string());
840 dom.set_attr(idx, "_selection_direction", &direction);
841 }
842 Ok(Value::Undefined)
843}
844pub fn dom_set_range_text(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
851 if let Some(idx) = this_dom_idx(&this) {
852 let replacement = arg(a, 0).to_js_string();
853 let chars: alloc::vec::Vec<char> = current_field_value(it, idx).chars().collect();
854 let len = chars.len();
855 let cur_start = it
856 .dom
857 .borrow()
858 .get_attr(idx, "_selection_start")
859 .and_then(|v| v.parse::<f64>().ok())
860 .unwrap_or(0.0);
861 let cur_end = it
862 .dom
863 .borrow()
864 .get_attr(idx, "_selection_end")
865 .and_then(|v| v.parse::<f64>().ok())
866 .unwrap_or(0.0);
867 let start = if matches!(arg(a, 1), Value::Undefined) { cur_start } else { arg(a, 1).to_number() };
868 let end = if matches!(arg(a, 2), Value::Undefined) { cur_end } else { arg(a, 2).to_number() };
869 let start = (start.max(0.0) as usize).min(len);
870 let end = (end.max(0.0) as usize).min(len).max(start);
871 let select_mode = if matches!(arg(a, 3), Value::Undefined) { String::from("preserve") } else { arg(a, 3).to_js_string() };
872 let new_value: String =
873 chars[..start].iter().chain(replacement.chars().collect::<alloc::vec::Vec<_>>().iter()).chain(chars[end..].iter()).collect();
874 let new_end = start + replacement.chars().count();
875 let (sel_start, sel_end) = match select_mode.as_str() {
876 "select" => (start, new_end),
877 "start" => (start, start),
878 "end" => (new_end, new_end),
879 _ => (start, new_end), };
881 let mut dom = it.dom.borrow_mut();
882 dom.set_attr(idx, "_live_value", &new_value);
883 dom.set_attr(idx, "_selection_start", &Value::Number(sel_start as f64).to_js_string());
884 dom.set_attr(idx, "_selection_end", &Value::Number(sel_end as f64).to_js_string());
885 }
886 Ok(Value::Undefined)
887}
888pub fn dom_is_same_node(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
891 let self_idx = this_dom_idx(&this);
892 let other_idx = this_dom_idx(&arg(a, 0));
893 Ok(Value::Bool(self_idx.is_some() && self_idx == other_idx))
894}
895pub fn dom_is_equal_node(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
901 let self_idx = this_dom_idx(&this);
902 let other_idx = this_dom_idx(&arg(a, 0));
903 match (self_idx, other_idx) {
904 (Some(ai), Some(bi)) => {
905 let dom = it.dom.borrow();
906 Ok(Value::Bool(dom.get_outer_html(ai) == dom.get_outer_html(bi)))
907 }
908 _ => Ok(Value::Bool(false)),
909 }
910}
911
912pub fn dom_set_pointer_capture(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
918 if let Some(idx) = this_dom_idx(&this) {
919 it.dom.borrow_mut().set_pointer_capture(idx, arg(a, 0).to_number() as i32);
920 }
921 Ok(Value::Undefined)
922}
923pub fn dom_release_pointer_capture(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
925 if let Some(idx) = this_dom_idx(&this) {
926 it.dom
927 .borrow_mut()
928 .release_pointer_capture(idx, arg(a, 0).to_number() as i32);
929 }
930 Ok(Value::Undefined)
931}
932pub fn dom_has_pointer_capture(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
934 let has = this_dom_idx(&this)
935 .map(|idx| it.dom.borrow().has_pointer_capture(idx, arg(a, 0).to_number() as i32))
936 .unwrap_or(false);
937 Ok(Value::Bool(has))
938}
939
940pub(crate) fn dom_rect_ctor(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
951 let x = arg(a, 0).to_number();
952 let y = arg(a, 1).to_number();
953 let w = arg(a, 2).to_number();
954 let h = arg(a, 3).to_number();
955 let x = if x.is_nan() { 0.0 } else { x };
956 let y = if y.is_nan() { 0.0 } else { y };
957 let w = if w.is_nan() { 0.0 } else { w };
958 let h = if h.is_nan() { 0.0 } else { h };
959 let o = Obj::plain();
960 {
961 let mut b = o.borrow_mut();
962 b.props.insert("x".into(), Value::Number(x));
963 b.props.insert("left".into(), Value::Number(x));
964 b.props.insert("y".into(), Value::Number(y));
965 b.props.insert("top".into(), Value::Number(y));
966 b.props.insert("width".into(), Value::Number(w));
967 b.props.insert("height".into(), Value::Number(h));
968 b.props.insert("right".into(), Value::Number(x + w));
969 b.props.insert("bottom".into(), Value::Number(y + h));
970 b.props.insert(
971 "toJSON".into(),
972 nv("toJSON", |_: &mut Interp, this: Value, _: &[Value]| {
973 if let Value::Object(o) = &this {
974 let clone = Obj::plain();
975 for (k, v) in o.borrow().props.iter() {
976 if k != "toJSON" {
977 clone.borrow_mut().props.insert(k.clone(), v.clone());
978 }
979 }
980 return Ok(Value::Object(clone));
981 }
982 Ok(Value::Undefined)
983 }),
984 );
985 }
986 Ok(Value::Object(o))
987}
988fn get_num_prop(it: &mut Interp, val: &Value, name: &str, default: f64) -> f64 {
989 it.get_property(val, name).map(|v| v.to_number()).unwrap_or(default)
990}
991
992pub fn dom_point_matrix_transform(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
993 let px = get_num_prop(it, &this, "x", 0.0);
994 let py = get_num_prop(it, &this, "y", 0.0);
995 let pz = get_num_prop(it, &this, "z", 0.0);
996 let pw = get_num_prop(it, &this, "w", 1.0);
997 let m = arg(a, 0);
998
999 let def_a = get_num_prop(it, &m, "a", 1.0);
1000 let m11 = get_num_prop(it, &m, "m11", def_a);
1001 let def_b = get_num_prop(it, &m, "b", 0.0);
1002 let m12 = get_num_prop(it, &m, "m12", def_b);
1003 let m13 = get_num_prop(it, &m, "m13", 0.0);
1004 let m14 = get_num_prop(it, &m, "m14", 0.0);
1005
1006 let def_c = get_num_prop(it, &m, "c", 0.0);
1007 let m21 = get_num_prop(it, &m, "m21", def_c);
1008 let def_d = get_num_prop(it, &m, "d", 1.0);
1009 let m22 = get_num_prop(it, &m, "m22", def_d);
1010 let m23 = get_num_prop(it, &m, "m23", 0.0);
1011 let m24 = get_num_prop(it, &m, "m24", 0.0);
1012
1013 let m31 = get_num_prop(it, &m, "m31", 0.0);
1014 let m32 = get_num_prop(it, &m, "m32", 0.0);
1015 let m33 = get_num_prop(it, &m, "m33", 1.0);
1016 let m34 = get_num_prop(it, &m, "m34", 0.0);
1017
1018 let def_e = get_num_prop(it, &m, "e", 0.0);
1019 let m41 = get_num_prop(it, &m, "m41", def_e);
1020 let def_f = get_num_prop(it, &m, "f", 0.0);
1021 let m42 = get_num_prop(it, &m, "m42", def_f);
1022 let m43 = get_num_prop(it, &m, "m43", 0.0);
1023 let m44 = get_num_prop(it, &m, "m44", 1.0);
1024
1025 let nx = m11 * px + m21 * py + m31 * pz + m41 * pw;
1026 let ny = m12 * px + m22 * py + m32 * pz + m42 * pw;
1027 let nz = m13 * px + m23 * py + m33 * pz + m43 * pw;
1028 let nw = m14 * px + m24 * py + m34 * pz + m44 * pw;
1029 dom_point_ctor(it, Value::Undefined, &[Value::Number(nx), Value::Number(ny), Value::Number(nz), Value::Number(nw)])
1030}
1031
1032pub(crate) fn dom_point_ctor(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1036 let x = arg(a, 0).to_number();
1037 let y = arg(a, 1).to_number();
1038 let z = arg(a, 2).to_number();
1039 let w = arg(a, 3).to_number();
1040 let x = if x.is_nan() { 0.0 } else { x };
1041 let y = if y.is_nan() { 0.0 } else { y };
1042 let z = if z.is_nan() { 0.0 } else { z };
1043 let w = if matches!(arg(a, 3), Value::Undefined) || w.is_nan() { 1.0 } else { w };
1044 let o = Obj::plain();
1045 {
1046 let mut b = o.borrow_mut();
1047 b.props.insert("x".into(), Value::Number(x));
1048 b.props.insert("y".into(), Value::Number(y));
1049 b.props.insert("z".into(), Value::Number(z));
1050 b.props.insert("w".into(), Value::Number(w));
1051 b.props.insert(
1052 "matrixTransform".into(),
1053 nv("matrixTransform", dom_point_matrix_transform),
1054 );
1055 b.props.insert(
1056 "toJSON".into(),
1057 nv("toJSON", |_: &mut Interp, this: Value, _: &[Value]| {
1058 if let Value::Object(o) = &this {
1059 let clone = Obj::plain();
1060 for (k, v) in o.borrow().props.iter() {
1061 if k != "toJSON" && k != "matrixTransform" {
1062 clone.borrow_mut().props.insert(k.clone(), v.clone());
1063 }
1064 }
1065 return Ok(Value::Object(clone));
1066 }
1067 Ok(Value::Undefined)
1068 }),
1069 );
1070 }
1071 Ok(Value::Object(o))
1072}
1073
1074pub(crate) fn dom_rect_from_rect(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1075 let r = arg(a, 0);
1076 let x = get_num_prop(it, &r, "x", 0.0);
1077 let y = get_num_prop(it, &r, "y", 0.0);
1078 let w = get_num_prop(it, &r, "width", 0.0);
1079 let h = get_num_prop(it, &r, "height", 0.0);
1080 dom_rect_ctor(it, Value::Undefined, &[Value::Number(x), Value::Number(y), Value::Number(w), Value::Number(h)])
1081}
1082
1083pub(crate) fn dom_point_from_point(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1084 let p = arg(a, 0);
1085 let x = get_num_prop(it, &p, "x", 0.0);
1086 let y = get_num_prop(it, &p, "y", 0.0);
1087 let z = get_num_prop(it, &p, "z", 0.0);
1088 let w = get_num_prop(it, &p, "w", 1.0);
1089 dom_point_ctor(it, Value::Undefined, &[Value::Number(x), Value::Number(y), Value::Number(z), Value::Number(w)])
1090}
1091
1092pub fn dom_matrix_to_string(it: &mut Interp, this: Value, _: &[Value]) -> Result<Value, Value> {
1093 let is_2d = it.get_property(&this, "is2D").map(|v| v.truthy()).unwrap_or(true);
1094 let m11 = get_num_prop(it, &this, "m11", 1.0);
1095 let m12 = get_num_prop(it, &this, "m12", 0.0);
1096 let m13 = get_num_prop(it, &this, "m13", 0.0);
1097 let m14 = get_num_prop(it, &this, "m14", 0.0);
1098 let m21 = get_num_prop(it, &this, "m21", 0.0);
1099 let m22 = get_num_prop(it, &this, "m22", 1.0);
1100 let m23 = get_num_prop(it, &this, "m23", 0.0);
1101 let m24 = get_num_prop(it, &this, "m24", 0.0);
1102 let m31 = get_num_prop(it, &this, "m31", 0.0);
1103 let m32 = get_num_prop(it, &this, "m32", 0.0);
1104 let m33 = get_num_prop(it, &this, "m33", 1.0);
1105 let m34 = get_num_prop(it, &this, "m34", 0.0);
1106 let m41 = get_num_prop(it, &this, "m41", 0.0);
1107 let m42 = get_num_prop(it, &this, "m42", 0.0);
1108 let m43 = get_num_prop(it, &this, "m43", 0.0);
1109 let m44 = get_num_prop(it, &this, "m44", 1.0);
1110 if is_2d {
1111 Ok(Value::str(alloc::format!("matrix({}, {}, {}, {}, {}, {})", m11, m12, m21, m22, m41, m42)))
1112 } else {
1113 Ok(Value::str(alloc::format!(
1114 "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
1115 m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44
1116 )))
1117 }
1118}
1119
1120pub fn dom_matrix_multiply(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1121 let m11 = get_num_prop(it, &this, "m11", 1.0);
1122 let m12 = get_num_prop(it, &this, "m12", 0.0);
1123 let m21 = get_num_prop(it, &this, "m21", 0.0);
1124 let m22 = get_num_prop(it, &this, "m22", 1.0);
1125 let m41 = get_num_prop(it, &this, "m41", 0.0);
1126 let m42 = get_num_prop(it, &this, "m42", 0.0);
1127
1128 let other = arg(a, 0);
1129 let om11 = get_num_prop(it, &other, "m11", 1.0);
1130 let om12 = get_num_prop(it, &other, "m12", 0.0);
1131 let om21 = get_num_prop(it, &other, "m21", 0.0);
1132 let om22 = get_num_prop(it, &other, "m22", 1.0);
1133 let om41 = get_num_prop(it, &other, "m41", 0.0);
1134 let om42 = get_num_prop(it, &other, "m42", 0.0);
1135
1136 let res_a = m11 * om11 + m21 * om12;
1137 let res_b = m12 * om11 + m22 * om12;
1138 let res_c = m11 * om21 + m21 * om22;
1139 let res_d = m12 * om21 + m22 * om22;
1140 let res_e = m11 * om41 + m21 * om42 + m41;
1141 let res_f = m12 * om41 + m22 * om42 + m42;
1142
1143 let arr = Value::Object(Obj::array(alloc::vec![
1144 Value::Number(res_a), Value::Number(res_b),
1145 Value::Number(res_c), Value::Number(res_d),
1146 Value::Number(res_e), Value::Number(res_f)
1147 ]));
1148 dom_matrix_ctor(it, Value::Undefined, &[arr])
1149}
1150
1151pub fn dom_matrix_translate(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1152 let m11 = get_num_prop(it, &this, "m11", 1.0);
1153 let m12 = get_num_prop(it, &this, "m12", 0.0);
1154 let m21 = get_num_prop(it, &this, "m21", 0.0);
1155 let m22 = get_num_prop(it, &this, "m22", 1.0);
1156 let m41 = get_num_prop(it, &this, "m41", 0.0);
1157 let m42 = get_num_prop(it, &this, "m42", 0.0);
1158
1159 let tx = arg(a, 0).to_number();
1160 let ty = arg(a, 1).to_number();
1161 let tx = if tx.is_nan() { 0.0 } else { tx };
1162 let ty = if ty.is_nan() { 0.0 } else { ty };
1163
1164 let res_e = m11 * tx + m21 * ty + m41;
1165 let res_f = m12 * tx + m22 * ty + m42;
1166
1167 let arr = Value::Object(Obj::array(alloc::vec![
1168 Value::Number(m11), Value::Number(m12),
1169 Value::Number(m21), Value::Number(m22),
1170 Value::Number(res_e), Value::Number(res_f)
1171 ]));
1172 dom_matrix_ctor(it, Value::Undefined, &[arr])
1173}
1174
1175pub fn dom_matrix_scale(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1176 let m11 = get_num_prop(it, &this, "m11", 1.0);
1177 let m12 = get_num_prop(it, &this, "m12", 0.0);
1178 let m21 = get_num_prop(it, &this, "m21", 0.0);
1179 let m22 = get_num_prop(it, &this, "m22", 1.0);
1180 let m41 = get_num_prop(it, &this, "m41", 0.0);
1181 let m42 = get_num_prop(it, &this, "m42", 0.0);
1182
1183 let sx = arg(a, 0).to_number();
1184 let sy = arg(a, 1).to_number();
1185 let sx = if sx.is_nan() { 1.0 } else { sx };
1186 let sy = if matches!(arg(a, 1), Value::Undefined) || sy.is_nan() { sx } else { sy };
1187
1188 let arr = Value::Object(Obj::array(alloc::vec![
1189 Value::Number(m11 * sx), Value::Number(m12 * sx),
1190 Value::Number(m21 * sy), Value::Number(m22 * sy),
1191 Value::Number(m41), Value::Number(m42)
1192 ]));
1193 dom_matrix_ctor(it, Value::Undefined, &[arr])
1194}
1195
1196pub fn dom_matrix_rotate(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1197 let m11 = get_num_prop(it, &this, "m11", 1.0);
1198 let m12 = get_num_prop(it, &this, "m12", 0.0);
1199 let m21 = get_num_prop(it, &this, "m21", 0.0);
1200 let m22 = get_num_prop(it, &this, "m22", 1.0);
1201 let m41 = get_num_prop(it, &this, "m41", 0.0);
1202 let m42 = get_num_prop(it, &this, "m42", 0.0);
1203
1204 let angle_deg = arg(a, 0).to_number();
1205 let angle_deg = if angle_deg.is_nan() { 0.0 } else { angle_deg };
1206 let rad = angle_deg * core::f64::consts::PI / 180.0;
1207 let cos_a = libm::cos(rad);
1208 let sin_a = libm::sin(rad);
1209
1210 let res_a = m11 * cos_a + m21 * sin_a;
1211 let res_b = m12 * cos_a + m22 * sin_a;
1212 let res_c = m11 * (-sin_a) + m21 * cos_a;
1213 let res_d = m12 * (-sin_a) + m22 * cos_a;
1214
1215 let arr = Value::Object(Obj::array(alloc::vec![
1216 Value::Number(res_a), Value::Number(res_b),
1217 Value::Number(res_c), Value::Number(res_d),
1218 Value::Number(m41), Value::Number(m42)
1219 ]));
1220 dom_matrix_ctor(it, Value::Undefined, &[arr])
1221}
1222
1223pub fn dom_matrix_invert(it: &mut Interp, this: Value, _: &[Value]) -> Result<Value, Value> {
1224 let a = get_num_prop(it, &this, "m11", 1.0);
1225 let b = get_num_prop(it, &this, "m12", 0.0);
1226 let c = get_num_prop(it, &this, "m21", 0.0);
1227 let d = get_num_prop(it, &this, "m22", 1.0);
1228 let e = get_num_prop(it, &this, "m41", 0.0);
1229 let f = get_num_prop(it, &this, "m42", 0.0);
1230
1231 let det = a * d - b * c;
1232 if det == 0.0 || det.is_nan() {
1233 let arr = Value::Object(Obj::array(alloc::vec![
1234 Value::Number(f64::NAN), Value::Number(f64::NAN),
1235 Value::Number(f64::NAN), Value::Number(f64::NAN),
1236 Value::Number(f64::NAN), Value::Number(f64::NAN)
1237 ]));
1238 return dom_matrix_ctor(it, Value::Undefined, &[arr]);
1239 }
1240 let inv_a = d / det;
1241 let inv_b = -b / det;
1242 let inv_c = -c / det;
1243 let inv_d = a / det;
1244 let inv_e = (c * f - d * e) / det;
1245 let inv_f = (b * e - a * f) / det;
1246
1247 let arr = Value::Object(Obj::array(alloc::vec![
1248 Value::Number(inv_a), Value::Number(inv_b),
1249 Value::Number(inv_c), Value::Number(inv_d),
1250 Value::Number(inv_e), Value::Number(inv_f)
1251 ]));
1252 dom_matrix_ctor(it, Value::Undefined, &[arr])
1253}
1254
1255fn update_matrix_props(target: &Value, new_m: Value) {
1256 if let (Value::Object(to), Value::Object(from)) = (target, &new_m) {
1257 let from_props = from.borrow().props.clone();
1258 let mut b = to.borrow_mut();
1259 for (k, v) in from_props {
1260 if k != "toJSON" && k != "toString" && k != "multiply" && k != "translate" && k != "scale" && k != "rotate" && k != "invert"
1261 && k != "multiplySelf" && k != "translateSelf" && k != "scaleSelf" && k != "rotateSelf" && k != "invertSelf" {
1262 b.props.insert(k, v);
1263 }
1264 }
1265 }
1266}
1267
1268pub fn dom_matrix_translate_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1269 let new_m = dom_matrix_translate(it, this.clone(), a)?;
1270 update_matrix_props(&this, new_m);
1271 Ok(this)
1272}
1273
1274pub fn dom_matrix_scale_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1275 let new_m = dom_matrix_scale(it, this.clone(), a)?;
1276 update_matrix_props(&this, new_m);
1277 Ok(this)
1278}
1279
1280pub fn dom_matrix_rotate_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1281 let new_m = dom_matrix_rotate(it, this.clone(), a)?;
1282 update_matrix_props(&this, new_m);
1283 Ok(this)
1284}
1285
1286pub fn dom_matrix_multiply_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1287 let new_m = dom_matrix_multiply(it, this.clone(), a)?;
1288 update_matrix_props(&this, new_m);
1289 Ok(this)
1290}
1291
1292pub fn dom_matrix_invert_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1293 let new_m = dom_matrix_invert(it, this.clone(), a)?;
1294 update_matrix_props(&this, new_m);
1295 Ok(this)
1296}
1297
1298pub fn dom_matrix_skew_x(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1299 let deg = arg(a, 0).to_number();
1300 let rad = deg * core::f64::consts::PI / 180.0;
1301 let tan_val = libm::tan(rad);
1302 let skew_m = dom_matrix_ctor(
1303 it,
1304 Value::Undefined,
1305 &[
1306 Value::Number(1.0),
1307 Value::Number(0.0),
1308 Value::Number(tan_val),
1309 Value::Number(1.0),
1310 Value::Number(0.0),
1311 Value::Number(0.0),
1312 ],
1313 )?;
1314 dom_matrix_multiply(it, this, &[skew_m])
1315}
1316
1317pub fn dom_matrix_skew_y(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1318 let deg = arg(a, 0).to_number();
1319 let rad = deg * core::f64::consts::PI / 180.0;
1320 let tan_val = libm::tan(rad);
1321 let skew_m = dom_matrix_ctor(
1322 it,
1323 Value::Undefined,
1324 &[
1325 Value::Number(1.0),
1326 Value::Number(tan_val),
1327 Value::Number(0.0),
1328 Value::Number(1.0),
1329 Value::Number(0.0),
1330 Value::Number(0.0),
1331 ],
1332 )?;
1333 dom_matrix_multiply(it, this, &[skew_m])
1334}
1335
1336pub fn dom_matrix_skew_x_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1337 let new_m = dom_matrix_skew_x(it, this.clone(), a)?;
1338 update_matrix_props(&this, new_m);
1339 Ok(this)
1340}
1341
1342pub fn dom_matrix_skew_y_self(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1343 let new_m = dom_matrix_skew_y(it, this.clone(), a)?;
1344 update_matrix_props(&this, new_m);
1345 Ok(this)
1346}
1347
1348pub fn dom_matrix_transform_point(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1349 let pt = arg(a, 0);
1350 dom_point_matrix_transform(it, pt, &[this])
1351}
1352
1353pub(crate) fn dom_matrix_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1356 let mut m11 = 1.0; let mut m12 = 0.0; let mut m13 = 0.0; let mut m14 = 0.0;
1357 let mut m21 = 0.0; let mut m22 = 1.0; let mut m23 = 0.0; let mut m24 = 0.0;
1358 let mut m31 = 0.0; let mut m32 = 0.0; let mut m33 = 1.0; let mut m34 = 0.0;
1359 let mut m41 = 0.0; let mut m42 = 0.0; let mut m43 = 0.0; let mut m44 = 1.0;
1360
1361 let init = arg(a, 0);
1362 if let Value::Object(o) = &init {
1363 let vals_opt = if let crate::os_lib::js::value::ObjKind::Array(ref arr) = o.borrow().kind {
1380 Some(arr.clone())
1381 } else {
1382 None
1383 };
1384 if let Some(arr) = vals_opt {
1385 if arr.len() >= 16 {
1386 m11 = arr[0].to_number(); m12 = arr[1].to_number(); m13 = arr[2].to_number(); m14 = arr[3].to_number();
1387 m21 = arr[4].to_number(); m22 = arr[5].to_number(); m23 = arr[6].to_number(); m24 = arr[7].to_number();
1388 m31 = arr[8].to_number(); m32 = arr[9].to_number(); m33 = arr[10].to_number(); m34 = arr[11].to_number();
1389 m41 = arr[12].to_number(); m42 = arr[13].to_number(); m43 = arr[14].to_number(); m44 = arr[15].to_number();
1390 } else if arr.len() >= 6 {
1391 m11 = arr[0].to_number(); m12 = arr[1].to_number();
1392 m21 = arr[2].to_number(); m22 = arr[3].to_number();
1393 m41 = arr[4].to_number(); m42 = arr[5].to_number();
1394 }
1395 } else {
1396 if let Ok(v) = it.get_property(&init, "a") { if !matches!(v, Value::Undefined) { m11 = v.to_number(); } }
1397 if let Ok(v) = it.get_property(&init, "b") { if !matches!(v, Value::Undefined) { m12 = v.to_number(); } }
1398 if let Ok(v) = it.get_property(&init, "c") { if !matches!(v, Value::Undefined) { m21 = v.to_number(); } }
1399 if let Ok(v) = it.get_property(&init, "d") { if !matches!(v, Value::Undefined) { m22 = v.to_number(); } }
1400 if let Ok(v) = it.get_property(&init, "e") { if !matches!(v, Value::Undefined) { m41 = v.to_number(); } }
1401 if let Ok(v) = it.get_property(&init, "f") { if !matches!(v, Value::Undefined) { m42 = v.to_number(); } }
1402
1403 if let Ok(v) = it.get_property(&init, "m11") { if !matches!(v, Value::Undefined) { m11 = v.to_number(); } }
1404 if let Ok(v) = it.get_property(&init, "m12") { if !matches!(v, Value::Undefined) { m12 = v.to_number(); } }
1405 if let Ok(v) = it.get_property(&init, "m13") { if !matches!(v, Value::Undefined) { m13 = v.to_number(); } }
1406 if let Ok(v) = it.get_property(&init, "m14") { if !matches!(v, Value::Undefined) { m14 = v.to_number(); } }
1407 if let Ok(v) = it.get_property(&init, "m21") { if !matches!(v, Value::Undefined) { m21 = v.to_number(); } }
1408 if let Ok(v) = it.get_property(&init, "m22") { if !matches!(v, Value::Undefined) { m22 = v.to_number(); } }
1409 if let Ok(v) = it.get_property(&init, "m23") { if !matches!(v, Value::Undefined) { m23 = v.to_number(); } }
1410 if let Ok(v) = it.get_property(&init, "m24") { if !matches!(v, Value::Undefined) { m24 = v.to_number(); } }
1411 if let Ok(v) = it.get_property(&init, "m31") { if !matches!(v, Value::Undefined) { m31 = v.to_number(); } }
1412 if let Ok(v) = it.get_property(&init, "m32") { if !matches!(v, Value::Undefined) { m32 = v.to_number(); } }
1413 if let Ok(v) = it.get_property(&init, "m33") { if !matches!(v, Value::Undefined) { m33 = v.to_number(); } }
1414 if let Ok(v) = it.get_property(&init, "m34") { if !matches!(v, Value::Undefined) { m34 = v.to_number(); } }
1415 if let Ok(v) = it.get_property(&init, "m41") { if !matches!(v, Value::Undefined) { m41 = v.to_number(); } }
1416 if let Ok(v) = it.get_property(&init, "m42") { if !matches!(v, Value::Undefined) { m42 = v.to_number(); } }
1417 if let Ok(v) = it.get_property(&init, "m43") { if !matches!(v, Value::Undefined) { m43 = v.to_number(); } }
1418 if let Ok(v) = it.get_property(&init, "m44") { if !matches!(v, Value::Undefined) { m44 = v.to_number(); } }
1419 }
1420 }
1421
1422 let is_2d = m13 == 0.0 && m14 == 0.0 && m23 == 0.0 && m24 == 0.0 && m31 == 0.0 && m32 == 0.0 && m33 == 1.0 && m34 == 0.0 && m43 == 0.0;
1423 let is_identity = m11 == 1.0 && m12 == 0.0 && m13 == 0.0 && m14 == 0.0
1424 && m21 == 0.0 && m22 == 1.0 && m23 == 0.0 && m24 == 0.0
1425 && m31 == 0.0 && m32 == 0.0 && m33 == 1.0 && m34 == 0.0
1426 && m41 == 0.0 && m42 == 0.0 && m43 == 0.0 && m44 == 1.0;
1427
1428 let o = Obj::plain();
1429 {
1430 let mut b = o.borrow_mut();
1431 b.props.insert("a".into(), Value::Number(m11));
1432 b.props.insert("b".into(), Value::Number(m12));
1433 b.props.insert("c".into(), Value::Number(m21));
1434 b.props.insert("d".into(), Value::Number(m22));
1435 b.props.insert("e".into(), Value::Number(m41));
1436 b.props.insert("f".into(), Value::Number(m42));
1437
1438 b.props.insert("m11".into(), Value::Number(m11));
1439 b.props.insert("m12".into(), Value::Number(m12));
1440 b.props.insert("m13".into(), Value::Number(m13));
1441 b.props.insert("m14".into(), Value::Number(m14));
1442 b.props.insert("m21".into(), Value::Number(m21));
1443 b.props.insert("m22".into(), Value::Number(m22));
1444 b.props.insert("m23".into(), Value::Number(m23));
1445 b.props.insert("m24".into(), Value::Number(m24));
1446 b.props.insert("m31".into(), Value::Number(m31));
1447 b.props.insert("m32".into(), Value::Number(m32));
1448 b.props.insert("m33".into(), Value::Number(m33));
1449 b.props.insert("m34".into(), Value::Number(m34));
1450 b.props.insert("m41".into(), Value::Number(m41));
1451 b.props.insert("m42".into(), Value::Number(m42));
1452 b.props.insert("m43".into(), Value::Number(m43));
1453 b.props.insert("m44".into(), Value::Number(m44));
1454
1455 b.props.insert("is2D".into(), Value::Bool(is_2d));
1456 b.props.insert("isIdentity".into(), Value::Bool(is_identity));
1457
1458 b.props.insert("toString".into(), nv("toString", dom_matrix_to_string));
1459 b.props.insert("multiply".into(), nv("multiply", dom_matrix_multiply));
1460 b.props.insert("translate".into(), nv("translate", dom_matrix_translate));
1461 b.props.insert("scale".into(), nv("scale", dom_matrix_scale));
1462 b.props.insert("rotate".into(), nv("rotate", dom_matrix_rotate));
1463 b.props.insert("skewX".into(), nv("skewX", dom_matrix_skew_x));
1464 b.props.insert("skewY".into(), nv("skewY", dom_matrix_skew_y));
1465 b.props.insert("invert".into(), nv("invert", dom_matrix_invert));
1466
1467 b.props.insert("multiplySelf".into(), nv("multiplySelf", dom_matrix_multiply_self));
1468 b.props.insert("translateSelf".into(), nv("translateSelf", dom_matrix_translate_self));
1469 b.props.insert("scaleSelf".into(), nv("scaleSelf", dom_matrix_scale_self));
1470 b.props.insert("rotateSelf".into(), nv("rotateSelf", dom_matrix_rotate_self));
1471 b.props.insert("skewXSelf".into(), nv("skewXSelf", dom_matrix_skew_x_self));
1472 b.props.insert("skewYSelf".into(), nv("skewYSelf", dom_matrix_skew_y_self));
1473 b.props.insert("invertSelf".into(), nv("invertSelf", dom_matrix_invert_self));
1474
1475 b.props.insert("transformPoint".into(), nv("transformPoint", dom_matrix_transform_point));
1476 b.props.insert(
1477 "toJSON".into(),
1478 nv("toJSON", |_: &mut Interp, this: Value, _: &[Value]| {
1479 if let Value::Object(o) = &this {
1480 let clone = Obj::plain();
1481 for (k, v) in o.borrow().props.iter() {
1482 if k != "toJSON" && k != "toString" && k != "multiply" && k != "translate" && k != "scale" && k != "rotate" && k != "invert"
1483 && k != "multiplySelf" && k != "translateSelf" && k != "scaleSelf" && k != "rotateSelf" && k != "invertSelf"
1484 && k != "skewX" && k != "skewY" && k != "skewXSelf" && k != "skewYSelf" && k != "transformPoint" {
1485 clone.borrow_mut().props.insert(k.clone(), v.clone());
1486 }
1487 }
1488 return Ok(Value::Object(clone));
1489 }
1490 Ok(Value::Undefined)
1491 }),
1492 );
1493 }
1494 Ok(Value::Object(o))
1495}
1496
1497pub fn dom_quad_get_bounds(it: &mut Interp, this: Value, _: &[Value]) -> Result<Value, Value> {
1498 let p1 = it.get_property(&this, "p1").unwrap_or(Value::Undefined);
1499 let p2 = it.get_property(&this, "p2").unwrap_or(Value::Undefined);
1500 let p3 = it.get_property(&this, "p3").unwrap_or(Value::Undefined);
1501 let p4 = it.get_property(&this, "p4").unwrap_or(Value::Undefined);
1502
1503 let x1 = get_num_prop(it, &p1, "x", 0.0);
1504 let y1 = get_num_prop(it, &p1, "y", 0.0);
1505 let x2 = get_num_prop(it, &p2, "x", 0.0);
1506 let y2 = get_num_prop(it, &p2, "y", 0.0);
1507 let x3 = get_num_prop(it, &p3, "x", 0.0);
1508 let y3 = get_num_prop(it, &p3, "y", 0.0);
1509 let x4 = get_num_prop(it, &p4, "x", 0.0);
1510 let y4 = get_num_prop(it, &p4, "y", 0.0);
1511
1512 let min_x = x1.min(x2).min(x3).min(x4);
1513 let max_x = x1.max(x2).max(x3).max(x4);
1514 let min_y = y1.min(y2).min(y3).min(y4);
1515 let max_y = y1.max(y2).max(y3).max(y4);
1516
1517 let w = (max_x - min_x).max(0.0) as i32;
1518 let h = (max_y - min_y).max(0.0) as i32;
1519 Ok(Value::Object(make_dom_rect(min_x as i32, min_y as i32, w, h)))
1520}
1521
1522
1523
1524pub(crate) fn dom_quad_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1526 let p1 = if matches!(arg(a, 0), Value::Undefined) { dom_point_ctor(it, Value::Undefined, &[])? } else { arg(a, 0) };
1527 let p2 = if matches!(arg(a, 1), Value::Undefined) { dom_point_ctor(it, Value::Undefined, &[])? } else { arg(a, 1) };
1528 let p3 = if matches!(arg(a, 2), Value::Undefined) { dom_point_ctor(it, Value::Undefined, &[])? } else { arg(a, 2) };
1529 let p4 = if matches!(arg(a, 3), Value::Undefined) { dom_point_ctor(it, Value::Undefined, &[])? } else { arg(a, 3) };
1530
1531 let o = Obj::plain();
1532 {
1533 let mut b = o.borrow_mut();
1534 b.props.insert("p1".into(), p1.clone());
1535 b.props.insert("p2".into(), p2.clone());
1536 b.props.insert("p3".into(), p3.clone());
1537 b.props.insert("p4".into(), p4.clone());
1538
1539 b.props.insert("getBounds".into(), nv("getBounds", dom_quad_get_bounds));
1540 b.props.insert(
1541 "toJSON".into(),
1542 nv("toJSON", |_: &mut Interp, this: Value, _: &[Value]| {
1543 if let Value::Object(o) = &this {
1544 let clone = Obj::plain();
1545 for (k, v) in o.borrow().props.iter() {
1546 if k != "toJSON" && k != "getBounds" {
1547 clone.borrow_mut().props.insert(k.clone(), v.clone());
1548 }
1549 }
1550 return Ok(Value::Object(clone));
1551 }
1552 Ok(Value::Undefined)
1553 }),
1554 );
1555 }
1556 Ok(Value::Object(o))
1557}
1558pub(crate) fn make_dom_rect(x: i32, y: i32, w: i32, h: i32) -> ObjRef {
1559 let o = Obj::plain();
1560 {
1561 let mut b = o.borrow_mut();
1562 b.props.insert("x".into(), Value::Number(x as f64));
1563 b.props.insert("left".into(), Value::Number(x as f64));
1564 b.props.insert("y".into(), Value::Number(y as f64));
1565 b.props.insert("top".into(), Value::Number(y as f64));
1566 b.props.insert("width".into(), Value::Number(w as f64));
1567 b.props.insert("height".into(), Value::Number(h as f64));
1568 b.props
1569 .insert("right".into(), Value::Number((x + w) as f64));
1570 b.props
1571 .insert("bottom".into(), Value::Number((y + h) as f64));
1572 }
1573 o
1574}
1575
1576pub fn dom_get_bounding_client_rect(
1577 it: &mut Interp,
1578 this: Value,
1579 _a: &[Value],
1580) -> Result<Value, Value> {
1581 let (x, y, w, h) = this_dom_idx(&this)
1582 .and_then(|idx| it.dom.borrow().get_rect(idx))
1583 .unwrap_or((0, 0, 0, 0));
1584 Ok(Value::Object(make_dom_rect(x, y, w, h)))
1585}
1586
1587pub fn dom_get_client_rects(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1593 let rect = this_dom_idx(&this).and_then(|idx| it.dom.borrow().get_rect(idx));
1594 let items = match rect {
1595 Some((x, y, w, h)) => alloc::vec![Value::Object(make_dom_rect(x, y, w, h))],
1596 None => Vec::new(),
1597 };
1598 Ok(Value::Object(Obj::array(items)))
1599}
1600
1601pub(crate) fn get_computed_style(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1603 match this_dom_idx(&arg(a, 0)) {
1604 Some(idx) => Ok(Value::Object(Obj::host(&format!("computed:{}", idx)))),
1605 None => Ok(Value::Object(Obj::host("computed:18446744073709551615"))), }
1607}
1608
1609pub fn computed_get_property_value(
1611 it: &mut Interp,
1612 this: Value,
1613 a: &[Value],
1614) -> Result<Value, Value> {
1615 if let Some(idx) = this_host_idx(&this, "computed:") {
1616 let prop = arg(a, 0).to_js_string();
1617 return Ok(Value::str(it.dom.borrow().get_computed(idx, &prop)));
1618 }
1619 Ok(Value::str(""))
1620}
1621
1622pub fn dom_matches(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1623 if let Some(idx) = this_dom_idx(&this) {
1624 let sel = arg(a, 0).to_js_string();
1625 return Ok(Value::Bool(it.dom.borrow().node_matches_scoped(idx, &sel, Some(idx))));
1628 }
1629 Ok(Value::Bool(false))
1630}
1631
1632pub fn dom_closest(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1634 if let Some(start) = this_dom_idx(&this) {
1635 let sel = arg(a, 0).to_js_string();
1636 let dom = it.dom.borrow();
1637 let mut cur = Some(start);
1638 let mut guard = 0;
1639 while let Some(c) = cur {
1640 if dom.node_matches_scoped(c, &sel, Some(start)) {
1643 return Ok(dom_handle(Some(c)));
1644 }
1645 cur = dom.nodes.get(c).and_then(|n| n.parent);
1646 guard += 1;
1647 if guard > dom.nodes.len() {
1648 break;
1649 }
1650 }
1651 }
1652 Ok(Value::Null)
1653}
1654
1655pub fn dom_element_query_selector(
1658 it: &mut Interp,
1659 this: Value,
1660 a: &[Value],
1661) -> Result<Value, Value> {
1662 if let Some(idx) = this_dom_idx(&this) {
1663 let sel = arg(a, 0).to_js_string();
1664 let dom = it.dom.borrow();
1665 for c in dom.query_all_scoped(&sel, Some(idx)) {
1666 if dom.is_ancestor_of(idx, c) {
1667 return Ok(dom_handle(Some(c)));
1668 }
1669 }
1670 }
1671 Ok(Value::Null)
1672}
1673
1674pub fn dom_element_query_selector_all(
1677 it: &mut Interp,
1678 this: Value,
1679 a: &[Value],
1680) -> Result<Value, Value> {
1681 let mut items: Vec<Value> = Vec::new();
1682 if let Some(idx) = this_dom_idx(&this) {
1683 let sel = arg(a, 0).to_js_string();
1684 let dom = it.dom.borrow();
1685 for c in dom.query_all_scoped(&sel, Some(idx)) {
1686 if dom.is_ancestor_of(idx, c) {
1687 items.push(Value::Object(Obj::dom(c)));
1688 }
1689 }
1690 }
1691 Ok(Value::Object(Obj::array(items)))
1692}
1693
1694pub fn dom_element_get_elements_by_class_name(
1698 it: &mut Interp,
1699 this: Value,
1700 a: &[Value],
1701) -> Result<Value, Value> {
1702 let mut items: Vec<Value> = Vec::new();
1703 if let Some(idx) = this_dom_idx(&this) {
1704 let raw = arg(a, 0).to_js_string();
1705 let sel_classes: Vec<String> = raw
1706 .split_whitespace()
1707 .map(|c| alloc::format!(".{}", c))
1708 .collect();
1709 let sel = if sel_classes.is_empty() {
1710 alloc::format!(".__invalid_class__")
1711 } else {
1712 sel_classes.concat()
1713 };
1714 let dom = it.dom.borrow();
1715 for c in dom.query_all(&sel) {
1716 if dom.is_ancestor_of(idx, c) {
1717 items.push(Value::Object(Obj::dom(c)));
1718 }
1719 }
1720 }
1721 Ok(Value::Object(Obj::array(items)))
1722}
1723pub fn dom_element_get_elements_by_tag_name(
1725 it: &mut Interp,
1726 this: Value,
1727 a: &[Value],
1728) -> Result<Value, Value> {
1729 let mut items: Vec<Value> = Vec::new();
1730 if let Some(idx) = this_dom_idx(&this) {
1731 let tag = arg(a, 0).to_js_string();
1732 let dom = it.dom.borrow();
1733 for c in dom.descendants_by_tag_name(idx, &tag) {
1739 items.push(Value::Object(Obj::dom(c)));
1740 }
1741 }
1742 Ok(Value::Object(Obj::array(items)))
1743}
1744pub fn dom_element_get_elements_by_tag_name_ns(
1746 it: &mut Interp,
1747 this: Value,
1748 a: &[Value],
1749) -> Result<Value, Value> {
1750 let mut items: Vec<Value> = Vec::new();
1751 if let Some(idx) = this_dom_idx(&this) {
1752 let tag = if a.len() >= 2 {
1753 arg(a, 1).to_js_string()
1754 } else {
1755 arg(a, 0).to_js_string()
1756 };
1757 let dom = it.dom.borrow();
1758 for c in dom.query_all(&tag) {
1759 if dom.is_ancestor_of(idx, c) {
1760 items.push(Value::Object(Obj::dom(c)));
1761 }
1762 }
1763 }
1764 Ok(Value::Object(Obj::array(items)))
1765}
1766
1767pub fn dom_set_attribute(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1768 if let Some(idx) = this_dom_idx(&this) {
1769 let name = arg(a, 0).to_js_string();
1770 let val = arg(a, 1).to_js_string();
1771 it.dom.borrow_mut().set_attr(idx, &name, &val);
1772 }
1773 Ok(Value::Undefined)
1774}
1775
1776pub fn dom_check_validity(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1786 if let Some(idx) = this_dom_idx(&this) {
1787 let tag = it.dom.borrow().nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
1788 if tag == "form" {
1789 let dom = it.dom.borrow();
1790 let fields = dom.form_associated_controls(idx, &["input", "textarea", "select"]);
1796 let all_valid = fields.iter().all(|&node| {
1797 let value = dom.effective_form_value(node);
1798 dom.validate_field(node, &value).is_none()
1799 });
1800 return Ok(Value::Bool(all_valid));
1801 }
1802 let value = it.dom.borrow().effective_form_value(idx);
1803 let valid = it.dom.borrow().validate_field(idx, &value).is_none();
1804 return Ok(Value::Bool(valid));
1805 }
1806 Ok(Value::Bool(true))
1807}
1808
1809pub fn dom_set_custom_validity(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1812 if let Some(idx) = this_dom_idx(&this) {
1813 let msg = arg(a, 0).to_js_string();
1814 if msg.is_empty() {
1815 it.dom.borrow_mut().remove_attr(idx, "_custom_validity");
1816 } else {
1817 it.dom.borrow_mut().set_attr(idx, "_custom_validity", &msg);
1818 }
1819 }
1820 Ok(Value::Undefined)
1821}
1822
1823pub fn dom_classlist_for_each(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1829 if let Some(idx) = this_host_idx(&this, "classList:") {
1830 let classes = it.dom.borrow().nodes.get(idx).map(|n| n.classes.clone()).unwrap_or_default();
1831 let cb = arg(a, 0);
1832 for (i, c) in classes.into_iter().enumerate() {
1833 it.call_value(&cb, Value::Undefined, &[Value::str(c), Value::Number(i as f64)])?;
1834 }
1835 }
1836 Ok(Value::Undefined)
1837}
1838pub fn dom_classlist_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1839 if let Some(idx) = this_host_idx(&this, "classList:") {
1840 for v in a {
1841 it.dom
1842 .borrow_mut()
1843 .class_add(idx, &v.to_js_string());
1844 }
1845 }
1846 Ok(Value::Undefined)
1847}
1848
1849pub fn dom_classlist_remove(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1850 if let Some(idx) = this_host_idx(&this, "classList:") {
1851 for v in a {
1852 it.dom
1853 .borrow_mut()
1854 .class_remove(idx, &v.to_js_string());
1855 }
1856 }
1857 Ok(Value::Undefined)
1858}
1859
1860pub fn dom_classlist_supports(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
1862 Ok(Value::Bool(false))
1863}
1864
1865
1866pub fn dom_classlist_toggle(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1870 if let Some(idx) = this_host_idx(&this, "classList:") {
1871 let cls = arg(a, 0).to_js_string();
1872 if matches!(arg(a, 1), Value::Undefined) {
1873 let on = it.dom.borrow_mut().class_toggle(idx, &cls);
1874 return Ok(Value::Bool(on));
1875 }
1876 let force = arg(a, 1).truthy();
1877 if force {
1878 it.dom.borrow_mut().class_add(idx, &cls);
1879 } else {
1880 it.dom.borrow_mut().class_remove(idx, &cls);
1881 }
1882 return Ok(Value::Bool(force));
1883 }
1884 Ok(Value::Bool(false))
1885}
1886
1887pub fn dom_classlist_contains(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1888 if let Some(idx) = this_host_idx(&this, "classList:") {
1889 return Ok(Value::Bool(
1890 it.dom
1891 .borrow()
1892 .class_contains(idx, &arg(a, 0).to_js_string()),
1893 ));
1894 }
1895 Ok(Value::Bool(false))
1896}
1897
1898pub fn dom_classlist_entries(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1904 if let Some(idx) = this_host_idx(&this, "classList:") {
1905 let classes = it.dom.borrow().nodes.get(idx).map(|n| n.classes.clone()).unwrap_or_default();
1906 let out: Vec<Value> = classes
1907 .into_iter()
1908 .enumerate()
1909 .map(|(i, c)| Value::Object(Obj::array(alloc::vec![Value::Number(i as f64), Value::str(c)])))
1910 .collect();
1911 return Ok(Value::Object(make_iterator(out)));
1912 }
1913 Ok(Value::Object(make_iterator(alloc::vec![])))
1914}
1915pub fn dom_classlist_keys(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1916 if let Some(idx) = this_host_idx(&this, "classList:") {
1917 let n = it.dom.borrow().nodes.get(idx).map(|n| n.classes.len()).unwrap_or(0);
1918 return Ok(Value::Object(make_iterator(
1919 (0..n).map(|i| Value::Number(i as f64)).collect(),
1920 )));
1921 }
1922 Ok(Value::Object(make_iterator(alloc::vec![])))
1923}
1924pub fn dom_classlist_values(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1925 if let Some(idx) = this_host_idx(&this, "classList:") {
1926 let classes = it.dom.borrow().nodes.get(idx).map(|n| n.classes.clone()).unwrap_or_default();
1927 return Ok(Value::Object(make_iterator(
1928 classes.into_iter().map(Value::str).collect(),
1929 )));
1930 }
1931 Ok(Value::Object(make_iterator(alloc::vec![])))
1932}
1933
1934pub(crate) fn document_create_element(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1935 let tag = arg(a, 0).to_js_string();
1936 let idx = it.dom.borrow_mut().create_element(&tag);
1937 Ok(Value::Object(Obj::dom(idx)))
1938}
1939pub(crate) fn document_create_element_ns(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1946 document_create_element(it, t, &[arg(a, 1)])
1947}
1948
1949pub(crate) fn option_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1952 let idx = it.dom.borrow_mut().create_element("option");
1953 let text = arg(a, 0);
1954 if !matches!(text, Value::Undefined) {
1955 it.dom.borrow_mut().set_text_content(idx, &text.to_js_string());
1956 }
1957 let value = arg(a, 1);
1958 if !matches!(value, Value::Undefined) {
1959 it.dom.borrow_mut().set_attr(idx, "value", &value.to_js_string());
1960 }
1961 if arg(a, 2).truthy() || arg(a, 3).truthy() {
1962 it.dom.borrow_mut().set_attr(idx, "selected", "selected");
1963 }
1964 Ok(Value::Object(Obj::dom(idx)))
1965}
1966
1967pub(crate) fn document_create_text_node(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1968 let text = arg(a, 0).to_js_string();
1969 let idx = it.dom.borrow_mut().create_text_node(&text);
1970 Ok(Value::Object(Obj::dom(idx)))
1971}
1972
1973pub(crate) fn document_create_comment(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1974 let text = arg(a, 0).to_js_string();
1975 let idx = it.dom.borrow_mut().create_comment(&text);
1976 Ok(Value::Object(Obj::dom(idx)))
1977}
1978
1979pub(crate) fn document_create_document_fragment(it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
1980 let idx = it.dom.borrow_mut().create_document_fragment();
1981 Ok(Value::Object(Obj::dom(idx)))
1982}
1983
1984pub(crate) fn split_css_rules(css: &str) -> alloc::vec::Vec<String> {
1985 let mut rules = alloc::vec![];
1986 let mut current_rule = String::new();
1987 let mut depth = 0;
1988 for c in css.chars() {
1989 current_rule.push(c);
1990 if c == '{' {
1991 depth += 1;
1992 } else if c == '}' {
1993 if depth > 0 {
1994 depth -= 1;
1995 }
1996 if depth == 0 {
1997 let trimmed = current_rule.trim();
1998 if !trimmed.is_empty() {
1999 rules.push(trimmed.to_string());
2000 }
2001 current_rule.clear();
2002 }
2003 }
2004 }
2005 let trimmed = current_rule.trim();
2006 if !trimmed.is_empty() {
2007 rules.push(trimmed.to_string());
2008 }
2009 rules
2010}
2011
2012pub(crate) fn dom_stylesheet_insert_rule(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2013 let rule_text = arg(a, 0).to_js_string();
2014 let insert_index = arg(a, 1).to_number() as usize;
2015 if let Value::Object(o) = this {
2016 if let ObjKind::Host(t) = &o.borrow().kind {
2017 if let Some(rest) = t.strip_prefix("stylesheet:") {
2018 let idx: usize = rest.parse().unwrap_or(usize::MAX);
2019 let text = it.dom.borrow().get_text_content(idx);
2020 let mut rules = split_css_rules(&text);
2021 let actual_index = insert_index.min(rules.len());
2022 rules.insert(actual_index, rule_text);
2023 let new_css = rules.join("\n");
2024 it.dom.borrow_mut().set_text_content(idx, &new_css);
2025 return Ok(Value::Number(actual_index as f64));
2026 }
2027 }
2028 }
2029 Ok(Value::Number(0.0))
2030}
2031
2032pub(crate) fn dom_stylesheet_delete_rule(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2033 let delete_index = arg(a, 0).to_number() as usize;
2034 if let Value::Object(o) = this {
2035 if let ObjKind::Host(t) = &o.borrow().kind {
2036 if let Some(rest) = t.strip_prefix("stylesheet:") {
2037 let idx: usize = rest.parse().unwrap_or(usize::MAX);
2038 let text = it.dom.borrow().get_text_content(idx);
2039 let mut rules = split_css_rules(&text);
2040 if delete_index < rules.len() {
2041 rules.remove(delete_index);
2042 let new_css = rules.join("\n");
2043 it.dom.borrow_mut().set_text_content(idx, &new_css);
2044 }
2045 }
2046 }
2047 }
2048 Ok(Value::Undefined)
2049}
2050
2051pub fn dom_append_child(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2052 let child = arg(a, 0);
2053 if let (Some(p), Some(c)) = (this_dom_idx(&this), this_dom_idx(&child)) {
2054 it.dom.borrow_mut().append_child(p, c);
2055 }
2056 Ok(child) }
2058
2059pub fn dom_insert_before(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2061 let new_child = arg(a, 0);
2062 if let (Some(p), Some(nc)) = (this_dom_idx(&this), this_dom_idx(&new_child)) {
2063 let rc = this_dom_idx(&arg(a, 1)); if let Some(r) = rc {
2065 let is_child =
2066 it.dom.borrow().nodes.get(r).and_then(|n| n.parent) == Some(p);
2067 if !is_child {
2068 return Err(make_dom_exception(
2069 "NotFoundError",
2070 "The node before which the new node is to be inserted is not a child of this node",
2071 ));
2072 }
2073 }
2074 it.dom.borrow_mut().insert_before(p, nc, rc);
2075 }
2076 Ok(new_child)
2077}
2078
2079pub fn dom_move_before(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2092 let moved_node = arg(a, 0);
2093 if let (Some(p), Some(mn)) = (this_dom_idx(&this), this_dom_idx(&moved_node)) {
2094 let rc = this_dom_idx(&arg(a, 1));
2095 if let Some(r) = rc {
2096 let is_child = it.dom.borrow().nodes.get(r).and_then(|n| n.parent) == Some(p);
2097 if !is_child {
2098 return Err(make_dom_exception(
2099 "NotFoundError",
2100 "The node before which the moved node is to be inserted is not a child of this node",
2101 ));
2102 }
2103 }
2104 it.dom.borrow_mut().insert_before(p, mn, rc);
2105 }
2106 Ok(moved_node)
2107}
2108
2109pub fn select_options_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2119 let select_idx = match &this {
2120 Value::Object(o) => o.borrow().props.get("_select_idx").map(|v| v.to_number() as usize),
2121 _ => None,
2122 };
2123 let (Some(select_idx), Some(opt_idx)) = (select_idx, this_dom_idx(&arg(a, 0))) else {
2124 return Ok(Value::Undefined);
2125 };
2126 let before = arg(a, 1);
2127 let ref_idx = match &before {
2128 Value::Number(n) => it.dom.borrow().select_options(select_idx).get(*n as usize).copied(),
2129 Value::Object(_) => this_dom_idx(&before),
2130 _ => None,
2131 };
2132 it.dom.borrow_mut().insert_before(select_idx, opt_idx, ref_idx);
2133 Ok(Value::Undefined)
2134}
2135pub fn dom_select_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2142 let (Some(select_idx), Some(opt_idx)) = (this_dom_idx(&this), this_dom_idx(&arg(a, 0))) else {
2143 return Ok(Value::Undefined);
2144 };
2145 let before = arg(a, 1);
2146 let ref_idx = match &before {
2147 Value::Number(n) => it.dom.borrow().select_options(select_idx).get(*n as usize).copied(),
2148 Value::Object(_) => this_dom_idx(&before),
2149 _ => None,
2150 };
2151 it.dom.borrow_mut().insert_before(select_idx, opt_idx, ref_idx);
2152 Ok(Value::Undefined)
2153}
2154pub fn select_options_remove(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2157 let select_idx = match &this {
2158 Value::Object(o) => o.borrow().props.get("_select_idx").map(|v| v.to_number() as usize),
2159 _ => None,
2160 };
2161 let Some(select_idx) = select_idx else {
2162 return Ok(Value::Undefined);
2163 };
2164 let n = arg(a, 0).to_number() as usize;
2165 let opt_idx = it.dom.borrow().select_options(select_idx).get(n).copied();
2170 if let Some(opt_idx) = opt_idx {
2171 it.dom.borrow_mut().remove_child(select_idx, opt_idx);
2172 }
2173 Ok(Value::Undefined)
2174}
2175pub fn select_options_named_item(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2179 let select_idx = match &this {
2180 Value::Object(o) => o.borrow().props.get("_select_idx").map(|v| v.to_number() as usize),
2181 _ => None,
2182 };
2183 let Some(select_idx) = select_idx else {
2184 return Ok(Value::Null);
2185 };
2186 let name = arg(a, 0).to_js_string();
2187 let dom = it.dom.borrow();
2188 for opt_idx in dom.select_options(select_idx) {
2189 if dom.get_attr(opt_idx, "id").as_deref() == Some(name.as_str())
2190 || dom.get_attr(opt_idx, "name").as_deref() == Some(name.as_str())
2191 {
2192 return Ok(Value::Object(Obj::dom(opt_idx)));
2193 }
2194 }
2195 Ok(Value::Null)
2196}
2197
2198pub fn dom_replace_child(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2200 let old_child = arg(a, 1);
2201 if let (Some(p), Some(nc), Some(oc)) = (
2202 this_dom_idx(&this),
2203 this_dom_idx(&arg(a, 0)),
2204 this_dom_idx(&old_child),
2205 ) {
2206 let is_child = it.dom.borrow().nodes.get(oc).and_then(|n| n.parent) == Some(p);
2211 if !is_child {
2212 return Err(make_dom_exception(
2213 "NotFoundError",
2214 "The node to be replaced is not a child of this node",
2215 ));
2216 }
2217 it.dom.borrow_mut().replace_child(p, nc, oc);
2218 }
2219 Ok(old_child)
2220}
2221
2222pub(crate) fn arg_to_node(it: &mut Interp, v: &Value) -> Option<usize> {
2224 match this_dom_idx(v) {
2225 Some(i) => Some(i),
2226 None => match v {
2227 Value::Undefined | Value::Null => None,
2228 other => Some(it.dom.borrow_mut().create_text_node(&other.to_js_string())),
2229 },
2230 }
2231}
2232
2233pub fn dom_get_html(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2240 if let Some(idx) = this_dom_idx(&this) {
2241 return Ok(Value::str(it.dom.borrow().get_inner_html(idx)));
2242 }
2243 Ok(Value::str(""))
2244}
2245
2246pub fn dom_set_html_unsafe(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2252 if let Some(idx) = this_dom_idx(&this) {
2253 let html = arg(a, 0).to_js_string();
2254 it.dom.borrow_mut().set_inner_html(idx, &html);
2255 }
2256 Ok(Value::Undefined)
2257}
2258
2259pub fn dom_insert_adjacent_html(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2260 if let Some(idx) = this_dom_idx(&this) {
2261 let pos = arg(a, 0).to_js_string().to_lowercase();
2265 let html = arg(a, 1).to_js_string();
2266 it.dom.borrow_mut().insert_adjacent_html(idx, &pos, &html);
2267 }
2268 Ok(Value::Undefined)
2269}
2270pub fn dom_insert_adjacent_element(
2272 it: &mut Interp,
2273 this: Value,
2274 a: &[Value],
2275) -> Result<Value, Value> {
2276 let el = arg(a, 1);
2277 if let (Some(idx), Some(node)) = (this_dom_idx(&this), this_dom_idx(&el)) {
2278 let pos = arg(a, 0).to_js_string().to_lowercase();
2279 it.dom.borrow_mut().insert_adjacent_node(idx, &pos, node);
2280 }
2281 Ok(el)
2282}
2283pub fn dom_insert_adjacent_text(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2285 if let Some(idx) = this_dom_idx(&this) {
2286 let pos = arg(a, 0).to_js_string().to_lowercase();
2287 let node = it
2288 .dom
2289 .borrow_mut()
2290 .create_text_node(&arg(a, 1).to_js_string());
2291 it.dom.borrow_mut().insert_adjacent_node(idx, &pos, node);
2292 }
2293 Ok(Value::Undefined)
2294}
2295pub fn dom_toggle_attribute(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2297 if let Some(idx) = this_dom_idx(&this) {
2298 let name = arg(a, 0).to_js_string();
2299 let has = it.dom.borrow().has_attr(idx, &name);
2300 let new_state = if a.len() > 1 && !matches!(arg(a, 1), Value::Undefined) {
2301 arg(a, 1).truthy()
2302 } else {
2303 !has
2304 };
2305 if new_state {
2306 it.dom.borrow_mut().set_attr(idx, &name, "");
2307 } else {
2308 it.dom.borrow_mut().remove_attr(idx, &name);
2309 }
2310 return Ok(Value::Bool(new_state));
2311 }
2312 Ok(Value::Bool(false))
2313}
2314
2315pub fn dom_dialog_show(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2318 if let Some(idx) = this_dom_idx(&this) {
2319 it.dom.borrow_mut().set_attr(idx, "open", "open");
2320 }
2321 Ok(Value::Undefined)
2322}
2323
2324pub fn dom_dialog_show_modal(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2331 if let Some(idx) = this_dom_idx(&this) {
2332 let mut dom = it.dom.borrow_mut();
2333 dom.set_attr(idx, "open", "open");
2334 dom.set_attr(idx, "_modal", "1");
2335 }
2336 Ok(Value::Undefined)
2337}
2338
2339pub fn dom_dialog_close(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2346 if let Some(idx) = this_dom_idx(&this) {
2347 {
2348 let mut dom = it.dom.borrow_mut();
2349 dom.remove_attr(idx, "open");
2350 dom.remove_attr(idx, "_modal");
2351 }
2352 if !a.is_empty() {
2353 it.dom
2354 .borrow_mut()
2355 .set_attr(idx, "_return_value", &arg(a, 0).to_js_string());
2356 }
2357 let _ = it.dispatch_event_in_interp(idx, "close", &[]);
2358 }
2359 Ok(Value::Undefined)
2360}
2361
2362pub fn dom_dialog_request_close(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2370 if let Some(idx) = this_dom_idx(&this) {
2371 let (_, prevented) = it.dispatch_event_in_interp(idx, "cancel", &[]);
2372 if !prevented {
2373 {
2374 let mut dom = it.dom.borrow_mut();
2375 dom.remove_attr(idx, "open");
2376 dom.remove_attr(idx, "_modal");
2377 }
2378 if !a.is_empty() {
2379 it.dom
2380 .borrow_mut()
2381 .set_attr(idx, "_return_value", &arg(a, 0).to_js_string());
2382 }
2383 let _ = it.dispatch_event_in_interp(idx, "close", &[]);
2384 }
2385 }
2386 Ok(Value::Undefined)
2387}
2388
2389fn popover_toggle_extra(old_open: bool, new_open: bool) -> alloc::vec::Vec<(String, Value)> {
2405 alloc::vec![
2406 (String::from("oldState"), Value::str(if old_open { "open" } else { "closed" })),
2407 (String::from("newState"), Value::str(if new_open { "open" } else { "closed" })),
2408 ]
2409}
2410pub fn dom_show_popover(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2411 if let Some(idx) = this_dom_idx(&this) {
2412 let was_open = it.dom.borrow().has_attr(idx, "_popover_open");
2413 let (_, prevented) =
2417 it.dispatch_event_in_interp(idx, "beforetoggle", &popover_toggle_extra(was_open, true));
2418 if prevented {
2419 return Ok(Value::Undefined);
2420 }
2421 it.dom.borrow_mut().set_attr(idx, "_popover_open", "");
2422 let _ = it.dispatch_event_in_interp(idx, "toggle", &popover_toggle_extra(was_open, true));
2423 }
2424 Ok(Value::Undefined)
2425}
2426pub fn dom_hide_popover(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2427 if let Some(idx) = this_dom_idx(&this) {
2428 let was_open = it.dom.borrow().has_attr(idx, "_popover_open");
2429 let (_, prevented) =
2430 it.dispatch_event_in_interp(idx, "beforetoggle", &popover_toggle_extra(was_open, false));
2431 if prevented {
2432 return Ok(Value::Undefined);
2433 }
2434 it.dom.borrow_mut().remove_attr(idx, "_popover_open");
2435 let _ = it.dispatch_event_in_interp(idx, "toggle", &popover_toggle_extra(was_open, false));
2436 }
2437 Ok(Value::Undefined)
2438}
2439pub fn dom_toggle_popover(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2440 if let Some(idx) = this_dom_idx(&this) {
2441 let was_open = it.dom.borrow().has_attr(idx, "_popover_open");
2442 let force = if matches!(arg(a, 0), Value::Undefined) {
2443 !was_open
2444 } else {
2445 arg(a, 0).truthy()
2446 };
2447 let (_, prevented) =
2448 it.dispatch_event_in_interp(idx, "beforetoggle", &popover_toggle_extra(was_open, force));
2449 if prevented {
2450 return Ok(Value::Bool(was_open));
2451 }
2452 if force {
2453 it.dom.borrow_mut().set_attr(idx, "_popover_open", "");
2454 } else {
2455 it.dom.borrow_mut().remove_attr(idx, "_popover_open");
2456 }
2457 let _ = it.dispatch_event_in_interp(idx, "toggle", &popover_toggle_extra(was_open, force));
2458 return Ok(Value::Bool(force));
2459 }
2460 Ok(Value::Bool(false))
2461}
2462
2463pub fn dom_replace_children(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2469 if let Some(p) = this_dom_idx(&this) {
2470 it.dom.borrow_mut().set_inner_html(p, "");
2471 for v in a {
2472 if let Some(c) = arg_to_node(it, v) {
2473 it.dom.borrow_mut().append_child(p, c);
2474 }
2475 }
2476 }
2477 Ok(Value::Undefined)
2478}
2479pub fn dom_append(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2480 if let Some(p) = this_dom_idx(&this) {
2481 for v in a {
2482 if let Some(c) = arg_to_node(it, v) {
2483 it.dom.borrow_mut().append_child(p, c);
2484 }
2485 }
2486 }
2487 Ok(Value::Undefined)
2488}
2489pub fn dom_prepend(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2491 if let Some(p) = this_dom_idx(&this) {
2492 let refnode = it
2493 .dom
2494 .borrow()
2495 .nodes
2496 .get(p)
2497 .and_then(|n| n.children.first().copied());
2498 for v in a {
2499 if let Some(c) = arg_to_node(it, v) {
2500 it.dom.borrow_mut().insert_before(p, c, refnode);
2501 }
2502 }
2503 }
2504 Ok(Value::Undefined)
2505}
2506pub fn dom_before(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2508 if let Some(s) = this_dom_idx(&this) {
2509 let parent = it.dom.borrow().nodes.get(s).and_then(|n| n.parent);
2510 if let Some(p) = parent {
2511 for v in a {
2512 if let Some(c) = arg_to_node(it, v) {
2513 it.dom.borrow_mut().insert_before(p, c, Some(s));
2514 }
2515 }
2516 }
2517 }
2518 Ok(Value::Undefined)
2519}
2520pub fn dom_after(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2522 if let Some(s) = this_dom_idx(&this) {
2523 let (parent, next) = {
2524 let dom = it.dom.borrow();
2525 let parent = dom.nodes.get(s).and_then(|n| n.parent);
2526 let next = parent.and_then(|p| dom.nodes.get(p)).and_then(|n| {
2527 n.children
2528 .iter()
2529 .position(|&c| c == s)
2530 .and_then(|i| n.children.get(i + 1).copied())
2531 });
2532 (parent, next)
2533 };
2534 if let Some(p) = parent {
2535 for v in a {
2536 if let Some(c) = arg_to_node(it, v) {
2537 it.dom.borrow_mut().insert_before(p, c, next);
2538 }
2539 }
2540 }
2541 }
2542 Ok(Value::Undefined)
2543}
2544pub fn dom_replace_with(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2546 if let Some(s) = this_dom_idx(&this) {
2547 let parent = it.dom.borrow().nodes.get(s).and_then(|n| n.parent);
2548 if let Some(p) = parent {
2549 for v in a {
2550 if let Some(c) = arg_to_node(it, v) {
2551 it.dom.borrow_mut().insert_before(p, c, Some(s));
2552 }
2553 }
2554 it.dom.borrow_mut().remove_node(s);
2555 }
2556 }
2557 Ok(Value::Undefined)
2558}
2559
2560pub fn dom_clone_node(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2562 if let Some(idx) = this_dom_idx(&this) {
2563 let deep = arg(a, 0).truthy();
2564 let new_idx = it.dom.borrow_mut().clone_node(idx, deep);
2565 return Ok(dom_handle(Some(new_idx)));
2566 }
2567 Ok(Value::Null)
2568}
2569pub fn document_import_node(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
2577 dom_clone_node(it, arg(a, 0), a.get(1..).unwrap_or(&[]))
2578}
2579pub fn document_adopt_node(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
2583 let node = arg(a, 0);
2584 if let Some(idx) = this_dom_idx(&node) {
2585 it.dom.borrow_mut().remove_node(idx);
2586 }
2587 Ok(node)
2588}
2589
2590pub(crate) fn scroll_top_target(a: &[Value]) -> Option<i32> {
2605 match arg(a, 0) {
2606 Value::Object(_) => obj_prop(&arg(a, 0), "top").map(|v| v.to_number() as i32),
2607 Value::Undefined => None,
2608 _ => Some(arg(a, 1).to_number() as i32),
2609 }
2610}
2611pub fn dom_scroll_to(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2612 if let Some(idx) = this_dom_idx(&this) {
2613 if let Some(top) = scroll_top_target(a) {
2614 let mut dom = it.dom.borrow_mut();
2615 dom.scroll_tops.insert(idx, top.max(0));
2616 dom.dirty = true;
2617 }
2618 }
2619 Ok(Value::Undefined)
2620}
2621pub fn dom_scroll_by(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2624 if let Some(idx) = this_dom_idx(&this) {
2625 if let Some(delta) = scroll_top_target(a) {
2626 let mut dom = it.dom.borrow_mut();
2627 let cur = dom.scroll_tops.get(&idx).copied().unwrap_or(0);
2628 dom.scroll_tops.insert(idx, (cur + delta).max(0));
2629 dom.dirty = true;
2630 }
2631 }
2632 Ok(Value::Undefined)
2633}
2634pub fn dom_scroll_into_view(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2649 let Some(idx) = this_dom_idx(&this) else {
2650 return Ok(Value::Undefined);
2651 };
2652 let Some((_, y, _, _)) = it.dom.borrow().get_rect(idx) else {
2653 return Ok(Value::Undefined);
2654 };
2655 let current_scroll_y = it
2656 .global
2657 .borrow()
2658 .vars
2659 .get("scrollY")
2660 .map(|v| v.to_number())
2661 .unwrap_or(0.0);
2662 let target_abs_y = (current_scroll_y + y as f64).max(0.0);
2663 it.dom.borrow_mut().pending_scroll_abs_y = Some(target_abs_y as i32);
2664 Ok(Value::Undefined)
2665}
2666pub fn dom_click(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2667 if let Some(idx) = this_dom_idx(&this) {
2668 let ty = it.dom.borrow().get_attr(idx, "type").unwrap_or_default();
2669 match ty.as_str() {
2670 "checkbox" => {
2671 let checked = {
2672 let dom = it.dom.borrow();
2673 if let Some(lc) = dom.get_attr(idx, "_live_checked") {
2674 lc == "true"
2675 } else {
2676 dom.has_attr(idx, "checked")
2677 }
2678 };
2679 let mut dom = it.dom.borrow_mut();
2680 if checked {
2681 dom.set_attr(idx, "_live_checked", "false");
2682 } else {
2683 dom.set_attr(idx, "_live_checked", "true");
2684 }
2685 }
2686 "radio" => {
2687 let mut dom = it.dom.borrow_mut();
2688 dom.set_attr(idx, "_live_checked", "true");
2689 dom.uncheck_radio_group_siblings(idx);
2690 }
2691 _ => {}
2692 }
2693 it.dispatch_event_in_interp(idx, "click", &[]);
2694 }
2695 Ok(Value::Undefined)
2696}
2697
2698pub fn dom_focus(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2706 if let Some(idx) = this_dom_idx(&this) {
2707 let prev = it.dom.borrow().focused_idx;
2708 if prev == Some(idx) {
2709 return Ok(Value::Undefined);
2710 }
2711 if let Some(p) = prev {
2712 it.dom.borrow_mut().focused_idx = None;
2713 let _ = it.dispatch_event_in_interp(p, "blur", &[]);
2714 let _ = it.dispatch_event_in_interp(p, "focusout", &[]);
2715 }
2716 it.dom.borrow_mut().focused_idx = Some(idx);
2717 let _ = it.dispatch_event_in_interp(idx, "focus", &[]);
2718 let _ = it.dispatch_event_in_interp(idx, "focusin", &[]);
2719 }
2720 Ok(Value::Undefined)
2721}
2722pub fn dom_blur(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2726 if let Some(idx) = this_dom_idx(&this) {
2727 let is_focused = it.dom.borrow().focused_idx == Some(idx);
2728 if is_focused {
2729 it.dom.borrow_mut().focused_idx = None;
2730 let _ = it.dispatch_event_in_interp(idx, "blur", &[]);
2731 let _ = it.dispatch_event_in_interp(idx, "focusout", &[]);
2732 }
2733 }
2734 Ok(Value::Undefined)
2735}
2736
2737pub(crate) fn dom_step_by(it: &mut Interp, this: Value, a: &[Value], sign: f64) -> Result<Value, Value> {
2743 if let Some(idx) = this_dom_idx(&this) {
2744 let (cur, step, min, max) = {
2745 let dom = it.dom.borrow();
2746 let raw_val = dom.get_attr(idx, "_live_value")
2747 .or_else(|| dom.get_attr(idx, "value"));
2748 (
2749 raw_val.and_then(|s| s.trim().parse::<f64>().ok()).unwrap_or(0.0),
2750 dom.get_attr(idx, "step").and_then(|s| s.trim().parse::<f64>().ok()).unwrap_or(1.0),
2751 dom.get_attr(idx, "min").and_then(|s| s.trim().parse::<f64>().ok()),
2752 dom.get_attr(idx, "max").and_then(|s| s.trim().parse::<f64>().ok()),
2753 )
2754 };
2755 let multiplier = match arg(a, 0) {
2756 Value::Undefined => 1.0,
2757 n => n.to_number(),
2758 };
2759 let mut new_val = cur + sign * step * multiplier;
2760 if let Some(mn) = min {
2761 if new_val < mn {
2762 new_val = mn;
2763 }
2764 }
2765 if let Some(mx) = max {
2766 if new_val > mx {
2767 new_val = mx;
2768 }
2769 }
2770 let str_val = Value::Number(new_val).to_js_string();
2771 it.dom.borrow_mut().set_attr(idx, "_live_value", &str_val);
2772 }
2773 Ok(Value::Undefined)
2774}
2775pub fn dom_step_up(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2776 dom_step_by(it, this, a, 1.0)
2777}
2778pub fn dom_step_down(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2779 dom_step_by(it, this, a, -1.0)
2780}
2781
2782pub fn dom_request_submit(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2786 if let Some(idx) = this_dom_idx(&this) {
2787 it.dispatch_event_in_interp(idx, "submit", &[]);
2788 }
2789 Ok(Value::Undefined)
2790}
2791
2792pub fn dom_contains(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2797 let other = arg(a, 0);
2798 if let (Some(anc), Some(desc)) = (this_dom_idx(&this), this_dom_idx(&other)) {
2799 let contains = anc == desc || it.dom.borrow().is_ancestor_of(anc, desc);
2800 return Ok(Value::Bool(contains));
2801 }
2802 Ok(Value::Bool(false))
2803}
2804
2805pub fn dom_get_root_node(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2810 if let Some(idx) = this_dom_idx(&this) {
2811 let mut cur = idx;
2812 let mut guard = 0;
2813 loop {
2814 let parent = it.dom.borrow().nodes.get(cur).and_then(|n| n.parent);
2815 match parent {
2816 Some(p) => {
2817 cur = p;
2818 guard += 1;
2819 if guard > it.dom.borrow().nodes.len() {
2820 break;
2821 }
2822 }
2823 None => break,
2824 }
2825 }
2826 if cur == 0 {
2827 if let Some(doc) = it.global.borrow().vars.get("document").cloned() {
2828 return Ok(doc);
2829 }
2830 }
2831 return Ok(Value::Object(Obj::dom(cur)));
2832 }
2833 if matches!(this, Value::Object(_)) {
2834 if let Ok(parent) = it.get_property(&this, "parentNode") {
2835 if !matches!(parent, Value::Null | Value::Undefined) {
2836 return dom_get_root_node(it, parent, _a);
2837 }
2838 }
2839 return Ok(this);
2840 }
2841 Ok(Value::Undefined)
2842}
2843
2844pub fn dom_lookup_namespace_uri(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
2859 Ok(Value::Null)
2860}
2861
2862pub fn dom_lookup_prefix(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
2863 Ok(Value::Null)
2864}
2865
2866pub fn dom_is_default_namespace(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
2867 Ok(Value::Bool(matches!(arg(a, 0), Value::Null | Value::Undefined)))
2868}
2869
2870pub fn dom_check_visibility(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2871 if let Some(idx) = this_dom_idx(&this) {
2872 let visibility = it.dom.borrow().get_computed(idx, "visibility");
2873 if visibility == "hidden" || visibility == "collapse" {
2874 return Ok(Value::Bool(false));
2875 }
2876 let mut cur = idx;
2877 let mut guard = 0;
2878 loop {
2879 if it.dom.borrow().get_computed(cur, "display") == "none" {
2880 return Ok(Value::Bool(false));
2881 }
2882 let parent = it.dom.borrow().nodes.get(cur).and_then(|n| n.parent);
2883 match parent {
2884 Some(p) => {
2885 cur = p;
2886 guard += 1;
2887 if guard > it.dom.borrow().nodes.len() {
2888 break;
2889 }
2890 }
2891 None => break,
2892 }
2893 }
2894 return Ok(Value::Bool(true));
2895 }
2896 Ok(Value::Bool(false))
2897}
2898
2899pub fn dom_compare_document_position(
2903 it: &mut Interp,
2904 this: Value,
2905 a: &[Value],
2906) -> Result<Value, Value> {
2907 let other = arg(a, 0);
2908 if let (Some(this_idx), Some(other_idx)) = (this_dom_idx(&this), this_dom_idx(&other)) {
2909 let mask = it.dom.borrow().document_position(this_idx, other_idx);
2910 return Ok(Value::Number(mask as f64));
2911 }
2912 Ok(Value::Number(1.0))
2914}
2915
2916pub fn dom_classlist_replace(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2918 if let Some(idx) = this_host_idx(&this, "classList:") {
2919 let old = arg(a, 0).to_js_string();
2920 let new = arg(a, 1).to_js_string();
2921 let mut dom = it.dom.borrow_mut();
2922 let had = dom.class_contains(idx, &old);
2923 if had {
2924 dom.class_remove(idx, &old);
2925 dom.class_add(idx, &new);
2926 }
2927 return Ok(Value::Bool(had));
2928 }
2929 Ok(Value::Bool(false))
2930}
2931
2932pub fn dom_remove_child(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2933 let child = arg(a, 0);
2934 if let (Some(p), Some(c)) = (this_dom_idx(&this), this_dom_idx(&child)) {
2935 let is_child = it.dom.borrow().nodes.get(c).and_then(|n| n.parent) == Some(p);
2940 if !is_child {
2941 return Err(make_dom_exception(
2942 "NotFoundError",
2943 "The node to be removed is not a child of this node",
2944 ));
2945 }
2946 it.dom.borrow_mut().remove_child(p, c);
2947 }
2948 Ok(child)
2949}
2950
2951pub fn dom_remove(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2959 if let Some(idx) = this_dom_idx(&this) {
2960 let is_select = it.dom.borrow().nodes.get(idx).map(|n| n.tag == "select").unwrap_or(false);
2961 if is_select {
2962 if let Some(v) = a.first() {
2963 let n = v.to_number();
2964 if n.is_finite() {
2965 if n >= 0.0 {
2969 let options = it.dom.borrow().select_options(idx);
2970 if let Some(&opt) = options.get(n as usize) {
2971 it.dom.borrow_mut().remove_child(idx, opt);
2972 }
2973 }
2974 return Ok(Value::Undefined);
2975 }
2976 }
2977 }
2978 it.dom.borrow_mut().remove_node(idx);
2979 }
2980 Ok(Value::Undefined)
2981}
2982
2983pub(crate) fn nv(name: &str, f: NativeFn) -> Value {
2985 Value::Object(Obj::native(name, f))
2986}
2987pub(crate) fn arg(args: &[Value], i: usize) -> Value {
2988 args.get(i).cloned().unwrap_or(Value::Undefined)
2989}
2990
2991pub fn html_collection_item(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2993 if let Value::Object(o) = &this {
2994 let idx_num = arg(a, 0).to_number();
2995 if idx_num >= 0.0 && idx_num.is_finite() {
2996 let i = idx_num as usize;
2997 let borrow = o.borrow();
2998 if let ObjKind::Array(ref vec) = borrow.kind {
2999 if let Some(val) = vec.get(i) {
3000 return Ok(val.clone());
3001 }
3002 }
3003 }
3004 }
3005 Ok(Value::Null)
3006}
3007
3008pub fn html_collection_named_item(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3011 if let Value::Object(o) = &this {
3012 let target = arg(a, 0).to_js_string();
3013 if !target.is_empty() {
3014 let borrow = o.borrow();
3015 if let ObjKind::Array(ref vec) = borrow.kind {
3016 let dom = it.dom.borrow();
3017 let mut matches: Vec<Value> = Vec::new();
3018 let mut match_indices: Vec<usize> = Vec::new();
3019 for val in vec {
3020 if let Value::Object(item_obj) = val {
3021 if let ObjKind::DomElement(elem_idx) = item_obj.borrow().kind {
3022 let id_match = dom.get_attr(elem_idx, "id").map(|id| id == target).unwrap_or(false);
3023 let name_match = dom.get_attr(elem_idx, "name").map(|name| name == target).unwrap_or(false);
3024 if id_match || name_match {
3025 matches.push(val.clone());
3026 match_indices.push(elem_idx);
3027 }
3028 }
3029 }
3030 }
3031 if matches.len() == 1 {
3032 return Ok(matches[0].clone());
3033 } else if matches.len() > 1 {
3034 return Ok(make_radio_node_list(it, match_indices));
3035 }
3036 }
3037 }
3038 }
3039 Ok(Value::Null)
3040}
3041
3042pub fn make_radio_node_list(_it: &Interp, elements: Vec<usize>) -> Value {
3045 let mut arr: Vec<Value> = Vec::with_capacity(elements.len());
3046 for &idx in &elements {
3047 arr.push(Value::Object(Obj::dom(idx)));
3048 }
3049 let obj = Obj::array(arr);
3050 obj.borrow_mut().props.insert("_is_radio_nodelist".into(), Value::Bool(true));
3051 obj.borrow_mut().props.insert("item".into(), nv("item", html_collection_item));
3052 Value::Object(obj)
3053}
3054
3055pub fn make_html_collection(_it: &Interp, elements: Vec<usize>) -> Value {
3059 let mut arr: Vec<Value> = Vec::with_capacity(elements.len());
3060 for &idx in &elements {
3061 arr.push(Value::Object(Obj::dom(idx)));
3062 }
3063 let obj = Obj::array(arr);
3064 obj.borrow_mut().props.insert("item".into(), nv("item", html_collection_item));
3065 obj.borrow_mut().props.insert("namedItem".into(), nv("namedItem", html_collection_named_item));
3066 Value::Object(obj)
3067}
3068
3069pub fn dom_parser_parse_from_string(it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3072 let html = args.first().map(|v| v.to_js_string()).unwrap_or_default();
3073 let root = crate::os_lib::dom::parse_html(&html);
3074 it.dom.borrow_mut().build_from(&root);
3075 let doc = Obj::host("document");
3076 {
3077 let mut d = doc.borrow_mut();
3078 d.props.insert(
3079 "getElementById".into(),
3080 nv("getElementById", document_get_element_by_id),
3081 );
3082 d.props.insert(
3083 "querySelector".into(),
3084 nv("querySelector", document_query_selector),
3085 );
3086 d.props.insert(
3087 "querySelectorAll".into(),
3088 nv("querySelectorAll", document_query_selector_all),
3089 );
3090 }
3091 Ok(Value::Object(doc))
3092}
3093
3094pub fn dom_parser_ctor(_it: &mut Interp, _this: Value, _args: &[Value]) -> Result<Value, Value> {
3096 let obj = Obj::plain();
3097 obj.borrow_mut().props.insert(
3098 "parseFromString".into(),
3099 nv("parseFromString", dom_parser_parse_from_string),
3100 );
3101 Ok(Value::Object(obj))
3102}
3103
3104pub fn dom_form_reset(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3107 let Some(form_idx) = this_dom_idx(&this) else {
3108 return Ok(Value::Undefined);
3109 };
3110 let child_indices: alloc::vec::Vec<usize> = {
3111 let dom = it.dom.borrow();
3112 let mut list = alloc::vec::Vec::new();
3113 fn collect_descendants(dom: &crate::os_lib::js::dom_bridge::DomBridge, node_idx: usize, out: &mut alloc::vec::Vec<usize>) {
3114 if let Some(node) = dom.nodes.get(node_idx) {
3115 for &child in &node.children {
3116 out.push(child);
3117 collect_descendants(dom, child, out);
3118 }
3119 }
3120 }
3121 collect_descendants(&dom, form_idx, &mut list);
3122 list
3123 };
3124
3125 let mut dom = it.dom.borrow_mut();
3126 for idx in child_indices {
3127 let tag = dom.nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
3128 match tag.as_str() {
3129 "input" => {
3130 let ty = dom.get_attr(idx, "type").unwrap_or_default().to_lowercase();
3131 if ty == "checkbox" || ty == "radio" {
3132 let default_checked = dom.has_attr(idx, "checked");
3133 dom.set_attr(idx, "_live_checked", if default_checked { "true" } else { "false" });
3134 } else {
3135 let default_val = dom.get_attr(idx, "value").unwrap_or_default();
3136 dom.set_attr(idx, "_live_value", &default_val);
3137 }
3138 }
3139 "textarea" => {
3140 let default_val = dom.get_text_content(idx);
3141 dom.set_attr(idx, "_live_value", &default_val);
3142 }
3143 "select" => {
3144 dom.remove_attr(idx, "_live_value");
3145 }
3146 _ => {}
3147 }
3148 }
3149 Ok(Value::Undefined)
3150}
3151
3152pub fn dom_canvas_measure_text(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3154 let text = args.first().map(|v| v.to_js_string()).unwrap_or_default();
3155 let width = (text.len() * 8) as f64;
3156 let obj = Obj::plain();
3157 {
3158 let mut b = obj.borrow_mut();
3159 b.props.insert("width".into(), Value::Number(width));
3160 b.props.insert("actualBoundingBoxAscent".into(), Value::Number(10.0));
3161 b.props.insert("actualBoundingBoxDescent".into(), Value::Number(2.0));
3162 b.props.insert("fontBoundingBoxAscent".into(), Value::Number(10.0));
3163 b.props.insert("fontBoundingBoxDescent".into(), Value::Number(2.0));
3164 b.props.insert("actualBoundingBoxLeft".into(), Value::Number(0.0));
3165 b.props.insert("actualBoundingBoxRight".into(), Value::Number(width));
3166 }
3167 Ok(Value::Object(obj))
3168}
3169
3170pub fn dom_canvas_get_image_data(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3171 let w = args.get(2).map(|v| v.to_number() as usize).unwrap_or(1).max(1);
3172 let h = args.get(3).map(|v| v.to_number() as usize).unwrap_or(1).max(1);
3173 let len = w * h * 4;
3174 let data_arr = Obj::array(alloc::vec![Value::Number(0.0); len]);
3175 let img_data = Obj::plain();
3176 {
3177 let mut b = img_data.borrow_mut();
3178 b.props.insert("width".into(), Value::Number(w as f64));
3179 b.props.insert("height".into(), Value::Number(h as f64));
3180 b.props.insert("data".into(), Value::Object(data_arr));
3181 }
3182 Ok(Value::Object(img_data))
3183}
3184
3185pub fn dom_canvas_create_image_data(
3188 it: &mut Interp,
3189 this: Value,
3190 args: &[Value],
3191) -> Result<Value, Value> {
3192 dom_canvas_get_image_data(it, this, args)
3193}
3194
3195pub fn dom_canvas_is_point_in_path(_it: &mut Interp, _this: Value, _args: &[Value]) -> Result<Value, Value> {
3196 Ok(Value::Bool(false))
3197}
3198
3199pub fn dom_canvas_get_transform(it: &mut Interp, _this: Value, _args: &[Value]) -> Result<Value, Value> {
3200 dom_matrix_ctor(it, Value::Undefined, &[])
3201}
3202
3203pub fn dom_canvas_set_line_dash(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3204 if let Value::Object(o) = &this {
3205 let segs = args.first().cloned().unwrap_or(Value::Undefined);
3206 let items = this_items(&segs);
3207 let mut nums: alloc::vec::Vec<Value> = items.into_iter().map(|v| Value::Number(v.to_number())).collect();
3208 if nums.len() % 2 != 0 {
3209 let dup = nums.clone();
3210 nums.extend(dup);
3211 }
3212 o.borrow_mut().props.insert("_line_dash".into(), Value::Object(Obj::array(nums)));
3213 }
3214 Ok(Value::Undefined)
3215}
3216
3217pub fn dom_canvas_get_line_dash(_it: &mut Interp, this: Value, _args: &[Value]) -> Result<Value, Value> {
3218 if let Value::Object(o) = &this {
3219 if let Some(Value::Object(arr)) = o.borrow().props.get("_line_dash") {
3220 let items = this_items(&Value::Object(arr.clone()));
3221 return Ok(Value::Object(Obj::array(items)));
3222 }
3223 }
3224 Ok(Value::Object(Obj::array(alloc::vec![])))
3225}
3226
3227pub fn dom_canvas_round_rect(_it: &mut Interp, _this: Value, _args: &[Value]) -> Result<Value, Value> {
3228 Ok(Value::Undefined)
3229}
3230
3231pub fn dom_canvas_reset(_it: &mut Interp, this: Value, _args: &[Value]) -> Result<Value, Value> {
3233 if let Value::Object(ref ctx) = this {
3234 let mut b = ctx.borrow_mut();
3235 b.props.insert("fillStyle".into(), Value::str("#000000"));
3236 b.props.insert("strokeStyle".into(), Value::str("#000000"));
3237 b.props.insert("lineWidth".into(), Value::Number(1.0));
3238 b.props.insert("lineCap".into(), Value::str("butt"));
3239 b.props.insert("lineJoin".into(), Value::str("miter"));
3240 b.props.insert("miterLimit".into(), Value::Number(10.0));
3241 b.props.insert("lineDashOffset".into(), Value::Number(0.0));
3242 b.props.insert("font".into(), Value::str("10px sans-serif"));
3243 b.props.insert("textAlign".into(), Value::str("start"));
3244 b.props.insert("textBaseline".into(), Value::str("alphabetic"));
3245 b.props.insert("globalAlpha".into(), Value::Number(1.0));
3246 b.props.insert("globalCompositeOperation".into(), Value::str("source-over"));
3247 b.props.insert("shadowBlur".into(), Value::Number(0.0));
3248 b.props.insert("shadowColor".into(), Value::str("rgba(0, 0, 0, 0)"));
3249 b.props.insert("shadowOffsetX".into(), Value::Number(0.0));
3250 b.props.insert("shadowOffsetY".into(), Value::Number(0.0));
3251 b.props.insert("imageSmoothingEnabled".into(), Value::Bool(true));
3252 b.props.insert("direction".into(), Value::str("inherit"));
3253 b.props.insert("filter".into(), Value::str("none"));
3254 b.props.shift_remove("_line_dash");
3255 }
3256 Ok(Value::Undefined)
3257}
3258
3259pub fn dom_path2d_ctor(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3261 let p = Obj::plain();
3262 {
3263 let mut b = p.borrow_mut();
3264 let path_data = args.first().map(|v| v.to_js_string()).unwrap_or_default();
3265 b.props.insert("_path_data".into(), Value::str(&path_data));
3266 for m in [
3267 "addPath", "closePath", "moveTo", "lineTo", "arc", "arcTo",
3268 "rect", "roundRect", "ellipse", "bezierCurveTo", "quadraticCurveTo"
3269 ] {
3270 b.props.insert(m.into(), nv(m, dom_noop));
3271 }
3272 }
3273 Ok(Value::Object(p))
3274}
3275
3276pub fn dom_canvas_create_gradient(_it: &mut Interp, _this: Value, _args: &[Value]) -> Result<Value, Value> {
3278 let grad = Obj::plain();
3279 grad.borrow_mut().props.insert(
3280 "addColorStop".into(),
3281 nv("addColorStop", dom_noop),
3282 );
3283 Ok(Value::Object(grad))
3284}
3285
3286pub fn dom_canvas_get_context(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3288 let ctx_type = args.first().map(|v| v.to_js_string()).unwrap_or_default();
3289 if ctx_type.eq_ignore_ascii_case("2d") {
3290 let (cw, ch) = match &this {
3294 Value::Object(o) => {
3295 let kind = o.borrow().kind.clone();
3296 if let ObjKind::DomElement(idx) = kind {
3297 let d = _it.dom.borrow();
3298 let w = d
3299 .get_attr(idx, "width")
3300 .and_then(|s| s.trim().parse::<u32>().ok())
3301 .unwrap_or(300);
3302 let h = d
3303 .get_attr(idx, "height")
3304 .and_then(|s| s.trim().parse::<u32>().ok())
3305 .unwrap_or(150);
3306 (w, h)
3307 } else {
3308 (300, 150)
3309 }
3310 }
3311 _ => (300, 150),
3312 };
3313 let c2d_id = crate::os_lib::canvas2d::create_context(cw, ch);
3314 if let (Some(id), Value::Object(o)) = (c2d_id, &this) {
3318 let kind = o.borrow().kind.clone();
3319 if let ObjKind::DomElement(idx) = kind {
3320 _it.dom
3321 .borrow_mut()
3322 .set_attr(idx, "_c2d_id", &alloc::format!("{}", id));
3323 }
3324 }
3325 let ctx = Obj::plain();
3326 {
3327 let mut b = ctx.borrow_mut();
3328 b.props.insert("canvas".into(), this);
3329 b.props.insert("fillStyle".into(), Value::str("#000000"));
3330 b.props.insert("strokeStyle".into(), Value::str("#000000"));
3331 b.props.insert("lineWidth".into(), Value::Number(1.0));
3332 b.props.insert("lineCap".into(), Value::str("butt"));
3333 b.props.insert("lineJoin".into(), Value::str("miter"));
3334 b.props.insert("miterLimit".into(), Value::Number(10.0));
3335 b.props.insert("lineDashOffset".into(), Value::Number(0.0));
3336 b.props.insert("font".into(), Value::str("10px sans-serif"));
3337 b.props.insert("textAlign".into(), Value::str("start"));
3338 b.props.insert("textBaseline".into(), Value::str("alphabetic"));
3339 b.props.insert("globalAlpha".into(), Value::Number(1.0));
3340 b.props.insert("globalCompositeOperation".into(), Value::str("source-over"));
3341 b.props.insert("shadowBlur".into(), Value::Number(0.0));
3342 b.props.insert("shadowColor".into(), Value::str("rgba(0, 0, 0, 0)"));
3343 b.props.insert("shadowOffsetX".into(), Value::Number(0.0));
3344 b.props.insert("shadowOffsetY".into(), Value::Number(0.0));
3345 b.props.insert("imageSmoothingEnabled".into(), Value::Bool(true));
3346 b.props.insert("direction".into(), Value::str("inherit"));
3347 b.props.insert("filter".into(), Value::str("none"));
3348 if let Some(id) = c2d_id {
3353 b.props.insert("_c2d_id".into(), Value::Number(id as f64));
3354 }
3355
3356 b.props.insert("fillRect".into(), nv("fillRect", canvas_fill_rect));
3364 b.props.insert("strokeRect".into(), nv("strokeRect", canvas_stroke_rect));
3365 b.props.insert("clearRect".into(), nv("clearRect", canvas_clear_rect));
3366
3367 b.props.insert("beginPath".into(), nv("beginPath", canvas_begin_path));
3368 b.props.insert("closePath".into(), nv("closePath", canvas_close_path));
3369 b.props.insert("moveTo".into(), nv("moveTo", canvas_move_to));
3370 b.props.insert("lineTo".into(), nv("lineTo", canvas_line_to));
3371 b.props.insert("arc".into(), nv("arc", canvas_arc));
3372 b.props.insert("arcTo".into(), nv("arcTo", dom_noop));
3373 b.props.insert("bezierCurveTo".into(), nv("bezierCurveTo", canvas_bezier_curve_to));
3374 b.props.insert("quadraticCurveTo".into(), nv("quadraticCurveTo", canvas_quadratic_curve_to));
3375 b.props.insert("ellipse".into(), nv("ellipse", canvas_ellipse));
3376 b.props.insert("rect".into(), nv("rect", canvas_rect));
3377 b.props.insert("roundRect".into(), nv("roundRect", canvas_round_rect));
3378
3379 b.props.insert("fill".into(), nv("fill", canvas_fill));
3380 b.props.insert("stroke".into(), nv("stroke", canvas_stroke));
3381 b.props.insert("clip".into(), nv("clip", canvas_clip));
3382
3383 b.props.insert("fillText".into(), nv("fillText", canvas_fill_text));
3384 b.props.insert("strokeText".into(), nv("strokeText", canvas_fill_text));
3385 b.props.insert("measureText".into(), nv("measureText", canvas_measure_text));
3386
3387 b.props.insert("drawImage".into(), nv("drawImage", canvas_draw_image));
3388 b.props.insert("getImageData".into(), nv("getImageData", canvas_get_image_data));
3389 b.props.insert("createImageData".into(), nv("createImageData", dom_canvas_create_image_data));
3390 b.props.insert("putImageData".into(), nv("putImageData", canvas_put_image_data));
3391 b.props.insert("isPointInPath".into(), nv("isPointInPath", dom_canvas_is_point_in_path));
3392 b.props.insert("isPointInStroke".into(), nv("isPointInStroke", dom_canvas_is_point_in_path));
3393 b.props.insert("getTransform".into(), nv("getTransform", dom_canvas_get_transform));
3394 b.props.insert("setLineDash".into(), nv("setLineDash", dom_canvas_set_line_dash));
3395 b.props.insert("getLineDash".into(), nv("getLineDash", dom_canvas_get_line_dash));
3396 b.props.insert("reset".into(), nv("reset", dom_canvas_reset));
3397
3398 b.props.insert("createLinearGradient".into(), nv("createLinearGradient", dom_canvas_create_gradient));
3399 b.props.insert("createRadialGradient".into(), nv("createRadialGradient", dom_canvas_create_gradient));
3400 b.props.insert("createConicGradient".into(), nv("createConicGradient", dom_canvas_create_gradient));
3401 b.props.insert("createPattern".into(), nv("createPattern", dom_canvas_create_gradient));
3402
3403 b.props.insert("save".into(), nv("save", canvas_save));
3404 b.props.insert("restore".into(), nv("restore", canvas_restore));
3405 b.props.insert("scale".into(), nv("scale", canvas_scale));
3406 b.props.insert("rotate".into(), nv("rotate", canvas_rotate));
3407 b.props.insert("translate".into(), nv("translate", canvas_translate));
3408 b.props.insert("transform".into(), nv("transform", canvas_transform));
3409 b.props.insert("setTransform".into(), nv("setTransform", canvas_set_transform));
3410 b.props.insert("resetTransform".into(), nv("resetTransform", canvas_reset_transform));
3411 }
3412 return Ok(Value::Object(ctx));
3413 } else if ctx_type.eq_ignore_ascii_case("webgl") || ctx_type.eq_ignore_ascii_case("webgl2") || ctx_type.eq_ignore_ascii_case("bitmaprenderer") {
3414 let ctx = Obj::plain();
3415 ctx.borrow_mut().props.insert("canvas".into(), this);
3416 return Ok(Value::Object(ctx));
3417 }
3418 Ok(Value::Null)
3419}
3420
3421pub fn dom_canvas_to_data_url(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3423 let mime = args.first().map(|v| v.to_js_string()).unwrap_or_else(|| String::from("image/png"));
3424 let header = if mime.eq_ignore_ascii_case("image/jpeg") {
3425 "data:image/jpeg;base64,"
3426 } else if mime.eq_ignore_ascii_case("image/webp") {
3427 "data:image/webp;base64,"
3428 } else {
3429 "data:image/png;base64,"
3430 };
3431 let dummy_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
3433 Ok(Value::str(alloc::format!("{}{}", header, dummy_base64)))
3434}
3435
3436pub fn dom_canvas_to_blob(it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3438 let cb = args.first().cloned().unwrap_or(Value::Undefined);
3439 let mime = args.get(1).map(|v| v.to_js_string()).unwrap_or_else(|| String::from("image/png"));
3440 if matches!(cb, Value::Object(_)) {
3441 let blob = Obj::plain();
3442 {
3443 let mut b = blob.borrow_mut();
3444 b.props.insert("size".into(), Value::Number(68.0));
3445 b.props.insert("type".into(), Value::str(mime));
3446 }
3447 it.call_value(&cb, Value::Undefined, &[Value::Object(blob)])?;
3448 }
3449 Ok(Value::Undefined)
3450}
3451
3452
3453fn canvas_ctx_id(this: &Value) -> Option<u32> {
3462 match this {
3463 Value::Object(o) => match o.borrow().props.get("_c2d_id") {
3464 Some(Value::Number(n)) if *n >= 0.0 => Some(*n as u32),
3465 _ => None,
3466 },
3467 _ => None,
3468 }
3469}
3470
3471fn canvas_arg_f32(a: &[Value], i: usize) -> f32 {
3473 let v = a.get(i).map(|v| v.to_number()).unwrap_or(0.0);
3474 if v.is_finite() {
3475 v as f32
3476 } else {
3477 0.0
3478 }
3479}
3480
3481fn canvas_style_argb(this: &Value, key: &str) -> u32 {
3487 let s = match this {
3488 Value::Object(o) => o
3489 .borrow()
3490 .props
3491 .get(key)
3492 .map(|v| v.to_js_string())
3493 .unwrap_or_default(),
3494 _ => String::new(),
3495 };
3496 crate::os_lib::css::parse_color(&s).unwrap_or(0xFF00_0000)
3497}
3498
3499fn canvas_sync_state(this: &Value, ctx: &mut crate::os_lib::canvas2d::Canvas2dContext) {
3504 ctx.state.fill = canvas_style_argb(this, "fillStyle");
3505 ctx.state.stroke = canvas_style_argb(this, "strokeStyle");
3506 if let Value::Object(o) = this {
3507 let b = o.borrow();
3508 if let Some(Value::Number(n)) = b.props.get("globalAlpha") {
3509 if n.is_finite() {
3510 ctx.state.global_alpha = (*n as f32).clamp(0.0, 1.0);
3511 }
3512 }
3513 if let Some(Value::Number(n)) = b.props.get("lineWidth") {
3514 if n.is_finite() && *n > 0.0 {
3515 ctx.state.line_width = *n as f32;
3516 }
3517 }
3518 if let Some(Value::Str(s)) = b.props.get("lineCap") {
3520 if let Some(v) = crate::os_lib::canvas2d::parse_line_cap(s) {
3521 ctx.state.line_cap = v;
3522 }
3523 }
3524 if let Some(Value::Str(s)) = b.props.get("lineJoin") {
3525 if let Some(v) = crate::os_lib::canvas2d::parse_line_join(s) {
3526 ctx.state.line_join = v;
3527 }
3528 }
3529 if let Some(Value::Number(n)) = b.props.get("miterLimit") {
3530 if n.is_finite() && *n > 0.0 {
3531 ctx.state.miter_limit = *n as f32;
3532 }
3533 }
3534 ctx.state.line_dash.clear();
3537 if let Some(Value::Object(arr)) = b.props.get("_line_dash") {
3538 let ab = arr.borrow();
3539 if let ObjKind::Array(items) = &ab.kind {
3540 for v in items {
3541 ctx.state.line_dash.push(v.to_number() as f32);
3542 }
3543 }
3544 }
3545 if let Some(Value::Number(n)) = b.props.get("lineDashOffset") {
3546 if n.is_finite() {
3547 ctx.state.line_dash_offset = *n as f32;
3548 }
3549 }
3550 }
3551}
3552
3553pub fn canvas_fill_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3554 if let Some(id) = canvas_ctx_id(&this) {
3555 let (x, y, w, h) = (
3556 canvas_arg_f32(a, 0),
3557 canvas_arg_f32(a, 1),
3558 canvas_arg_f32(a, 2),
3559 canvas_arg_f32(a, 3),
3560 );
3561 crate::os_lib::canvas2d::with_context(id, |c| {
3562 canvas_sync_state(&this, c);
3563 c.fill_rect(x, y, w, h);
3564 });
3565 }
3566 Ok(Value::Undefined)
3567}
3568
3569pub fn canvas_stroke_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3570 if let Some(id) = canvas_ctx_id(&this) {
3571 let (x, y, w, h) = (
3572 canvas_arg_f32(a, 0),
3573 canvas_arg_f32(a, 1),
3574 canvas_arg_f32(a, 2),
3575 canvas_arg_f32(a, 3),
3576 );
3577 crate::os_lib::canvas2d::with_context(id, |c| {
3578 canvas_sync_state(&this, c);
3579 let mut p = crate::os_lib::canvas2d::PathBuilder::new();
3582 p.rect(&c.state.transform, x, y, w, h);
3583 let color = c.apply_alpha(c.state.stroke);
3584 let lw = c.state.line_width;
3585 let (cap, join, limit) =
3586 (c.state.line_cap, c.state.line_join, c.state.miter_limit);
3587 let sub = c.apply_dash(&p.finish());
3588 c.surface
3589 .stroke_path_styled(&sub, color, lw, cap, join, limit);
3590 });
3591 }
3592 Ok(Value::Undefined)
3593}
3594
3595pub fn canvas_clear_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3596 if let Some(id) = canvas_ctx_id(&this) {
3597 let (x, y, w, h) = (
3598 canvas_arg_f32(a, 0),
3599 canvas_arg_f32(a, 1),
3600 canvas_arg_f32(a, 2),
3601 canvas_arg_f32(a, 3),
3602 );
3603 crate::os_lib::canvas2d::with_context(id, |c| c.clear_rect(x, y, w, h));
3604 }
3605 Ok(Value::Undefined)
3606}
3607
3608pub fn canvas_begin_path(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3609 if let Some(id) = canvas_ctx_id(&this) {
3610 crate::os_lib::canvas2d::with_context(id, |c| c.path.begin_path());
3611 }
3612 Ok(Value::Undefined)
3613}
3614
3615pub fn canvas_close_path(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3616 if let Some(id) = canvas_ctx_id(&this) {
3617 crate::os_lib::canvas2d::with_context(id, |c| c.path.close_path());
3618 }
3619 Ok(Value::Undefined)
3620}
3621
3622pub fn canvas_move_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3623 if let Some(id) = canvas_ctx_id(&this) {
3624 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3625 crate::os_lib::canvas2d::with_context(id, |c| {
3626 let m = c.state.transform;
3627 c.path.move_to(&m, x, y);
3628 });
3629 }
3630 Ok(Value::Undefined)
3631}
3632
3633pub fn canvas_line_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3634 if let Some(id) = canvas_ctx_id(&this) {
3635 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3636 crate::os_lib::canvas2d::with_context(id, |c| {
3637 let m = c.state.transform;
3638 c.path.line_to(&m, x, y);
3639 });
3640 }
3641 Ok(Value::Undefined)
3642}
3643
3644pub fn canvas_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3645 if let Some(id) = canvas_ctx_id(&this) {
3646 let (x, y, w, h) = (
3647 canvas_arg_f32(a, 0),
3648 canvas_arg_f32(a, 1),
3649 canvas_arg_f32(a, 2),
3650 canvas_arg_f32(a, 3),
3651 );
3652 crate::os_lib::canvas2d::with_context(id, |c| {
3653 let m = c.state.transform;
3654 c.path.rect(&m, x, y, w, h);
3655 });
3656 }
3657 Ok(Value::Undefined)
3658}
3659
3660pub fn canvas_arc(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3661 if let Some(id) = canvas_ctx_id(&this) {
3662 let (cx, cy, r) = (
3663 canvas_arg_f32(a, 0),
3664 canvas_arg_f32(a, 1),
3665 canvas_arg_f32(a, 2),
3666 );
3667 let start = canvas_arg_f32(a, 3);
3668 let end = canvas_arg_f32(a, 4);
3669 let ccw = a.get(5).map(|v| v.truthy()).unwrap_or(false);
3670 crate::os_lib::canvas2d::with_context(id, |c| {
3671 let m = c.state.transform;
3672 c.path.arc(&m, cx, cy, r, start, end, ccw);
3673 });
3674 }
3675 Ok(Value::Undefined)
3676}
3677
3678pub fn canvas_fill(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3679 if let Some(id) = canvas_ctx_id(&this) {
3680 let rule = match a.first().map(|v| v.to_js_string()) {
3682 Some(s) if s.eq_ignore_ascii_case("evenodd") => {
3683 crate::os_lib::canvas2d::FillRule::EvenOdd
3684 }
3685 _ => crate::os_lib::canvas2d::FillRule::NonZero,
3686 };
3687 crate::os_lib::canvas2d::with_context(id, |c| {
3688 canvas_sync_state(&this, c);
3689 c.fill(rule);
3690 });
3691 }
3692 Ok(Value::Undefined)
3693}
3694
3695pub fn canvas_stroke(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3696 if let Some(id) = canvas_ctx_id(&this) {
3697 crate::os_lib::canvas2d::with_context(id, |c| {
3698 canvas_sync_state(&this, c);
3699 c.stroke_with_width();
3701 });
3702 }
3703 Ok(Value::Undefined)
3704}
3705
3706
3707pub fn canvas_save(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3709 if let Some(id) = canvas_ctx_id(&this) {
3710 crate::os_lib::canvas2d::with_context(id, |c| {
3711 canvas_sync_state(&this, c);
3714 c.save();
3715 });
3716 }
3717 Ok(Value::Undefined)
3718}
3719
3720pub fn canvas_restore(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3725 if let Some(id) = canvas_ctx_id(&this) {
3726 let restored = crate::os_lib::canvas2d::with_context(id, |c| {
3727 c.restore();
3728 (c.state.global_alpha, c.state.line_width)
3729 });
3730 if let (Some((ga, lw)), Value::Object(o)) = (restored, &this) {
3733 let mut b = o.borrow_mut();
3734 b.props.insert("globalAlpha".into(), Value::Number(ga as f64));
3735 b.props.insert("lineWidth".into(), Value::Number(lw as f64));
3736 }
3737 }
3738 Ok(Value::Undefined)
3739}
3740
3741pub fn canvas_translate(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3742 if let Some(id) = canvas_ctx_id(&this) {
3743 let (tx, ty) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3744 crate::os_lib::canvas2d::with_context(id, |c| c.translate(tx, ty));
3745 }
3746 Ok(Value::Undefined)
3747}
3748
3749pub fn canvas_scale(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3750 if let Some(id) = canvas_ctx_id(&this) {
3751 let (sx, sy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3752 crate::os_lib::canvas2d::with_context(id, |c| c.scale(sx, sy));
3753 }
3754 Ok(Value::Undefined)
3755}
3756
3757pub fn canvas_rotate(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3758 if let Some(id) = canvas_ctx_id(&this) {
3759 let rad = canvas_arg_f32(a, 0);
3760 crate::os_lib::canvas2d::with_context(id, |c| c.rotate(rad));
3761 }
3762 Ok(Value::Undefined)
3763}
3764
3765pub fn canvas_transform(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3767 if let Some(id) = canvas_ctx_id(&this) {
3768 let m = crate::os_lib::canvas2d::Matrix {
3769 a: canvas_arg_f32(a, 0),
3770 b: canvas_arg_f32(a, 1),
3771 c: canvas_arg_f32(a, 2),
3772 d: canvas_arg_f32(a, 3),
3773 e: canvas_arg_f32(a, 4),
3774 f: canvas_arg_f32(a, 5),
3775 };
3776 crate::os_lib::canvas2d::with_context(id, |ctx| {
3777 let cur = ctx.state.transform;
3778 ctx.set_transform(cur.multiply(&m));
3779 });
3780 }
3781 Ok(Value::Undefined)
3782}
3783
3784pub fn canvas_set_transform(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3786 if let Some(id) = canvas_ctx_id(&this) {
3787 let m = crate::os_lib::canvas2d::Matrix {
3788 a: canvas_arg_f32(a, 0),
3789 b: canvas_arg_f32(a, 1),
3790 c: canvas_arg_f32(a, 2),
3791 d: canvas_arg_f32(a, 3),
3792 e: canvas_arg_f32(a, 4),
3793 f: canvas_arg_f32(a, 5),
3794 };
3795 crate::os_lib::canvas2d::with_context(id, |ctx| ctx.set_transform(m));
3796 }
3797 Ok(Value::Undefined)
3798}
3799
3800pub fn canvas_reset_transform(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3801 if let Some(id) = canvas_ctx_id(&this) {
3802 crate::os_lib::canvas2d::with_context(id, |c| c.reset_transform());
3803 }
3804 Ok(Value::Undefined)
3805}
3806
3807pub fn canvas_bezier_curve_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3808 if let Some(id) = canvas_ctx_id(&this) {
3809 let (c1x, c1y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3810 let (c2x, c2y) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3811 let (x, y) = (canvas_arg_f32(a, 4), canvas_arg_f32(a, 5));
3812 crate::os_lib::canvas2d::with_context(id, |c| {
3813 let m = c.state.transform;
3814 c.path.bezier_curve_to(&m, c1x, c1y, c2x, c2y, x, y);
3815 });
3816 }
3817 Ok(Value::Undefined)
3818}
3819
3820pub fn canvas_quadratic_curve_to(
3821 _it: &mut Interp,
3822 this: Value,
3823 a: &[Value],
3824) -> Result<Value, Value> {
3825 if let Some(id) = canvas_ctx_id(&this) {
3826 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3827 let (x, y) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3828 crate::os_lib::canvas2d::with_context(id, |c| {
3829 let m = c.state.transform;
3830 c.path.quadratic_curve_to(&m, cx, cy, x, y);
3831 });
3832 }
3833 Ok(Value::Undefined)
3834}
3835
3836
3837pub fn canvas_ellipse(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3839 if let Some(id) = canvas_ctx_id(&this) {
3840 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3841 let (rx, ry) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3842 let rot = canvas_arg_f32(a, 4);
3843 let start = canvas_arg_f32(a, 5);
3844 let end = canvas_arg_f32(a, 6);
3845 let ccw = a.get(7).map(|v| v.truthy()).unwrap_or(false);
3846 crate::os_lib::canvas2d::with_context(id, |c| {
3847 let m = c.state.transform;
3848 c.path.ellipse(&m, cx, cy, rx, ry, rot, start, end, ccw);
3849 });
3850 }
3851 Ok(Value::Undefined)
3852}
3853
3854pub fn canvas_round_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3860 if let Some(id) = canvas_ctx_id(&this) {
3861 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3862 let (w, h) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3863 let vals: alloc::vec::Vec<f32> = match a.get(4) {
3865 Some(Value::Object(o)) => {
3866 let b = o.borrow();
3867 if let ObjKind::Array(items) = &b.kind {
3868 items
3869 .iter()
3870 .map(|v| {
3871 let n = v.to_number();
3872 if n.is_finite() {
3873 n as f32
3874 } else {
3875 0.0
3876 }
3877 })
3878 .collect()
3879 } else {
3880 alloc::vec![0.0]
3881 }
3882 }
3883 Some(v) => {
3884 let n = v.to_number();
3885 alloc::vec![if n.is_finite() { n as f32 } else { 0.0 }]
3886 }
3887 None => alloc::vec![0.0],
3888 };
3889 let radii = match vals.len() {
3891 0 => [0.0; 4],
3892 1 => [vals[0]; 4],
3893 2 => [vals[0], vals[1], vals[0], vals[1]],
3894 3 => [vals[0], vals[1], vals[2], vals[1]],
3895 _ => [vals[0], vals[1], vals[2], vals[3]],
3896 };
3897 crate::os_lib::canvas2d::with_context(id, |c| {
3898 let m = c.state.transform;
3899 c.path.round_rect(&m, x, y, w, h, radii);
3900 });
3901 }
3902 Ok(Value::Undefined)
3903}
3904
3905
3906pub fn canvas_clip(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3911 if let Some(id) = canvas_ctx_id(&this) {
3912 let rule = match a.first().map(|v| v.to_js_string()) {
3913 Some(s) if s.eq_ignore_ascii_case("evenodd") => {
3914 crate::os_lib::canvas2d::FillRule::EvenOdd
3915 }
3916 _ => crate::os_lib::canvas2d::FillRule::NonZero,
3917 };
3918 crate::os_lib::canvas2d::with_context(id, |c| {
3919 c.clip(rule);
3920 });
3921 }
3922 Ok(Value::Undefined)
3923}
3924
3925
3926pub fn canvas_get_image_data(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3931 let Some(id) = canvas_ctx_id(&this) else {
3932 return dom_canvas_get_image_data(it, this, a);
3933 };
3934 let (x, y) = (canvas_arg_f32(a, 0) as i32, canvas_arg_f32(a, 1) as i32);
3935 let w = (canvas_arg_f32(a, 2) as i32).max(0);
3936 let h = (canvas_arg_f32(a, 3) as i32).max(0);
3937 let bytes = crate::os_lib::canvas2d::with_context(id, |c| {
3938 crate::os_lib::canvas2d::get_image_data(&c.surface, x, y, w, h)
3939 });
3940 let Some(bytes) = bytes else {
3941 return dom_canvas_get_image_data(it, this, a);
3942 };
3943 let items: alloc::vec::Vec<Value> = bytes
3944 .into_iter()
3945 .map(|b| Value::Number(b as f64))
3946 .collect();
3947 let img = Obj::plain();
3948 {
3949 let mut b = img.borrow_mut();
3950 b.props.insert("width".into(), Value::Number(w as f64));
3951 b.props.insert("height".into(), Value::Number(h as f64));
3952 b.props.insert("data".into(), Value::Object(Obj::array(items)));
3953 }
3954 Ok(Value::Object(img))
3955}
3956
3957pub fn canvas_put_image_data(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3961 let Some(id) = canvas_ctx_id(&this) else {
3962 return Ok(Value::Undefined);
3963 };
3964 let Some(Value::Object(img)) = a.first() else {
3965 return Ok(Value::Undefined);
3966 };
3967 let (w, h, bytes) = {
3968 let b = img.borrow();
3969 let w = b
3970 .props
3971 .get("width")
3972 .map(|v| v.to_number() as i32)
3973 .unwrap_or(0);
3974 let h = b
3975 .props
3976 .get("height")
3977 .map(|v| v.to_number() as i32)
3978 .unwrap_or(0);
3979 let mut bytes: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
3980 if let Some(Value::Object(arr)) = b.props.get("data") {
3981 let ab = arr.borrow();
3982 if let ObjKind::Array(items) = &ab.kind {
3983 bytes.reserve(items.len());
3984 for v in items {
3985 let n = v.to_number();
3986 bytes.push(if n.is_finite() {
3988 n.clamp(0.0, 255.0) as u8
3989 } else {
3990 0
3991 });
3992 }
3993 }
3994 }
3995 (w, h, bytes)
3996 };
3997 let (dx, dy) = (canvas_arg_f32(a, 1) as i32, canvas_arg_f32(a, 2) as i32);
3998 crate::os_lib::canvas2d::with_context(id, |c| {
3999 crate::os_lib::canvas2d::put_image_data(&mut c.surface, &bytes, w, h, dx, dy);
4000 });
4001 Ok(Value::Undefined)
4002}
4003
4004
4005enum DrawSrc {
4007 Image(alloc::sync::Arc<crate::os_lib::web_engine::DecodedImage>),
4009 Canvas(u32, u32, alloc::vec::Vec<u8>),
4011}
4012
4013fn draw_src_from(it: &mut Interp, v: &Value) -> Option<DrawSrc> {
4019 let Value::Object(o) = v else {
4020 return None;
4021 };
4022 let idx = match o.borrow().kind {
4023 ObjKind::DomElement(i) => Some(i),
4024 _ => None,
4025 }?;
4026 let (tag, src, cid) = {
4027 let dom = it.dom.borrow();
4028 let tag = dom.nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
4029 let src = dom.get_attr(idx, "src").unwrap_or_default();
4030 let cid = dom
4031 .get_attr(idx, "_c2d_id")
4032 .and_then(|s| s.parse::<u32>().ok());
4033 (tag, src, cid)
4034 };
4035 if tag == "canvas" {
4036 let id = cid?;
4037 return crate::os_lib::canvas2d::with_context(id, |c| {
4038 let (w, h) = (c.surface.width, c.surface.height);
4039 DrawSrc::Canvas(w, h, crate::os_lib::canvas2d::get_image_data(
4040 &c.surface, 0, 0, w as i32, h as i32,
4041 ))
4042 });
4043 }
4044 crate::os_lib::web_engine::shared_image(&src).map(DrawSrc::Image)
4045}
4046
4047pub fn canvas_draw_image(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4050 let Some(id) = canvas_ctx_id(&this) else {
4051 return Ok(Value::Undefined);
4052 };
4053 let Some(src) = a.first().and_then(|v| draw_src_from(it, v)) else {
4054 return Ok(Value::Undefined);
4055 };
4056 let (sw, sh, bytes): (u32, u32, &[u8]) = match &src {
4057 DrawSrc::Image(img) => (img.width, img.height, &img.rgba),
4058 DrawSrc::Canvas(w, h, b) => (*w, *h, b),
4059 };
4060 let spec = match a.len() {
4062 0..=2 => return Ok(Value::Undefined),
4063 3 | 4 => crate::os_lib::canvas2d::BlitSpec {
4064 sx: 0.0,
4065 sy: 0.0,
4066 sw: sw as f32,
4067 sh: sh as f32,
4068 dx: canvas_arg_f32(a, 1),
4069 dy: canvas_arg_f32(a, 2),
4070 dw: sw as f32,
4071 dh: sh as f32,
4072 },
4073 5..=8 => crate::os_lib::canvas2d::BlitSpec {
4074 sx: 0.0,
4075 sy: 0.0,
4076 sw: sw as f32,
4077 sh: sh as f32,
4078 dx: canvas_arg_f32(a, 1),
4079 dy: canvas_arg_f32(a, 2),
4080 dw: canvas_arg_f32(a, 3),
4081 dh: canvas_arg_f32(a, 4),
4082 },
4083 _ => crate::os_lib::canvas2d::BlitSpec {
4084 sx: canvas_arg_f32(a, 1),
4085 sy: canvas_arg_f32(a, 2),
4086 sw: canvas_arg_f32(a, 3),
4087 sh: canvas_arg_f32(a, 4),
4088 dx: canvas_arg_f32(a, 5),
4089 dy: canvas_arg_f32(a, 6),
4090 dw: canvas_arg_f32(a, 7),
4091 dh: canvas_arg_f32(a, 8),
4092 },
4093 };
4094 let image = crate::os_lib::canvas2d::ImageSource {
4095 width: sw,
4096 height: sh,
4097 rgba: bytes,
4098 };
4099 crate::os_lib::canvas2d::with_context(id, |c| {
4100 canvas_sync_state(&this, c);
4101 c.sync_clip();
4102 let m = c.state.transform;
4103 let alpha = c.state.global_alpha;
4104 c.surface.draw_image(&image, &spec, &m, alpha);
4105 });
4106 Ok(Value::Undefined)
4107}
4108
4109
4110struct OwnedGlyph {
4112 width: u32,
4113 height: u32,
4114 x_offset: i32,
4115 y_offset: i32,
4116 advance: f32,
4117 alpha: alloc::vec::Vec<u8>,
4118}
4119
4120fn glyphs_for(text: &str, size_px: u32) -> alloc::vec::Vec<OwnedGlyph> {
4128 let mut out = alloc::vec::Vec::new();
4129 let mut font_lock = crate::kernel::vector_font::GLOBAL_VECTOR_FONT.lock();
4130 let Some(font) = font_lock.as_mut() else {
4131 return out;
4132 };
4133 for c in text.chars() {
4134 if let Some(g) = font.get_glyph(c, size_px) {
4135 out.push(OwnedGlyph {
4136 width: g.width,
4137 height: g.height,
4138 x_offset: g.x_offset,
4139 y_offset: g.y_offset,
4140 advance: g.advance as f32,
4141 alpha: g.data.clone(),
4142 });
4143 }
4144 }
4145 out
4146}
4147
4148fn canvas_font_size(this: &Value) -> f32 {
4150 let Value::Object(o) = this else {
4151 return 10.0;
4152 };
4153 let b = o.borrow();
4154 let Some(Value::Str(s)) = b.props.get("font") else {
4155 return 10.0;
4156 };
4157 crate::os_lib::canvas2d::parse_font_size(s).unwrap_or(10.0)
4158}
4159
4160pub fn canvas_fill_text(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4165 let Some(id) = canvas_ctx_id(&this) else {
4166 return Ok(Value::Undefined);
4167 };
4168 let text = a.first().map(|v| v.to_js_string()).unwrap_or_default();
4169 if text.is_empty() {
4170 return Ok(Value::Undefined);
4171 }
4172 let (x, y) = (canvas_arg_f32(a, 1), canvas_arg_f32(a, 2));
4173 if !x.is_finite() || !y.is_finite() {
4174 return Ok(Value::Undefined);
4175 }
4176 let size = canvas_font_size(&this);
4177 let glyphs = glyphs_for(&text, size as u32);
4178 if glyphs.is_empty() {
4179 return Ok(Value::Undefined);
4180 }
4181 let total: f32 = glyphs.iter().map(|g| g.advance).sum();
4182
4183 let (align, baseline) = {
4184 let mut al = crate::os_lib::canvas2d::TextAlign::Start;
4185 let mut bl = crate::os_lib::canvas2d::TextBaseline::Alphabetic;
4186 if let Value::Object(o) = &this {
4187 let b = o.borrow();
4188 if let Some(Value::Str(s)) = b.props.get("textAlign") {
4189 if let Some(v) = crate::os_lib::canvas2d::parse_text_align(s) {
4190 al = v;
4191 }
4192 }
4193 if let Some(Value::Str(s)) = b.props.get("textBaseline") {
4194 if let Some(v) = crate::os_lib::canvas2d::parse_text_baseline(s) {
4195 bl = v;
4196 }
4197 }
4198 }
4199 (al, bl)
4200 };
4201 let x0 = x + crate::os_lib::canvas2d::align_offset(align, total);
4202 let y0 = y + crate::os_lib::canvas2d::baseline_offset(baseline, size);
4203
4204 crate::os_lib::canvas2d::with_context(id, |c| {
4205 canvas_sync_state(&this, c);
4206 c.sync_clip();
4207 let color = c.apply_alpha(c.state.fill);
4208 let m = c.state.transform;
4209 let mut pen = x0;
4210 for g in &glyphs {
4211 let bm = crate::os_lib::canvas2d::GlyphBitmap {
4212 width: g.width,
4213 height: g.height,
4214 x_offset: g.x_offset,
4215 y_offset: g.y_offset,
4216 advance: g.advance,
4217 alpha: &g.alpha,
4218 };
4219 c.surface.draw_glyph(&bm, pen, y0, color, &m);
4220 pen += g.advance;
4221 }
4222 });
4223 Ok(Value::Undefined)
4224}
4225
4226pub fn canvas_measure_text(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4231 let text = a.first().map(|v| v.to_js_string()).unwrap_or_default();
4232 let size = canvas_font_size(&this);
4233 let glyphs = glyphs_for(&text, size as u32);
4234 if glyphs.is_empty() && !text.is_empty() {
4235 return dom_canvas_measure_text(it, this, a);
4237 }
4238 let width: f32 = glyphs.iter().map(|g| g.advance).sum();
4239 let obj = Obj::plain();
4240 {
4241 let mut b = obj.borrow_mut();
4242 b.props.insert("width".into(), Value::Number(width as f64));
4243 let ascent = glyphs
4245 .iter()
4246 .map(|g| -g.y_offset)
4247 .max()
4248 .unwrap_or(0)
4249 .max(0) as f64;
4250 let descent = glyphs
4251 .iter()
4252 .map(|g| g.y_offset + g.height as i32)
4253 .max()
4254 .unwrap_or(0)
4255 .max(0) as f64;
4256 b.props
4257 .insert("actualBoundingBoxAscent".into(), Value::Number(ascent));
4258 b.props
4259 .insert("actualBoundingBoxDescent".into(), Value::Number(descent));
4260 b.props.insert(
4261 "fontBoundingBoxAscent".into(),
4262 Value::Number((size * 0.85) as f64),
4263 );
4264 b.props.insert(
4265 "fontBoundingBoxDescent".into(),
4266 Value::Number((size * 0.15) as f64),
4267 );
4268 b.props
4269 .insert("actualBoundingBoxLeft".into(), Value::Number(0.0));
4270 b.props.insert(
4271 "actualBoundingBoxRight".into(),
4272 Value::Number(width as f64),
4273 );
4274 }
4275 Ok(Value::Object(obj))
4276}