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 if name == "src" && val.starts_with("blob:") {
1773 let is_media = it
1774 .dom
1775 .borrow()
1776 .nodes
1777 .get(idx)
1778 .map(|n| n.tag == "video" || n.tag == "audio")
1779 .unwrap_or(false);
1780 if is_media {
1781 let ms_val = match it.global.borrow().vars.get("__media_source_urls") {
1782 Some(Value::Object(r)) => r.borrow().props.get(&val).cloned(),
1783 _ => None,
1784 };
1785 if let Some(ms) = ms_val {
1786 super::media_source_open_internal(it, &ms);
1787 }
1788 }
1789 }
1790 }
1791 Ok(Value::Undefined)
1792}
1793
1794pub fn dom_check_validity(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1804 if let Some(idx) = this_dom_idx(&this) {
1805 let tag = it.dom.borrow().nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
1806 if tag == "form" {
1807 let dom = it.dom.borrow();
1808 let fields = dom.form_associated_controls(idx, &["input", "textarea", "select"]);
1814 let all_valid = fields.iter().all(|&node| {
1815 let value = dom.effective_form_value(node);
1816 dom.validate_field(node, &value).is_none()
1817 });
1818 return Ok(Value::Bool(all_valid));
1819 }
1820 let value = it.dom.borrow().effective_form_value(idx);
1821 let valid = it.dom.borrow().validate_field(idx, &value).is_none();
1822 return Ok(Value::Bool(valid));
1823 }
1824 Ok(Value::Bool(true))
1825}
1826
1827pub fn dom_set_custom_validity(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1830 if let Some(idx) = this_dom_idx(&this) {
1831 let msg = arg(a, 0).to_js_string();
1832 if msg.is_empty() {
1833 it.dom.borrow_mut().remove_attr(idx, "_custom_validity");
1834 } else {
1835 it.dom.borrow_mut().set_attr(idx, "_custom_validity", &msg);
1836 }
1837 }
1838 Ok(Value::Undefined)
1839}
1840
1841pub fn dom_classlist_for_each(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1847 if let Some(idx) = this_host_idx(&this, "classList:") {
1848 let classes = it.dom.borrow().nodes.get(idx).map(|n| n.classes.clone()).unwrap_or_default();
1849 let cb = arg(a, 0);
1850 for (i, c) in classes.into_iter().enumerate() {
1851 it.call_value(&cb, Value::Undefined, &[Value::str(c), Value::Number(i as f64)])?;
1852 }
1853 }
1854 Ok(Value::Undefined)
1855}
1856pub fn dom_classlist_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1857 if let Some(idx) = this_host_idx(&this, "classList:") {
1858 for v in a {
1859 it.dom
1860 .borrow_mut()
1861 .class_add(idx, &v.to_js_string());
1862 }
1863 }
1864 Ok(Value::Undefined)
1865}
1866
1867pub fn dom_classlist_remove(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1868 if let Some(idx) = this_host_idx(&this, "classList:") {
1869 for v in a {
1870 it.dom
1871 .borrow_mut()
1872 .class_remove(idx, &v.to_js_string());
1873 }
1874 }
1875 Ok(Value::Undefined)
1876}
1877
1878pub fn dom_classlist_supports(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
1880 Ok(Value::Bool(false))
1881}
1882
1883
1884pub fn dom_classlist_toggle(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1888 if let Some(idx) = this_host_idx(&this, "classList:") {
1889 let cls = arg(a, 0).to_js_string();
1890 if matches!(arg(a, 1), Value::Undefined) {
1891 let on = it.dom.borrow_mut().class_toggle(idx, &cls);
1892 return Ok(Value::Bool(on));
1893 }
1894 let force = arg(a, 1).truthy();
1895 if force {
1896 it.dom.borrow_mut().class_add(idx, &cls);
1897 } else {
1898 it.dom.borrow_mut().class_remove(idx, &cls);
1899 }
1900 return Ok(Value::Bool(force));
1901 }
1902 Ok(Value::Bool(false))
1903}
1904
1905pub fn dom_classlist_contains(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1906 if let Some(idx) = this_host_idx(&this, "classList:") {
1907 return Ok(Value::Bool(
1908 it.dom
1909 .borrow()
1910 .class_contains(idx, &arg(a, 0).to_js_string()),
1911 ));
1912 }
1913 Ok(Value::Bool(false))
1914}
1915
1916pub fn dom_classlist_entries(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1922 if let Some(idx) = this_host_idx(&this, "classList:") {
1923 let classes = it.dom.borrow().nodes.get(idx).map(|n| n.classes.clone()).unwrap_or_default();
1924 let out: Vec<Value> = classes
1925 .into_iter()
1926 .enumerate()
1927 .map(|(i, c)| Value::Object(Obj::array(alloc::vec![Value::Number(i as f64), Value::str(c)])))
1928 .collect();
1929 return Ok(Value::Object(make_iterator(out)));
1930 }
1931 Ok(Value::Object(make_iterator(alloc::vec![])))
1932}
1933pub fn dom_classlist_keys(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1934 if let Some(idx) = this_host_idx(&this, "classList:") {
1935 let n = it.dom.borrow().nodes.get(idx).map(|n| n.classes.len()).unwrap_or(0);
1936 return Ok(Value::Object(make_iterator(
1937 (0..n).map(|i| Value::Number(i as f64)).collect(),
1938 )));
1939 }
1940 Ok(Value::Object(make_iterator(alloc::vec![])))
1941}
1942pub fn dom_classlist_values(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
1943 if let Some(idx) = this_host_idx(&this, "classList:") {
1944 let classes = it.dom.borrow().nodes.get(idx).map(|n| n.classes.clone()).unwrap_or_default();
1945 return Ok(Value::Object(make_iterator(
1946 classes.into_iter().map(Value::str).collect(),
1947 )));
1948 }
1949 Ok(Value::Object(make_iterator(alloc::vec![])))
1950}
1951
1952pub(crate) fn document_create_element(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1953 let tag = arg(a, 0).to_js_string();
1954 let idx = it.dom.borrow_mut().create_element(&tag);
1955 if tag.contains('-') {
1957 it.ce_try_upgrade(idx, false);
1958 }
1959 Ok(Value::Object(Obj::dom(idx)))
1960}
1961pub(crate) fn document_create_element_ns(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1968 document_create_element(it, t, &[arg(a, 1)])
1969}
1970
1971pub(crate) fn option_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1974 let idx = it.dom.borrow_mut().create_element("option");
1975 let text = arg(a, 0);
1976 if !matches!(text, Value::Undefined) {
1977 it.dom.borrow_mut().set_text_content(idx, &text.to_js_string());
1978 }
1979 let value = arg(a, 1);
1980 if !matches!(value, Value::Undefined) {
1981 it.dom.borrow_mut().set_attr(idx, "value", &value.to_js_string());
1982 }
1983 if arg(a, 2).truthy() || arg(a, 3).truthy() {
1984 it.dom.borrow_mut().set_attr(idx, "selected", "selected");
1985 }
1986 Ok(Value::Object(Obj::dom(idx)))
1987}
1988
1989pub(crate) fn document_create_text_node(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1990 let text = arg(a, 0).to_js_string();
1991 let idx = it.dom.borrow_mut().create_text_node(&text);
1992 Ok(Value::Object(Obj::dom(idx)))
1993}
1994
1995pub(crate) fn document_create_comment(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1996 let text = arg(a, 0).to_js_string();
1997 let idx = it.dom.borrow_mut().create_comment(&text);
1998 Ok(Value::Object(Obj::dom(idx)))
1999}
2000
2001pub(crate) fn document_create_document_fragment(it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
2002 let idx = it.dom.borrow_mut().create_document_fragment();
2003 Ok(Value::Object(Obj::dom(idx)))
2004}
2005
2006pub(crate) fn split_css_rules(css: &str) -> alloc::vec::Vec<String> {
2007 let mut rules = alloc::vec![];
2008 let mut current_rule = String::new();
2009 let mut depth = 0;
2010 for c in css.chars() {
2011 current_rule.push(c);
2012 if c == '{' {
2013 depth += 1;
2014 } else if c == '}' {
2015 if depth > 0 {
2016 depth -= 1;
2017 }
2018 if depth == 0 {
2019 let trimmed = current_rule.trim();
2020 if !trimmed.is_empty() {
2021 rules.push(trimmed.to_string());
2022 }
2023 current_rule.clear();
2024 }
2025 }
2026 }
2027 let trimmed = current_rule.trim();
2028 if !trimmed.is_empty() {
2029 rules.push(trimmed.to_string());
2030 }
2031 rules
2032}
2033
2034pub(crate) fn dom_stylesheet_insert_rule(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2035 let rule_text = arg(a, 0).to_js_string();
2036 let insert_index = arg(a, 1).to_number() as usize;
2037 if let Value::Object(o) = this {
2038 if let ObjKind::Host(t) = &o.borrow().kind {
2039 if let Some(rest) = t.strip_prefix("stylesheet:") {
2040 let idx: usize = rest.parse().unwrap_or(usize::MAX);
2041 let text = it.dom.borrow().get_text_content(idx);
2042 let mut rules = split_css_rules(&text);
2043 let actual_index = insert_index.min(rules.len());
2044 rules.insert(actual_index, rule_text);
2045 let new_css = rules.join("\n");
2046 it.dom.borrow_mut().set_text_content(idx, &new_css);
2047 return Ok(Value::Number(actual_index as f64));
2048 }
2049 }
2050 }
2051 Ok(Value::Number(0.0))
2052}
2053
2054pub(crate) fn dom_stylesheet_delete_rule(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2055 let delete_index = arg(a, 0).to_number() as usize;
2056 if let Value::Object(o) = this {
2057 if let ObjKind::Host(t) = &o.borrow().kind {
2058 if let Some(rest) = t.strip_prefix("stylesheet:") {
2059 let idx: usize = rest.parse().unwrap_or(usize::MAX);
2060 let text = it.dom.borrow().get_text_content(idx);
2061 let mut rules = split_css_rules(&text);
2062 if delete_index < rules.len() {
2063 rules.remove(delete_index);
2064 let new_css = rules.join("\n");
2065 it.dom.borrow_mut().set_text_content(idx, &new_css);
2066 }
2067 }
2068 }
2069 }
2070 Ok(Value::Undefined)
2071}
2072
2073pub fn dom_append_child(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2074 let child = arg(a, 0);
2075 if let (Some(p), Some(c)) = (this_dom_idx(&this), this_dom_idx(&child)) {
2076 it.dom.borrow_mut().append_child(p, c);
2077 }
2078 Ok(child) }
2080
2081pub fn dom_insert_before(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2083 let new_child = arg(a, 0);
2084 if let (Some(p), Some(nc)) = (this_dom_idx(&this), this_dom_idx(&new_child)) {
2085 let rc = this_dom_idx(&arg(a, 1)); if let Some(r) = rc {
2087 let is_child =
2088 it.dom.borrow().nodes.get(r).and_then(|n| n.parent) == Some(p);
2089 if !is_child {
2090 return Err(make_dom_exception(
2091 "NotFoundError",
2092 "The node before which the new node is to be inserted is not a child of this node",
2093 ));
2094 }
2095 }
2096 it.dom.borrow_mut().insert_before(p, nc, rc);
2097 }
2098 Ok(new_child)
2099}
2100
2101pub fn dom_move_before(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2114 let moved_node = arg(a, 0);
2115 if let (Some(p), Some(mn)) = (this_dom_idx(&this), this_dom_idx(&moved_node)) {
2116 let rc = this_dom_idx(&arg(a, 1));
2117 if let Some(r) = rc {
2118 let is_child = it.dom.borrow().nodes.get(r).and_then(|n| n.parent) == Some(p);
2119 if !is_child {
2120 return Err(make_dom_exception(
2121 "NotFoundError",
2122 "The node before which the moved node is to be inserted is not a child of this node",
2123 ));
2124 }
2125 }
2126 it.dom.borrow_mut().insert_before(p, mn, rc);
2127 }
2128 Ok(moved_node)
2129}
2130
2131pub fn select_options_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2141 let select_idx = match &this {
2142 Value::Object(o) => o.borrow().props.get("_select_idx").map(|v| v.to_number() as usize),
2143 _ => None,
2144 };
2145 let (Some(select_idx), Some(opt_idx)) = (select_idx, this_dom_idx(&arg(a, 0))) else {
2146 return Ok(Value::Undefined);
2147 };
2148 let before = arg(a, 1);
2149 let ref_idx = match &before {
2150 Value::Number(n) => it.dom.borrow().select_options(select_idx).get(*n as usize).copied(),
2151 Value::Object(_) => this_dom_idx(&before),
2152 _ => None,
2153 };
2154 it.dom.borrow_mut().insert_before(select_idx, opt_idx, ref_idx);
2155 Ok(Value::Undefined)
2156}
2157pub fn dom_select_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2164 let (Some(select_idx), Some(opt_idx)) = (this_dom_idx(&this), this_dom_idx(&arg(a, 0))) else {
2165 return Ok(Value::Undefined);
2166 };
2167 let before = arg(a, 1);
2168 let ref_idx = match &before {
2169 Value::Number(n) => it.dom.borrow().select_options(select_idx).get(*n as usize).copied(),
2170 Value::Object(_) => this_dom_idx(&before),
2171 _ => None,
2172 };
2173 it.dom.borrow_mut().insert_before(select_idx, opt_idx, ref_idx);
2174 Ok(Value::Undefined)
2175}
2176pub fn select_options_remove(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::Undefined);
2185 };
2186 let n = arg(a, 0).to_number() as usize;
2187 let opt_idx = it.dom.borrow().select_options(select_idx).get(n).copied();
2192 if let Some(opt_idx) = opt_idx {
2193 it.dom.borrow_mut().remove_child(select_idx, opt_idx);
2194 }
2195 Ok(Value::Undefined)
2196}
2197pub fn select_options_named_item(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2201 let select_idx = match &this {
2202 Value::Object(o) => o.borrow().props.get("_select_idx").map(|v| v.to_number() as usize),
2203 _ => None,
2204 };
2205 let Some(select_idx) = select_idx else {
2206 return Ok(Value::Null);
2207 };
2208 let name = arg(a, 0).to_js_string();
2209 let dom = it.dom.borrow();
2210 for opt_idx in dom.select_options(select_idx) {
2211 if dom.get_attr(opt_idx, "id").as_deref() == Some(name.as_str())
2212 || dom.get_attr(opt_idx, "name").as_deref() == Some(name.as_str())
2213 {
2214 return Ok(Value::Object(Obj::dom(opt_idx)));
2215 }
2216 }
2217 Ok(Value::Null)
2218}
2219
2220pub fn dom_replace_child(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2222 let old_child = arg(a, 1);
2223 if let (Some(p), Some(nc), Some(oc)) = (
2224 this_dom_idx(&this),
2225 this_dom_idx(&arg(a, 0)),
2226 this_dom_idx(&old_child),
2227 ) {
2228 let is_child = it.dom.borrow().nodes.get(oc).and_then(|n| n.parent) == Some(p);
2233 if !is_child {
2234 return Err(make_dom_exception(
2235 "NotFoundError",
2236 "The node to be replaced is not a child of this node",
2237 ));
2238 }
2239 it.dom.borrow_mut().replace_child(p, nc, oc);
2240 }
2241 Ok(old_child)
2242}
2243
2244pub(crate) fn arg_to_node(it: &mut Interp, v: &Value) -> Option<usize> {
2246 match this_dom_idx(v) {
2247 Some(i) => Some(i),
2248 None => match v {
2249 Value::Undefined | Value::Null => None,
2250 other => Some(it.dom.borrow_mut().create_text_node(&other.to_js_string())),
2251 },
2252 }
2253}
2254
2255pub fn dom_get_html(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2262 if let Some(idx) = this_dom_idx(&this) {
2263 return Ok(Value::str(it.dom.borrow().get_inner_html(idx)));
2264 }
2265 Ok(Value::str(""))
2266}
2267
2268pub fn dom_set_html_unsafe(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2274 if let Some(idx) = this_dom_idx(&this) {
2275 let html = arg(a, 0).to_js_string();
2276 it.dom.borrow_mut().set_inner_html(idx, &html);
2277 }
2278 Ok(Value::Undefined)
2279}
2280
2281pub fn dom_insert_adjacent_html(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2282 if let Some(idx) = this_dom_idx(&this) {
2283 let pos = arg(a, 0).to_js_string().to_lowercase();
2287 let html = arg(a, 1).to_js_string();
2288 it.dom.borrow_mut().insert_adjacent_html(idx, &pos, &html);
2289 }
2290 Ok(Value::Undefined)
2291}
2292pub fn dom_insert_adjacent_element(
2294 it: &mut Interp,
2295 this: Value,
2296 a: &[Value],
2297) -> Result<Value, Value> {
2298 let el = arg(a, 1);
2299 if let (Some(idx), Some(node)) = (this_dom_idx(&this), this_dom_idx(&el)) {
2300 let pos = arg(a, 0).to_js_string().to_lowercase();
2301 it.dom.borrow_mut().insert_adjacent_node(idx, &pos, node);
2302 }
2303 Ok(el)
2304}
2305pub fn dom_insert_adjacent_text(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2307 if let Some(idx) = this_dom_idx(&this) {
2308 let pos = arg(a, 0).to_js_string().to_lowercase();
2309 let node = it
2310 .dom
2311 .borrow_mut()
2312 .create_text_node(&arg(a, 1).to_js_string());
2313 it.dom.borrow_mut().insert_adjacent_node(idx, &pos, node);
2314 }
2315 Ok(Value::Undefined)
2316}
2317pub fn dom_toggle_attribute(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2319 if let Some(idx) = this_dom_idx(&this) {
2320 let name = arg(a, 0).to_js_string();
2321 let has = it.dom.borrow().has_attr(idx, &name);
2322 let new_state = if a.len() > 1 && !matches!(arg(a, 1), Value::Undefined) {
2323 arg(a, 1).truthy()
2324 } else {
2325 !has
2326 };
2327 if new_state {
2328 it.dom.borrow_mut().set_attr(idx, &name, "");
2329 } else {
2330 it.dom.borrow_mut().remove_attr(idx, &name);
2331 }
2332 return Ok(Value::Bool(new_state));
2333 }
2334 Ok(Value::Bool(false))
2335}
2336
2337pub fn dom_dialog_show(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2340 if let Some(idx) = this_dom_idx(&this) {
2341 it.dom.borrow_mut().set_attr(idx, "open", "open");
2342 }
2343 Ok(Value::Undefined)
2344}
2345
2346pub fn dom_dialog_show_modal(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2353 if let Some(idx) = this_dom_idx(&this) {
2354 let mut dom = it.dom.borrow_mut();
2355 dom.set_attr(idx, "open", "open");
2356 dom.set_attr(idx, "_modal", "1");
2357 }
2358 Ok(Value::Undefined)
2359}
2360
2361pub fn dom_dialog_close(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2368 if let Some(idx) = this_dom_idx(&this) {
2369 {
2370 let mut dom = it.dom.borrow_mut();
2371 dom.remove_attr(idx, "open");
2372 dom.remove_attr(idx, "_modal");
2373 }
2374 if !a.is_empty() {
2375 it.dom
2376 .borrow_mut()
2377 .set_attr(idx, "_return_value", &arg(a, 0).to_js_string());
2378 }
2379 let _ = it.dispatch_event_in_interp(idx, "close", &[]);
2380 }
2381 Ok(Value::Undefined)
2382}
2383
2384pub fn dom_dialog_request_close(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2392 if let Some(idx) = this_dom_idx(&this) {
2393 let (_, prevented) = it.dispatch_event_in_interp(idx, "cancel", &[]);
2394 if !prevented {
2395 {
2396 let mut dom = it.dom.borrow_mut();
2397 dom.remove_attr(idx, "open");
2398 dom.remove_attr(idx, "_modal");
2399 }
2400 if !a.is_empty() {
2401 it.dom
2402 .borrow_mut()
2403 .set_attr(idx, "_return_value", &arg(a, 0).to_js_string());
2404 }
2405 let _ = it.dispatch_event_in_interp(idx, "close", &[]);
2406 }
2407 }
2408 Ok(Value::Undefined)
2409}
2410
2411fn popover_toggle_extra(old_open: bool, new_open: bool) -> alloc::vec::Vec<(String, Value)> {
2427 alloc::vec![
2428 (String::from("oldState"), Value::str(if old_open { "open" } else { "closed" })),
2429 (String::from("newState"), Value::str(if new_open { "open" } else { "closed" })),
2430 ]
2431}
2432pub fn dom_show_popover(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2433 if let Some(idx) = this_dom_idx(&this) {
2434 let was_open = it.dom.borrow().has_attr(idx, "_popover_open");
2435 let (_, prevented) =
2439 it.dispatch_event_in_interp(idx, "beforetoggle", &popover_toggle_extra(was_open, true));
2440 if prevented {
2441 return Ok(Value::Undefined);
2442 }
2443 it.dom.borrow_mut().set_attr(idx, "_popover_open", "");
2444 let _ = it.dispatch_event_in_interp(idx, "toggle", &popover_toggle_extra(was_open, true));
2445 }
2446 Ok(Value::Undefined)
2447}
2448pub fn dom_hide_popover(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2449 if let Some(idx) = this_dom_idx(&this) {
2450 let was_open = it.dom.borrow().has_attr(idx, "_popover_open");
2451 let (_, prevented) =
2452 it.dispatch_event_in_interp(idx, "beforetoggle", &popover_toggle_extra(was_open, false));
2453 if prevented {
2454 return Ok(Value::Undefined);
2455 }
2456 it.dom.borrow_mut().remove_attr(idx, "_popover_open");
2457 let _ = it.dispatch_event_in_interp(idx, "toggle", &popover_toggle_extra(was_open, false));
2458 }
2459 Ok(Value::Undefined)
2460}
2461pub fn dom_toggle_popover(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2462 if let Some(idx) = this_dom_idx(&this) {
2463 let was_open = it.dom.borrow().has_attr(idx, "_popover_open");
2464 let force = if matches!(arg(a, 0), Value::Undefined) {
2465 !was_open
2466 } else {
2467 arg(a, 0).truthy()
2468 };
2469 let (_, prevented) =
2470 it.dispatch_event_in_interp(idx, "beforetoggle", &popover_toggle_extra(was_open, force));
2471 if prevented {
2472 return Ok(Value::Bool(was_open));
2473 }
2474 if force {
2475 it.dom.borrow_mut().set_attr(idx, "_popover_open", "");
2476 } else {
2477 it.dom.borrow_mut().remove_attr(idx, "_popover_open");
2478 }
2479 let _ = it.dispatch_event_in_interp(idx, "toggle", &popover_toggle_extra(was_open, force));
2480 return Ok(Value::Bool(force));
2481 }
2482 Ok(Value::Bool(false))
2483}
2484
2485pub fn dom_replace_children(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2491 if let Some(p) = this_dom_idx(&this) {
2492 it.dom.borrow_mut().set_inner_html(p, "");
2493 for v in a {
2494 if let Some(c) = arg_to_node(it, v) {
2495 it.dom.borrow_mut().append_child(p, c);
2496 }
2497 }
2498 }
2499 Ok(Value::Undefined)
2500}
2501pub fn dom_append(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2502 if let Some(p) = this_dom_idx(&this) {
2503 for v in a {
2504 if let Some(c) = arg_to_node(it, v) {
2505 it.dom.borrow_mut().append_child(p, c);
2506 }
2507 }
2508 }
2509 Ok(Value::Undefined)
2510}
2511pub fn dom_prepend(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2513 if let Some(p) = this_dom_idx(&this) {
2514 let refnode = it
2515 .dom
2516 .borrow()
2517 .nodes
2518 .get(p)
2519 .and_then(|n| n.children.first().copied());
2520 for v in a {
2521 if let Some(c) = arg_to_node(it, v) {
2522 it.dom.borrow_mut().insert_before(p, c, refnode);
2523 }
2524 }
2525 }
2526 Ok(Value::Undefined)
2527}
2528pub fn dom_before(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2530 if let Some(s) = this_dom_idx(&this) {
2531 let parent = it.dom.borrow().nodes.get(s).and_then(|n| n.parent);
2532 if let Some(p) = parent {
2533 for v in a {
2534 if let Some(c) = arg_to_node(it, v) {
2535 it.dom.borrow_mut().insert_before(p, c, Some(s));
2536 }
2537 }
2538 }
2539 }
2540 Ok(Value::Undefined)
2541}
2542pub fn dom_after(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2544 if let Some(s) = this_dom_idx(&this) {
2545 let (parent, next) = {
2546 let dom = it.dom.borrow();
2547 let parent = dom.nodes.get(s).and_then(|n| n.parent);
2548 let next = parent.and_then(|p| dom.nodes.get(p)).and_then(|n| {
2549 n.children
2550 .iter()
2551 .position(|&c| c == s)
2552 .and_then(|i| n.children.get(i + 1).copied())
2553 });
2554 (parent, next)
2555 };
2556 if let Some(p) = parent {
2557 for v in a {
2558 if let Some(c) = arg_to_node(it, v) {
2559 it.dom.borrow_mut().insert_before(p, c, next);
2560 }
2561 }
2562 }
2563 }
2564 Ok(Value::Undefined)
2565}
2566pub fn dom_replace_with(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2568 if let Some(s) = this_dom_idx(&this) {
2569 let parent = it.dom.borrow().nodes.get(s).and_then(|n| n.parent);
2570 if let Some(p) = parent {
2571 for v in a {
2572 if let Some(c) = arg_to_node(it, v) {
2573 it.dom.borrow_mut().insert_before(p, c, Some(s));
2574 }
2575 }
2576 it.dom.borrow_mut().remove_node(s);
2577 }
2578 }
2579 Ok(Value::Undefined)
2580}
2581
2582pub fn dom_clone_node(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2584 if let Some(idx) = this_dom_idx(&this) {
2585 let deep = arg(a, 0).truthy();
2586 let new_idx = it.dom.borrow_mut().clone_node(idx, deep);
2587 return Ok(dom_handle(Some(new_idx)));
2588 }
2589 Ok(Value::Null)
2590}
2591pub fn document_import_node(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
2599 dom_clone_node(it, arg(a, 0), a.get(1..).unwrap_or(&[]))
2600}
2601pub fn document_adopt_node(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
2605 let node = arg(a, 0);
2606 if let Some(idx) = this_dom_idx(&node) {
2607 it.dom.borrow_mut().remove_node(idx);
2608 }
2609 Ok(node)
2610}
2611
2612pub(crate) fn scroll_top_target(a: &[Value]) -> Option<i32> {
2626 match arg(a, 0) {
2627 Value::Object(_) => obj_prop(&arg(a, 0), "top").map(|v| v.to_number() as i32),
2628 Value::Undefined => None,
2629 _ => Some(arg(a, 1).to_number() as i32),
2630 }
2631}
2632
2633pub(crate) fn scroll_left_target(a: &[Value]) -> Option<i32> {
2634 match arg(a, 0) {
2635 Value::Object(_) => obj_prop(&arg(a, 0), "left").map(|v| v.to_number() as i32),
2636 Value::Undefined => None,
2637 _ => Some(arg(a, 0).to_number() as i32),
2638 }
2639}
2640
2641pub fn dom_scroll_to(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2642 if let Some(idx) = this_dom_idx(&this) {
2643 let top = scroll_top_target(a);
2644 let left = scroll_left_target(a);
2645 if top.is_some() || left.is_some() {
2646 let mut dom = it.dom.borrow_mut();
2647 if let Some(t) = top {
2648 dom.scroll_tops.insert(idx, t.max(0));
2649 }
2650 if let Some(l) = left {
2651 dom.scroll_lefts.insert(idx, l.max(0));
2652 }
2653 dom.dirty = true;
2654 }
2655 }
2656 Ok(Value::Undefined)
2657}
2658
2659pub fn dom_scroll_by(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2662 if let Some(idx) = this_dom_idx(&this) {
2663 let delta_top = scroll_top_target(a);
2664 let delta_left = scroll_left_target(a);
2665 if delta_top.is_some() || delta_left.is_some() {
2666 let mut dom = it.dom.borrow_mut();
2667 if let Some(dt) = delta_top {
2668 let cur = dom.scroll_tops.get(&idx).copied().unwrap_or(0);
2669 dom.scroll_tops.insert(idx, (cur + dt).max(0));
2670 }
2671 if let Some(dl) = delta_left {
2672 let cur = dom.scroll_lefts.get(&idx).copied().unwrap_or(0);
2673 dom.scroll_lefts.insert(idx, (cur + dl).max(0));
2674 }
2675 dom.dirty = true;
2676 }
2677 }
2678 Ok(Value::Undefined)
2679}
2680pub fn dom_scroll_into_view(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2695 let Some(idx) = this_dom_idx(&this) else {
2696 return Ok(Value::Undefined);
2697 };
2698 let Some((_, y, _, _)) = it.dom.borrow().get_rect(idx) else {
2699 return Ok(Value::Undefined);
2700 };
2701 let current_scroll_y = it
2702 .global
2703 .borrow()
2704 .vars
2705 .get("scrollY")
2706 .map(|v| v.to_number())
2707 .unwrap_or(0.0);
2708 let target_abs_y = (current_scroll_y + y as f64).max(0.0);
2709 it.dom.borrow_mut().pending_scroll_abs_y = Some(target_abs_y as i32);
2710 Ok(Value::Undefined)
2711}
2712pub fn dom_click(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2713 if let Some(idx) = this_dom_idx(&this) {
2714 let ty = it.dom.borrow().get_attr(idx, "type").unwrap_or_default();
2715 match ty.as_str() {
2716 "checkbox" => {
2717 let checked = {
2718 let dom = it.dom.borrow();
2719 if let Some(lc) = dom.get_attr(idx, "_live_checked") {
2720 lc == "true"
2721 } else {
2722 dom.has_attr(idx, "checked")
2723 }
2724 };
2725 let mut dom = it.dom.borrow_mut();
2726 if checked {
2727 dom.set_attr(idx, "_live_checked", "false");
2728 } else {
2729 dom.set_attr(idx, "_live_checked", "true");
2730 }
2731 }
2732 "radio" => {
2733 let mut dom = it.dom.borrow_mut();
2734 dom.set_attr(idx, "_live_checked", "true");
2735 dom.uncheck_radio_group_siblings(idx);
2736 }
2737 _ => {}
2738 }
2739 it.dispatch_event_in_interp(idx, "click", &[]);
2740 }
2741 Ok(Value::Undefined)
2742}
2743
2744pub fn dom_focus(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2752 if let Some(idx) = this_dom_idx(&this) {
2753 let prev = it.dom.borrow().focused_idx;
2754 if prev == Some(idx) {
2755 return Ok(Value::Undefined);
2756 }
2757 if let Some(p) = prev {
2758 it.dom.borrow_mut().focused_idx = None;
2759 let _ = it.dispatch_event_in_interp(p, "blur", &[]);
2760 let _ = it.dispatch_event_in_interp(p, "focusout", &[]);
2761 }
2762 it.dom.borrow_mut().focused_idx = Some(idx);
2763 let _ = it.dispatch_event_in_interp(idx, "focus", &[]);
2764 let _ = it.dispatch_event_in_interp(idx, "focusin", &[]);
2765 }
2766 Ok(Value::Undefined)
2767}
2768pub fn dom_blur(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2772 if let Some(idx) = this_dom_idx(&this) {
2773 let is_focused = it.dom.borrow().focused_idx == Some(idx);
2774 if is_focused {
2775 it.dom.borrow_mut().focused_idx = None;
2776 let _ = it.dispatch_event_in_interp(idx, "blur", &[]);
2777 let _ = it.dispatch_event_in_interp(idx, "focusout", &[]);
2778 }
2779 }
2780 Ok(Value::Undefined)
2781}
2782
2783pub(crate) fn dom_step_by(it: &mut Interp, this: Value, a: &[Value], sign: f64) -> Result<Value, Value> {
2789 if let Some(idx) = this_dom_idx(&this) {
2790 let (cur, step, min, max) = {
2791 let dom = it.dom.borrow();
2792 let raw_val = dom.get_attr(idx, "_live_value")
2793 .or_else(|| dom.get_attr(idx, "value"));
2794 (
2795 raw_val.and_then(|s| s.trim().parse::<f64>().ok()).unwrap_or(0.0),
2796 dom.get_attr(idx, "step").and_then(|s| s.trim().parse::<f64>().ok()).unwrap_or(1.0),
2797 dom.get_attr(idx, "min").and_then(|s| s.trim().parse::<f64>().ok()),
2798 dom.get_attr(idx, "max").and_then(|s| s.trim().parse::<f64>().ok()),
2799 )
2800 };
2801 let multiplier = match arg(a, 0) {
2802 Value::Undefined => 1.0,
2803 n => n.to_number(),
2804 };
2805 let mut new_val = cur + sign * step * multiplier;
2806 if let Some(mn) = min {
2807 if new_val < mn {
2808 new_val = mn;
2809 }
2810 }
2811 if let Some(mx) = max {
2812 if new_val > mx {
2813 new_val = mx;
2814 }
2815 }
2816 let str_val = Value::Number(new_val).to_js_string();
2817 it.dom.borrow_mut().set_attr(idx, "_live_value", &str_val);
2818 }
2819 Ok(Value::Undefined)
2820}
2821pub fn dom_step_up(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2822 dom_step_by(it, this, a, 1.0)
2823}
2824pub fn dom_step_down(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2825 dom_step_by(it, this, a, -1.0)
2826}
2827
2828pub fn dom_request_submit(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2832 if let Some(idx) = this_dom_idx(&this) {
2833 it.dispatch_event_in_interp(idx, "submit", &[]);
2834 }
2835 Ok(Value::Undefined)
2836}
2837
2838pub fn dom_contains(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2843 let other = arg(a, 0);
2844 if let (Some(anc), Some(desc)) = (this_dom_idx(&this), this_dom_idx(&other)) {
2845 let contains = anc == desc || it.dom.borrow().is_ancestor_of(anc, desc);
2846 return Ok(Value::Bool(contains));
2847 }
2848 Ok(Value::Bool(false))
2849}
2850
2851pub fn dom_get_root_node(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2856 if let Some(idx) = this_dom_idx(&this) {
2857 let mut cur = idx;
2858 let mut guard = 0;
2859 loop {
2860 let parent = it.dom.borrow().nodes.get(cur).and_then(|n| n.parent);
2861 match parent {
2862 Some(p) => {
2863 cur = p;
2864 guard += 1;
2865 if guard > it.dom.borrow().nodes.len() {
2866 break;
2867 }
2868 }
2869 None => break,
2870 }
2871 }
2872 if cur == 0 {
2873 if let Some(doc) = it.global.borrow().vars.get("document").cloned() {
2874 return Ok(doc);
2875 }
2876 }
2877 return Ok(Value::Object(Obj::dom(cur)));
2878 }
2879 if matches!(this, Value::Object(_)) {
2880 if let Ok(parent) = it.get_property(&this, "parentNode") {
2881 if !matches!(parent, Value::Null | Value::Undefined) {
2882 return dom_get_root_node(it, parent, _a);
2883 }
2884 }
2885 return Ok(this);
2886 }
2887 Ok(Value::Undefined)
2888}
2889
2890pub fn dom_lookup_namespace_uri(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
2905 Ok(Value::Null)
2906}
2907
2908pub fn dom_lookup_prefix(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
2909 Ok(Value::Null)
2910}
2911
2912pub fn dom_is_default_namespace(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
2913 Ok(Value::Bool(matches!(arg(a, 0), Value::Null | Value::Undefined)))
2914}
2915
2916pub fn dom_check_visibility(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
2917 if let Some(idx) = this_dom_idx(&this) {
2918 let visibility = it.dom.borrow().get_computed(idx, "visibility");
2919 if visibility == "hidden" || visibility == "collapse" {
2920 return Ok(Value::Bool(false));
2921 }
2922 let mut cur = idx;
2923 let mut guard = 0;
2924 loop {
2925 if it.dom.borrow().get_computed(cur, "display") == "none" {
2926 return Ok(Value::Bool(false));
2927 }
2928 let parent = it.dom.borrow().nodes.get(cur).and_then(|n| n.parent);
2929 match parent {
2930 Some(p) => {
2931 cur = p;
2932 guard += 1;
2933 if guard > it.dom.borrow().nodes.len() {
2934 break;
2935 }
2936 }
2937 None => break,
2938 }
2939 }
2940 return Ok(Value::Bool(true));
2941 }
2942 Ok(Value::Bool(false))
2943}
2944
2945pub fn dom_compare_document_position(
2949 it: &mut Interp,
2950 this: Value,
2951 a: &[Value],
2952) -> Result<Value, Value> {
2953 let other = arg(a, 0);
2954 if let (Some(this_idx), Some(other_idx)) = (this_dom_idx(&this), this_dom_idx(&other)) {
2955 let mask = it.dom.borrow().document_position(this_idx, other_idx);
2956 return Ok(Value::Number(mask as f64));
2957 }
2958 Ok(Value::Number(1.0))
2960}
2961
2962pub fn dom_classlist_replace(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2964 if let Some(idx) = this_host_idx(&this, "classList:") {
2965 let old = arg(a, 0).to_js_string();
2966 let new = arg(a, 1).to_js_string();
2967 let mut dom = it.dom.borrow_mut();
2968 let had = dom.class_contains(idx, &old);
2969 if had {
2970 dom.class_remove(idx, &old);
2971 dom.class_add(idx, &new);
2972 }
2973 return Ok(Value::Bool(had));
2974 }
2975 Ok(Value::Bool(false))
2976}
2977
2978pub fn dom_remove_child(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
2979 let child = arg(a, 0);
2980 if let (Some(p), Some(c)) = (this_dom_idx(&this), this_dom_idx(&child)) {
2981 let is_child = it.dom.borrow().nodes.get(c).and_then(|n| n.parent) == Some(p);
2986 if !is_child {
2987 return Err(make_dom_exception(
2988 "NotFoundError",
2989 "The node to be removed is not a child of this node",
2990 ));
2991 }
2992 it.dom.borrow_mut().remove_child(p, c);
2993 }
2994 Ok(child)
2995}
2996
2997pub fn dom_remove(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3005 if let Some(idx) = this_dom_idx(&this) {
3006 let is_select = it.dom.borrow().nodes.get(idx).map(|n| n.tag == "select").unwrap_or(false);
3007 if is_select {
3008 if let Some(v) = a.first() {
3009 let n = v.to_number();
3010 if n.is_finite() {
3011 if n >= 0.0 {
3015 let options = it.dom.borrow().select_options(idx);
3016 if let Some(&opt) = options.get(n as usize) {
3017 it.dom.borrow_mut().remove_child(idx, opt);
3018 }
3019 }
3020 return Ok(Value::Undefined);
3021 }
3022 }
3023 }
3024 it.dom.borrow_mut().remove_node(idx);
3025 }
3026 Ok(Value::Undefined)
3027}
3028
3029pub(crate) fn nv(name: &str, f: NativeFn) -> Value {
3031 Value::Object(Obj::native(name, f))
3032}
3033pub(crate) fn arg(args: &[Value], i: usize) -> Value {
3034 args.get(i).cloned().unwrap_or(Value::Undefined)
3035}
3036
3037pub fn html_collection_item(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3039 if let Value::Object(o) = &this {
3040 let idx_num = arg(a, 0).to_number();
3041 if idx_num >= 0.0 && idx_num.is_finite() {
3042 let i = idx_num as usize;
3043 let borrow = o.borrow();
3044 if let ObjKind::Array(ref vec) = borrow.kind {
3045 if let Some(val) = vec.get(i) {
3046 return Ok(val.clone());
3047 }
3048 }
3049 }
3050 }
3051 Ok(Value::Null)
3052}
3053
3054pub fn html_collection_named_item(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3057 if let Value::Object(o) = &this {
3058 let target = arg(a, 0).to_js_string();
3059 if !target.is_empty() {
3060 let borrow = o.borrow();
3061 if let ObjKind::Array(ref vec) = borrow.kind {
3062 let dom = it.dom.borrow();
3063 let mut matches: Vec<Value> = Vec::new();
3064 let mut match_indices: Vec<usize> = Vec::new();
3065 for val in vec {
3066 if let Value::Object(item_obj) = val {
3067 if let ObjKind::DomElement(elem_idx) = item_obj.borrow().kind {
3068 let id_match = dom.get_attr(elem_idx, "id").map(|id| id == target).unwrap_or(false);
3069 let name_match = dom.get_attr(elem_idx, "name").map(|name| name == target).unwrap_or(false);
3070 if id_match || name_match {
3071 matches.push(val.clone());
3072 match_indices.push(elem_idx);
3073 }
3074 }
3075 }
3076 }
3077 if matches.len() == 1 {
3078 return Ok(matches[0].clone());
3079 } else if matches.len() > 1 {
3080 return Ok(make_radio_node_list(it, match_indices));
3081 }
3082 }
3083 }
3084 }
3085 Ok(Value::Null)
3086}
3087
3088pub fn make_radio_node_list(_it: &Interp, elements: Vec<usize>) -> Value {
3091 let mut arr: Vec<Value> = Vec::with_capacity(elements.len());
3092 for &idx in &elements {
3093 arr.push(Value::Object(Obj::dom(idx)));
3094 }
3095 let obj = Obj::array(arr);
3096 obj.borrow_mut().props.insert("_is_radio_nodelist".into(), Value::Bool(true));
3097 obj.borrow_mut().props.insert("item".into(), nv("item", html_collection_item));
3098 Value::Object(obj)
3099}
3100
3101pub fn make_html_collection(_it: &Interp, elements: Vec<usize>) -> Value {
3105 let mut arr: Vec<Value> = Vec::with_capacity(elements.len());
3106 for &idx in &elements {
3107 arr.push(Value::Object(Obj::dom(idx)));
3108 }
3109 let obj = Obj::array(arr);
3110 obj.borrow_mut().props.insert("item".into(), nv("item", html_collection_item));
3111 obj.borrow_mut().props.insert("namedItem".into(), nv("namedItem", html_collection_named_item));
3112 Value::Object(obj)
3113}
3114
3115fn parsed_doc_get_root_idx(this: &Value) -> Option<usize> {
3116 if let Value::Object(o) = this {
3117 if let Some(Value::Number(n)) = o.borrow().props.get("_root_idx") {
3118 return Some(*n as usize);
3119 }
3120 }
3121 None
3122}
3123
3124pub(crate) fn parsed_doc_get_element_by_id(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3125 if let Some(root_idx) = parsed_doc_get_root_idx(&this) {
3126 let id = arg(a, 0).to_js_string();
3127 let dom = it.dom.borrow();
3128 if let Some(n) = dom.nodes.get(root_idx) {
3129 if !n.is_text && n.id == id {
3130 return Ok(dom_handle(Some(root_idx)));
3131 }
3132 }
3133 for desc in dom.descendants_by_tag_name(root_idx, "*") {
3134 if let Some(n) = dom.nodes.get(desc) {
3135 if !n.is_text && n.id == id {
3136 return Ok(dom_handle(Some(desc)));
3137 }
3138 }
3139 }
3140 return Ok(Value::Null);
3141 }
3142 document_get_element_by_id(it, this, a)
3143}
3144
3145pub(crate) fn parsed_doc_query_selector(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3146 if let Some(root_idx) = parsed_doc_get_root_idx(&this) {
3147 let sel = arg(a, 0).to_js_string();
3148 let dom = it.dom.borrow();
3149 for c in dom.query_all_scoped(&sel, Some(root_idx)) {
3150 if c == root_idx || dom.is_ancestor_of(root_idx, c) {
3151 return Ok(dom_handle(Some(c)));
3152 }
3153 }
3154 return Ok(Value::Null);
3155 }
3156 document_query_selector(it, this, a)
3157}
3158
3159pub(crate) fn parsed_doc_query_selector_all(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3160 if let Some(root_idx) = parsed_doc_get_root_idx(&this) {
3161 let sel = arg(a, 0).to_js_string();
3162 let dom = it.dom.borrow();
3163 let mut items = Vec::new();
3164 for c in dom.query_all_scoped(&sel, Some(root_idx)) {
3165 if c == root_idx || dom.is_ancestor_of(root_idx, c) {
3166 items.push(Value::Object(Obj::dom(c)));
3167 }
3168 }
3169 return Ok(Value::Object(Obj::array(items)));
3170 }
3171 document_query_selector_all(it, this, a)
3172}
3173
3174pub(crate) fn parsed_doc_get_elements_by_tag_name(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3175 if let Some(root_idx) = parsed_doc_get_root_idx(&this) {
3176 let tag = arg(a, 0).to_js_string();
3177 let dom = it.dom.borrow();
3178 let mut items = Vec::new();
3179 let want_all = tag == "*";
3180 if let Some(n) = dom.nodes.get(root_idx) {
3181 if !n.is_text && (want_all || n.tag.eq_ignore_ascii_case(&tag)) {
3182 items.push(Value::Object(Obj::dom(root_idx)));
3183 }
3184 }
3185 for desc in dom.descendants_by_tag_name(root_idx, &tag) {
3186 items.push(Value::Object(Obj::dom(desc)));
3187 }
3188 return Ok(Value::Object(Obj::array(items)));
3189 }
3190 document_get_elements_by_tag_name(it, this, a)
3191}
3192
3193pub fn dom_parser_parse_from_string(it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3197 let raw_input = args.first().map(|v| v.to_js_string()).unwrap_or_default();
3198 let mime = args.get(1).map(|v| v.to_js_string()).unwrap_or_default();
3199 let mime_type = crate::os_lib::xml_parser::parse_mime_type(&mime);
3200 if mime_type == crate::os_lib::xml_parser::SupportedMimeType::Unsupported {
3201 return Err(Value::str("TypeError: Invalid or unsupported MIME type"));
3202 }
3203
3204 let root_node = match mime_type {
3205 crate::os_lib::xml_parser::SupportedMimeType::Html => {
3206 crate::os_lib::dom::parse_html(&raw_input)
3207 }
3208 crate::os_lib::xml_parser::SupportedMimeType::Xml
3209 | crate::os_lib::xml_parser::SupportedMimeType::Svg
3210 | crate::os_lib::xml_parser::SupportedMimeType::Xhtml => {
3211 match crate::os_lib::xml_parser::parse_xml(&raw_input) {
3212 Ok(node) => node,
3213 Err(e) => crate::os_lib::xml_parser::create_parsererror_node(&e),
3214 }
3215 }
3216 crate::os_lib::xml_parser::SupportedMimeType::Unsupported => unreachable!(),
3217 };
3218
3219 let root_idx = it.dom.borrow_mut().import_node(&root_node);
3220 if mime_type != crate::os_lib::xml_parser::SupportedMimeType::Html {
3221 it.dom.borrow_mut().mark_subtree_xml(root_idx);
3222 }
3223 let doc = Obj::host("document");
3224 {
3225 let mut d = doc.borrow_mut();
3226 d.props.insert("_root_idx".into(), Value::Number(root_idx as f64));
3227 d.props.insert("nodeType".into(), Value::Number(9.0));
3228 d.props.insert("nodeName".into(), Value::str("#document"));
3229 d.props.insert("contentType".into(), Value::str(&mime));
3230
3231 let doc_elem_idx = {
3232 let dom = it.dom.borrow();
3233 if let Some(n) = dom.nodes.get(root_idx) {
3234 if !n.is_text && !n.tag.starts_with('#') {
3235 Some(root_idx)
3236 } else {
3237 n.children.iter().copied().find(|&c| {
3238 dom.nodes.get(c).map(|cn| !cn.is_text).unwrap_or(false)
3239 })
3240 }
3241 } else {
3242 None
3243 }
3244 };
3245
3246 if let Some(de) = doc_elem_idx {
3247 d.props.insert("documentElement".into(), Value::Object(Obj::dom(de)));
3248 } else {
3249 d.props.insert("documentElement".into(), Value::Null);
3250 }
3251
3252 d.props.insert(
3253 "getElementById".into(),
3254 nv("getElementById", parsed_doc_get_element_by_id),
3255 );
3256 d.props.insert(
3257 "querySelector".into(),
3258 nv("querySelector", parsed_doc_query_selector),
3259 );
3260 d.props.insert(
3261 "querySelectorAll".into(),
3262 nv("querySelectorAll", parsed_doc_query_selector_all),
3263 );
3264 d.props.insert(
3265 "getElementsByTagName".into(),
3266 nv("getElementsByTagName", parsed_doc_get_elements_by_tag_name),
3267 );
3268 }
3269 Ok(Value::Object(doc))
3270}
3271
3272pub fn dom_parser_ctor(_it: &mut Interp, _this: Value, _args: &[Value]) -> Result<Value, Value> {
3274 let obj = Obj::plain();
3275 obj.borrow_mut().props.insert(
3276 "parseFromString".into(),
3277 nv("parseFromString", dom_parser_parse_from_string),
3278 );
3279 Ok(Value::Object(obj))
3280}
3281
3282pub fn dom_form_reset(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
3285 let Some(form_idx) = this_dom_idx(&this) else {
3286 return Ok(Value::Undefined);
3287 };
3288 let child_indices: alloc::vec::Vec<usize> = {
3289 let dom = it.dom.borrow();
3290 let mut list = alloc::vec::Vec::new();
3291 fn collect_descendants(dom: &crate::os_lib::js::dom_bridge::DomBridge, node_idx: usize, out: &mut alloc::vec::Vec<usize>) {
3292 if let Some(node) = dom.nodes.get(node_idx) {
3293 for &child in &node.children {
3294 out.push(child);
3295 collect_descendants(dom, child, out);
3296 }
3297 }
3298 }
3299 collect_descendants(&dom, form_idx, &mut list);
3300 list
3301 };
3302
3303 let mut dom = it.dom.borrow_mut();
3304 for idx in child_indices {
3305 let tag = dom.nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
3306 match tag.as_str() {
3307 "input" => {
3308 let ty = dom.get_attr(idx, "type").unwrap_or_default().to_lowercase();
3309 if ty == "checkbox" || ty == "radio" {
3310 let default_checked = dom.has_attr(idx, "checked");
3311 dom.set_attr(idx, "_live_checked", if default_checked { "true" } else { "false" });
3312 } else {
3313 let default_val = dom.get_attr(idx, "value").unwrap_or_default();
3314 dom.set_attr(idx, "_live_value", &default_val);
3315 }
3316 }
3317 "textarea" => {
3318 let default_val = dom.get_text_content(idx);
3319 dom.set_attr(idx, "_live_value", &default_val);
3320 }
3321 "select" => {
3322 dom.remove_attr(idx, "_live_value");
3323 }
3324 _ => {}
3325 }
3326 }
3327 Ok(Value::Undefined)
3328}
3329
3330pub fn dom_canvas_measure_text(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3332 let text = args.first().map(|v| v.to_js_string()).unwrap_or_default();
3333 let width = (text.len() * 8) as f64;
3334 let obj = Obj::plain();
3335 {
3336 let mut b = obj.borrow_mut();
3337 b.props.insert("width".into(), Value::Number(width));
3338 b.props.insert("actualBoundingBoxAscent".into(), Value::Number(10.0));
3339 b.props.insert("actualBoundingBoxDescent".into(), Value::Number(2.0));
3340 b.props.insert("fontBoundingBoxAscent".into(), Value::Number(10.0));
3341 b.props.insert("fontBoundingBoxDescent".into(), Value::Number(2.0));
3342 b.props.insert("actualBoundingBoxLeft".into(), Value::Number(0.0));
3343 b.props.insert("actualBoundingBoxRight".into(), Value::Number(width));
3344 }
3345 Ok(Value::Object(obj))
3346}
3347
3348pub fn parse_image_data_color_space(val: Option<&Value>) -> &'static str {
3350 if let Some(Value::Object(obj)) = val {
3351 let b = obj.borrow();
3352 if let Some(Value::Str(s)) = b.props.get("colorSpace") {
3353 if s.eq_ignore_ascii_case("display-p3") {
3354 return "display-p3";
3355 }
3356 }
3357 }
3358 "srgb"
3359}
3360
3361pub fn make_image_data_object(
3363 w: usize,
3364 h: usize,
3365 data_items: alloc::vec::Vec<Value>,
3366 color_space: &str,
3367) -> Value {
3368 let img = Obj::plain();
3369 {
3370 let mut b = img.borrow_mut();
3371 b.props.insert("width".into(), Value::Number(w as f64));
3372 b.props.insert("height".into(), Value::Number(h as f64));
3373 b.props.insert("data".into(), Value::Object(Obj::array(data_items)));
3374 b.props.insert("colorSpace".into(), Value::Str(alloc::rc::Rc::new(color_space.into())));
3375 }
3376 Value::Object(img)
3377}
3378
3379pub fn dom_canvas_create_image_data(
3385 _it: &mut Interp,
3386 _this: Value,
3387 args: &[Value],
3388) -> Result<Value, Value> {
3389 if let Some(Value::Object(other)) = args.first() {
3391 let b = other.borrow();
3392 if let (Some(w_val), Some(h_val)) = (b.props.get("width"), b.props.get("height")) {
3393 let w = (w_val.to_number().abs() as usize).max(1);
3394 let h = (h_val.to_number().abs() as usize).max(1);
3395 let cs = b
3396 .props
3397 .get("colorSpace")
3398 .map(|v| v.to_js_string())
3399 .unwrap_or_else(|| "srgb".into());
3400 let items = alloc::vec![Value::Number(0.0); w * h * 4];
3401 return Ok(make_image_data_object(w, h, items, &cs));
3402 }
3403 }
3404
3405 let w = args.first().map(|v| v.to_number().abs() as usize).unwrap_or(1).max(1);
3407 let h = args.get(1).map(|v| v.to_number().abs() as usize).unwrap_or(1).max(1);
3408 let cs = parse_image_data_color_space(args.get(2));
3409 let items = alloc::vec![Value::Number(0.0); w * h * 4];
3410 Ok(make_image_data_object(w, h, items, cs))
3411}
3412
3413pub fn dom_image_data_ctor(
3419 _it: &mut Interp,
3420 _this: Value,
3421 args: &[Value],
3422) -> Result<Value, Value> {
3423 if let Some(Value::Object(arr_obj)) = args.first() {
3424 let b = arr_obj.borrow();
3425 if let ObjKind::Array(items) = &b.kind {
3426 let w = args.get(1).map(|v| v.to_number().abs() as usize).unwrap_or(1).max(1);
3427 let h = if let Some(hv) = args.get(2) {
3428 if let Value::Number(n) = hv {
3429 (*n).abs() as usize
3430 } else {
3431 items.len() / (w * 4)
3432 }
3433 } else {
3434 items.len() / (w * 4)
3435 }.max(1);
3436 let settings_arg = if args.len() > 3 { args.get(3) } else { args.get(2) };
3437 let cs = parse_image_data_color_space(settings_arg);
3438 return Ok(make_image_data_object(w, h, items.clone(), cs));
3439 }
3440 }
3441
3442 let w = args.first().map(|v| v.to_number().abs() as usize).unwrap_or(1).max(1);
3444 let h = args.get(1).map(|v| v.to_number().abs() as usize).unwrap_or(1).max(1);
3445 let cs = parse_image_data_color_space(args.get(2));
3446 let items = alloc::vec![Value::Number(0.0); w * h * 4];
3447 Ok(make_image_data_object(w, h, items, cs))
3448}
3449
3450pub fn dom_canvas_is_point_in_path(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3451 let (subpaths, x, y, rule_str) = if let Some(pid) = args.first().and_then(path2d_id) {
3452 let subpaths = crate::os_lib::canvas2d::get_path2d_subpaths(pid).unwrap_or_default();
3453 let x = canvas_arg_f32(args, 1);
3454 let y = canvas_arg_f32(args, 2);
3455 let rule_str = args.get(3).map(|v| v.to_js_string());
3456 (subpaths, x, y, rule_str)
3457 } else {
3458 let ctx_id = canvas_ctx_id(&this);
3459 let subpaths = ctx_id
3460 .and_then(|id| crate::os_lib::canvas2d::with_context(id, |c| c.path.finish()))
3461 .unwrap_or_default();
3462 let x = canvas_arg_f32(args, 0);
3463 let y = canvas_arg_f32(args, 1);
3464 let rule_str = args.get(2).map(|v| v.to_js_string());
3465 (subpaths, x, y, rule_str)
3466 };
3467
3468 let rule = if rule_str
3469 .as_deref()
3470 .map(|s| s.eq_ignore_ascii_case("evenodd"))
3471 .unwrap_or(false)
3472 {
3473 crate::os_lib::canvas2d::FillRule::EvenOdd
3474 } else {
3475 crate::os_lib::canvas2d::FillRule::NonZero
3476 };
3477
3478 let inside = crate::os_lib::canvas2d::is_point_in_path(&subpaths, x, y, rule);
3479 Ok(Value::Bool(inside))
3480}
3481
3482pub fn dom_canvas_is_point_in_stroke(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3483 let ctx_id = canvas_ctx_id(&this);
3484
3485 let (subpaths, x, y) = if let Some(pid) = args.first().and_then(path2d_id) {
3486 let subpaths = crate::os_lib::canvas2d::get_path2d_subpaths(pid).unwrap_or_default();
3487 let x = canvas_arg_f32(args, 1);
3488 let y = canvas_arg_f32(args, 2);
3489 (subpaths, x, y)
3490 } else {
3491 let subpaths = ctx_id
3492 .and_then(|id| crate::os_lib::canvas2d::with_context(id, |c| c.path.finish()))
3493 .unwrap_or_default();
3494 let x = canvas_arg_f32(args, 0);
3495 let y = canvas_arg_f32(args, 1);
3496 (subpaths, x, y)
3497 };
3498
3499 let inside = if let Some(id) = ctx_id {
3500 crate::os_lib::canvas2d::with_context(id, |c| {
3501 canvas_sync_state(&this, c);
3502 c.is_point_in_stroke(&subpaths, x, y)
3503 })
3504 .unwrap_or(false)
3505 } else {
3506 crate::os_lib::canvas2d::is_point_in_stroke(&subpaths, x, y, 1.0)
3507 };
3508 Ok(Value::Bool(inside))
3509}
3510
3511pub fn dom_canvas_get_transform(it: &mut Interp, this: Value, _args: &[Value]) -> Result<Value, Value> {
3512 let (a, b, c, d, e, f) = canvas_ctx_id(&this)
3513 .and_then(|id| {
3514 crate::os_lib::canvas2d::with_context(id, |ctx| {
3515 (
3516 ctx.state.transform.a,
3517 ctx.state.transform.b,
3518 ctx.state.transform.c,
3519 ctx.state.transform.d,
3520 ctx.state.transform.e,
3521 ctx.state.transform.f,
3522 )
3523 })
3524 })
3525 .unwrap_or((1.0, 0.0, 0.0, 1.0, 0.0, 0.0));
3526
3527 let nums = alloc::vec![
3528 Value::Number(a as f64),
3529 Value::Number(b as f64),
3530 Value::Number(c as f64),
3531 Value::Number(d as f64),
3532 Value::Number(e as f64),
3533 Value::Number(f as f64),
3534 ];
3535 let arr = Value::Object(Obj::array(nums));
3536 dom_matrix_ctor(it, Value::Undefined, &[arr])
3537}
3538
3539pub fn dom_canvas_set_line_dash(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3540 if let Value::Object(o) = &this {
3541 let segs = args.first().cloned().unwrap_or(Value::Undefined);
3542 let items = this_items(&segs);
3543 let mut nums: alloc::vec::Vec<Value> = items.into_iter().map(|v| Value::Number(v.to_number())).collect();
3544 if nums.len() % 2 != 0 {
3545 let dup = nums.clone();
3546 nums.extend(dup);
3547 }
3548 o.borrow_mut().props.insert("_line_dash".into(), Value::Object(Obj::array(nums)));
3549 }
3550 Ok(Value::Undefined)
3551}
3552
3553pub fn dom_canvas_get_line_dash(_it: &mut Interp, this: Value, _args: &[Value]) -> Result<Value, Value> {
3554 if let Value::Object(o) = &this {
3555 if let Some(Value::Object(arr)) = o.borrow().props.get("_line_dash") {
3556 let items = this_items(&Value::Object(arr.clone()));
3557 return Ok(Value::Object(Obj::array(items)));
3558 }
3559 }
3560 Ok(Value::Object(Obj::array(alloc::vec![])))
3561}
3562
3563pub fn dom_canvas_round_rect(it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3564 canvas_round_rect(it, this, args)
3565}
3566
3567pub fn dom_canvas_reset(_it: &mut Interp, this: Value, _args: &[Value]) -> Result<Value, Value> {
3569 if let Value::Object(ref ctx) = this {
3570 let mut b = ctx.borrow_mut();
3571 b.props.insert("fillStyle".into(), Value::str("#000000"));
3572 b.props.insert("strokeStyle".into(), Value::str("#000000"));
3573 b.props.insert("lineWidth".into(), Value::Number(1.0));
3574 b.props.insert("lineCap".into(), Value::str("butt"));
3575 b.props.insert("lineJoin".into(), Value::str("miter"));
3576 b.props.insert("miterLimit".into(), Value::Number(10.0));
3577 b.props.insert("lineDashOffset".into(), Value::Number(0.0));
3578 b.props.insert("font".into(), Value::str("10px sans-serif"));
3579 b.props.insert("textAlign".into(), Value::str("start"));
3580 b.props.insert("textBaseline".into(), Value::str("alphabetic"));
3581 b.props.insert("globalAlpha".into(), Value::Number(1.0));
3582 b.props.insert("globalCompositeOperation".into(), Value::str("source-over"));
3583 b.props.insert("shadowBlur".into(), Value::Number(0.0));
3584 b.props.insert("shadowColor".into(), Value::str("rgba(0, 0, 0, 0)"));
3585 b.props.insert("shadowOffsetX".into(), Value::Number(0.0));
3586 b.props.insert("shadowOffsetY".into(), Value::Number(0.0));
3587 b.props.insert("imageSmoothingEnabled".into(), Value::Bool(true));
3588 b.props.insert("direction".into(), Value::str("inherit"));
3589 b.props.insert("filter".into(), Value::str("none"));
3590 b.props.shift_remove("_line_dash");
3591 }
3592 Ok(Value::Undefined)
3593}
3594
3595fn dom_matrix_arg_to_matrix(it: &mut Interp, val: &Value) -> Option<crate::os_lib::canvas2d::Matrix> {
3596 if let Value::Object(_) = val {
3597 let a = it.get_property(val, "a").ok().map(|v| v.to_number()).unwrap_or(1.0);
3598 let b = it.get_property(val, "b").ok().map(|v| v.to_number()).unwrap_or(0.0);
3599 let c = it.get_property(val, "c").ok().map(|v| v.to_number()).unwrap_or(0.0);
3600 let d = it.get_property(val, "d").ok().map(|v| v.to_number()).unwrap_or(1.0);
3601 let e = it.get_property(val, "e").ok().map(|v| v.to_number()).unwrap_or(0.0);
3602 let f = it.get_property(val, "f").ok().map(|v| v.to_number()).unwrap_or(0.0);
3603
3604 if a.is_finite() && b.is_finite() && c.is_finite() && d.is_finite() && e.is_finite() && f.is_finite() {
3605 Some(crate::os_lib::canvas2d::Matrix {
3606 a: a as f32,
3607 b: b as f32,
3608 c: c as f32,
3609 d: d as f32,
3610 e: e as f32,
3611 f: f as f32,
3612 })
3613 } else {
3614 None
3615 }
3616 } else {
3617 None
3618 }
3619}
3620
3621pub fn dom_path2d_add_path(it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3622 if let Some(id) = path2d_id(&this) {
3623 if let Some(src_arg) = args.first() {
3624 if let Some(src_id) = path2d_id(src_arg) {
3625 let transform = args.get(1).and_then(|v| dom_matrix_arg_to_matrix(it, v));
3626 crate::os_lib::canvas2d::add_path_to_path2d(id, src_id, transform.as_ref());
3627 }
3628 }
3629 }
3630 Ok(Value::Undefined)
3631}
3632
3633pub fn dom_path2d_close_path(_it: &mut Interp, this: Value, _args: &[Value]) -> Result<Value, Value> {
3634 if let Some(id) = path2d_id(&this) {
3635 crate::os_lib::canvas2d::with_path2d(id, |p| p.close_path());
3636 }
3637 Ok(Value::Undefined)
3638}
3639
3640pub fn dom_path2d_move_to(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3641 if let Some(id) = path2d_id(&this) {
3642 let (x, y) = (canvas_arg_f32(args, 0), canvas_arg_f32(args, 1));
3643 crate::os_lib::canvas2d::with_path2d(id, |p| p.move_to(&crate::os_lib::canvas2d::Matrix::identity(), x, y));
3644 }
3645 Ok(Value::Undefined)
3646}
3647
3648pub fn dom_path2d_line_to(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
3649 if let Some(id) = path2d_id(&this) {
3650 let (x, y) = (canvas_arg_f32(args, 0), canvas_arg_f32(args, 1));
3651 crate::os_lib::canvas2d::with_path2d(id, |p| p.line_to(&crate::os_lib::canvas2d::Matrix::identity(), x, y));
3652 }
3653 Ok(Value::Undefined)
3654}
3655
3656pub fn dom_path2d_arc(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3657 if let Some(id) = path2d_id(&this) {
3658 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3659 let r = canvas_arg_f32(a, 2);
3660 let start = canvas_arg_f32(a, 3);
3661 let end = canvas_arg_f32(a, 4);
3662 let ccw = a.get(5).map(|v| v.truthy()).unwrap_or(false);
3663 crate::os_lib::canvas2d::with_path2d(id, |p| {
3664 p.arc(&crate::os_lib::canvas2d::Matrix::identity(), cx, cy, r, start, end, ccw);
3665 });
3666 }
3667 Ok(Value::Undefined)
3668}
3669
3670pub fn dom_path2d_arc_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3671 if let Some(id) = path2d_id(&this) {
3672 let (x1, y1) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3673 let (x2, y2) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3674 let radius = canvas_arg_f32(a, 4);
3675 crate::os_lib::canvas2d::with_path2d(id, |p| {
3676 p.arc_to(&crate::os_lib::canvas2d::Matrix::identity(), x1, y1, x2, y2, radius);
3677 });
3678 }
3679 Ok(Value::Undefined)
3680}
3681
3682pub fn dom_path2d_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3683 if let Some(id) = path2d_id(&this) {
3684 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3685 let (w, h) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3686 crate::os_lib::canvas2d::with_path2d(id, |p| {
3687 p.rect(&crate::os_lib::canvas2d::Matrix::identity(), x, y, w, h);
3688 });
3689 }
3690 Ok(Value::Undefined)
3691}
3692
3693fn parse_round_rect_radii(it: &Interp, radii_val: Option<&Value>) -> Result<[f32; 4], Value> {
3699 match radii_val {
3700 None | Some(Value::Undefined) => Ok([0.0; 4]),
3701 Some(Value::Object(o)) => {
3702 let b = o.borrow();
3703 if let ObjKind::Array(items) = &b.kind {
3704 if items.is_empty() || items.len() > 4 {
3705 return Err(set_error_props(
3706 it,
3707 Value::Undefined,
3708 alloc::format!(
3709 "Failed to execute 'roundRect': 1 to 4 radii expected, got {}",
3710 items.len()
3711 ),
3712 "RangeError",
3713 ));
3714 }
3715 let mut vals = alloc::vec::Vec::with_capacity(items.len());
3716 for item in items {
3717 let n = item.to_number();
3718 let f = if n.is_finite() { n as f32 } else { 0.0 };
3719 if f < 0.0 {
3720 return Err(set_error_props(
3721 it,
3722 Value::Undefined,
3723 alloc::format!(
3724 "Failed to execute 'roundRect': radius must be non-negative, got {f}"
3725 ),
3726 "RangeError",
3727 ));
3728 }
3729 vals.push(f);
3730 }
3731 let radii = match vals.len() {
3732 1 => [vals[0]; 4],
3733 2 => [vals[0], vals[1], vals[0], vals[1]],
3734 3 => [vals[0], vals[1], vals[2], vals[1]],
3735 4 => [vals[0], vals[1], vals[2], vals[3]],
3736 _ => [0.0; 4],
3737 };
3738 Ok(radii)
3739 } else {
3740 let n = radii_val.map_or(f64::NAN, |v| v.to_number());
3741 let f = if n.is_finite() { n as f32 } else { 0.0 };
3742 if f < 0.0 {
3743 return Err(set_error_props(
3744 it,
3745 Value::Undefined,
3746 alloc::format!(
3747 "Failed to execute 'roundRect': radius must be non-negative, got {f}"
3748 ),
3749 "RangeError",
3750 ));
3751 }
3752 Ok([f; 4])
3753 }
3754 }
3755 Some(v) => {
3756 let n = v.to_number();
3757 let f = if n.is_finite() { n as f32 } else { 0.0 };
3758 if f < 0.0 {
3759 return Err(set_error_props(
3760 it,
3761 Value::Undefined,
3762 alloc::format!(
3763 "Failed to execute 'roundRect': radius must be non-negative, got {f}"
3764 ),
3765 "RangeError",
3766 ));
3767 }
3768 Ok([f; 4])
3769 }
3770 }
3771}
3772
3773pub fn dom_path2d_round_rect(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3774 if let Some(id) = path2d_id(&this) {
3775 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3776 let (w, h) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3777 let radii = parse_round_rect_radii(it, a.get(4))?;
3778 crate::os_lib::canvas2d::with_path2d(id, |p| {
3779 p.round_rect(&crate::os_lib::canvas2d::Matrix::identity(), x, y, w, h, radii);
3780 });
3781 }
3782 Ok(Value::Undefined)
3783}
3784
3785pub fn dom_path2d_ellipse(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3786 if let Some(id) = path2d_id(&this) {
3787 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3788 let (rx, ry) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3789 let rot = canvas_arg_f32(a, 4);
3790 let start = canvas_arg_f32(a, 5);
3791 let end = canvas_arg_f32(a, 6);
3792 let ccw = a.get(7).map(|v| v.truthy()).unwrap_or(false);
3793 crate::os_lib::canvas2d::with_path2d(id, |p| {
3794 p.ellipse(&crate::os_lib::canvas2d::Matrix::identity(), cx, cy, rx, ry, rot, start, end, ccw);
3795 });
3796 }
3797 Ok(Value::Undefined)
3798}
3799
3800pub fn dom_path2d_bezier_curve_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3801 if let Some(id) = path2d_id(&this) {
3802 let (c1x, c1y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3803 let (c2x, c2y) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3804 let (x, y) = (canvas_arg_f32(a, 4), canvas_arg_f32(a, 5));
3805 crate::os_lib::canvas2d::with_path2d(id, |p| {
3806 p.bezier_curve_to(&crate::os_lib::canvas2d::Matrix::identity(), c1x, c1y, c2x, c2y, x, y);
3807 });
3808 }
3809 Ok(Value::Undefined)
3810}
3811
3812pub fn dom_path2d_quadratic_curve_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3813 if let Some(id) = path2d_id(&this) {
3814 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
3815 let (x, y) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
3816 crate::os_lib::canvas2d::with_path2d(id, |p| {
3817 p.quadratic_curve_to(&crate::os_lib::canvas2d::Matrix::identity(), cx, cy, x, y);
3818 });
3819 }
3820 Ok(Value::Undefined)
3821}
3822
3823pub fn dom_path2d_ctor(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
3825 let p = Obj::plain();
3826 let id = if let Some(arg) = args.first() {
3827 if let Some(src_id) = path2d_id(arg) {
3828 crate::os_lib::canvas2d::clone_path2d(src_id)
3829 } else if let Value::Str(s) = arg {
3830 crate::os_lib::canvas2d::create_path2d(Some(s))
3831 } else if !matches!(arg, Value::Undefined | Value::Null) {
3832 let s = arg.to_js_string();
3833 crate::os_lib::canvas2d::create_path2d(Some(&s))
3834 } else {
3835 crate::os_lib::canvas2d::create_path2d(None)
3836 }
3837 } else {
3838 crate::os_lib::canvas2d::create_path2d(None)
3839 };
3840 {
3841 let mut b = p.borrow_mut();
3842 b.props.insert("_path2d_id".into(), Value::Number(id as f64));
3843 b.props.insert("addPath".into(), nv("addPath", dom_path2d_add_path));
3844 b.props.insert("closePath".into(), nv("closePath", dom_path2d_close_path));
3845 b.props.insert("moveTo".into(), nv("moveTo", dom_path2d_move_to));
3846 b.props.insert("lineTo".into(), nv("lineTo", dom_path2d_line_to));
3847 b.props.insert("arc".into(), nv("arc", dom_path2d_arc));
3848 b.props.insert("arcTo".into(), nv("arcTo", dom_path2d_arc_to));
3849 b.props.insert("rect".into(), nv("rect", dom_path2d_rect));
3850 b.props.insert("roundRect".into(), nv("roundRect", dom_path2d_round_rect));
3851 b.props.insert("ellipse".into(), nv("ellipse", dom_path2d_ellipse));
3852 b.props.insert("bezierCurveTo".into(), nv("bezierCurveTo", dom_path2d_bezier_curve_to));
3853 b.props.insert("quadraticCurveTo".into(), nv("quadraticCurveTo", dom_path2d_quadratic_curve_to));
3854 }
3855 Ok(Value::Object(p))
3856}
3857
3858pub fn dom_canvas_create_linear_gradient(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
3860 let x0 = canvas_arg_f32(a, 0);
3861 let y0 = canvas_arg_f32(a, 1);
3862 let x1 = canvas_arg_f32(a, 2);
3863 let y1 = canvas_arg_f32(a, 3);
3864 let id = crate::os_lib::canvas2d::create_linear_gradient(x0, y0, x1, y1);
3865 let grad = Obj::plain();
3866 {
3867 let mut b = grad.borrow_mut();
3868 b.props.insert("_gradient_id".into(), Value::Number(id as f64));
3869 b.props.insert("addColorStop".into(), nv("addColorStop", dom_gradient_add_color_stop));
3870 }
3871 Ok(Value::Object(grad))
3872}
3873
3874pub fn dom_canvas_create_radial_gradient(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
3876 let x0 = canvas_arg_f32(a, 0);
3877 let y0 = canvas_arg_f32(a, 1);
3878 let r0 = canvas_arg_f32(a, 2);
3879 let x1 = canvas_arg_f32(a, 3);
3880 let y1 = canvas_arg_f32(a, 4);
3881 let r1 = canvas_arg_f32(a, 5);
3882 let id = crate::os_lib::canvas2d::create_radial_gradient(x0, y0, r0, x1, y1, r1);
3883 let grad = Obj::plain();
3884 {
3885 let mut b = grad.borrow_mut();
3886 b.props.insert("_gradient_id".into(), Value::Number(id as f64));
3887 b.props.insert("addColorStop".into(), nv("addColorStop", dom_gradient_add_color_stop));
3888 }
3889 Ok(Value::Object(grad))
3890}
3891
3892pub fn dom_gradient_add_color_stop(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
3894 let id = match &this {
3895 Value::Object(o) => {
3896 let b = o.borrow();
3897 b.props.get("_gradient_id").and_then(|v| match v {
3898 Value::Number(n) => Some(*n as u32),
3899 _ => None,
3900 })
3901 }
3902 _ => None,
3903 };
3904 let Some(id) = id else {
3905 return Ok(Value::Undefined);
3906 };
3907 let offset = canvas_arg_f32(a, 0);
3908 let color_str = a.get(1).map(|v| v.to_js_string()).unwrap_or_default();
3909 let color = crate::os_lib::css::parse_color(&color_str).unwrap_or(0xFF00_0000);
3910 crate::os_lib::canvas2d::add_color_stop_to_gradient(id, offset, color);
3911 Ok(Value::Undefined)
3912}
3913
3914pub fn dom_canvas_create_conic_gradient(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
3916 let start_angle = canvas_arg_f32(a, 0);
3917 let cx = canvas_arg_f32(a, 1);
3918 let cy = canvas_arg_f32(a, 2);
3919 let id = crate::os_lib::canvas2d::create_conic_gradient(start_angle, cx, cy);
3920 let grad = Obj::plain();
3921 {
3922 let mut b = grad.borrow_mut();
3923 b.props.insert("_gradient_id".into(), Value::Number(id as f64));
3924 b.props.insert("addColorStop".into(), nv("addColorStop", dom_gradient_add_color_stop));
3925 }
3926 Ok(Value::Object(grad))
3927}
3928
3929fn extract_pattern_pixels(it: &mut Interp, v: &Value) -> Option<(u32, u32, alloc::vec::Vec<u32>)> {
3931 let Value::Object(o) = v else {
3932 return None;
3933 };
3934
3935 {
3937 let b = o.borrow();
3938 if let (Some(Value::Number(w_num)), Some(Value::Number(h_num)), Some(Value::Object(data_arr))) =
3939 (b.props.get("width"), b.props.get("height"), b.props.get("data"))
3940 {
3941 let w = *w_num as u32;
3942 let h = *h_num as u32;
3943 if w > 0 && h > 0 {
3944 let ab = data_arr.borrow();
3945 let items_owned: alloc::vec::Vec<Value> = match &ab.kind {
3947 ObjKind::Bytes(st) => st.values(),
3948 _ => alloc::vec::Vec::new(),
3949 };
3950 let items_ref = match &ab.kind {
3951 ObjKind::Array(items) => Some(items),
3952 ObjKind::Bytes(_) => Some(&items_owned),
3953 _ => None,
3954 };
3955 if let Some(items) = items_ref {
3956 let total = (w * h) as usize;
3957 let mut pixels = alloc::vec::Vec::with_capacity(total);
3958 for chunk in items.chunks(4) {
3959 if chunk.len() == 4 {
3960 let r = chunk[0].to_number().clamp(0.0, 255.0) as u32;
3961 let g = chunk[1].to_number().clamp(0.0, 255.0) as u32;
3962 let b = chunk[2].to_number().clamp(0.0, 255.0) as u32;
3963 let a = chunk[3].to_number().clamp(0.0, 255.0) as u32;
3964 pixels.push((a << 24) | (r << 16) | (g << 8) | b);
3965 } else {
3966 pixels.push(0);
3967 }
3968 }
3969 if pixels.len() == total {
3970 return Some((w, h, pixels));
3971 }
3972 }
3973 }
3974 }
3975 }
3976
3977 let idx = match o.borrow().kind {
3979 ObjKind::DomElement(i) => Some(i),
3980 _ => None,
3981 }?;
3982 let (tag, src, cid) = {
3983 let dom = it.dom.borrow();
3984 let tag = dom.nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
3985 let src = dom.get_attr(idx, "src").unwrap_or_default();
3986 let cid = dom
3987 .get_attr(idx, "_c2d_id")
3988 .and_then(|s| s.parse::<u32>().ok());
3989 (tag, src, cid)
3990 };
3991
3992 if tag == "canvas" {
3993 let id = cid?;
3994 return crate::os_lib::canvas2d::with_context(id, |c| {
3995 let (w, h) = (c.surface.width, c.surface.height);
3996 if w == 0 || h == 0 {
3997 None
3998 } else {
3999 Some((w, h, c.surface.pixels.clone()))
4000 }
4001 }).flatten();
4002 }
4003
4004 if let Some(img) = crate::os_lib::web_engine::shared_image(&src) {
4005 let (w, h) = (img.width, img.height);
4006 if w > 0 && h > 0 {
4007 let mut pixels = alloc::vec::Vec::with_capacity((w * h) as usize);
4008 for chunk in img.rgba.chunks_exact(4) {
4009 let r = chunk[0] as u32;
4010 let g = chunk[1] as u32;
4011 let b = chunk[2] as u32;
4012 let a = chunk[3] as u32;
4013 pixels.push((a << 24) | (r << 16) | (g << 8) | b);
4014 }
4015 return Some((w, h, pixels));
4016 }
4017 }
4018
4019 None
4020}
4021
4022pub fn dom_pattern_set_transform(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4024 let Value::Object(o) = &this else {
4025 return Ok(Value::Undefined);
4026 };
4027 let pid = match o.borrow().props.get("_pattern_id") {
4028 Some(Value::Number(n)) if *n >= 0.0 => *n as u32,
4029 _ => return Ok(Value::Undefined),
4030 };
4031 let matrix = if let Some(arg) = a.first() {
4032 dom_matrix_arg_to_matrix(it, arg).unwrap_or_else(crate::os_lib::canvas2d::Matrix::identity)
4033 } else {
4034 crate::os_lib::canvas2d::Matrix::identity()
4035 };
4036 crate::os_lib::canvas2d::set_pattern_transform(pid, matrix);
4037 Ok(Value::Undefined)
4038}
4039
4040pub fn dom_canvas_create_pattern(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
4042 let Some(image_arg) = a.first() else {
4043 return Ok(Value::Null);
4044 };
4045 let Some((w, h, pixels)) = extract_pattern_pixels(it, image_arg) else {
4046 return Ok(Value::Null);
4047 };
4048 let rep_str = a.get(1).map(|v| v.to_js_string()).unwrap_or_else(|| String::from("repeat"));
4049 let repetition = crate::os_lib::canvas2d::parse_repetition(&rep_str);
4050 let id = crate::os_lib::canvas2d::create_pattern(w, h, pixels, repetition);
4051
4052 let pat = Obj::plain();
4053 {
4054 let mut b = pat.borrow_mut();
4055 b.props.insert("_pattern_id".into(), Value::Number(id as f64));
4056 b.props.insert("setTransform".into(), nv("setTransform", dom_pattern_set_transform));
4057 }
4058 Ok(Value::Object(pat))
4059}
4060
4061pub fn dom_canvas_get_context(_it: &mut Interp, this: Value, args: &[Value]) -> Result<Value, Value> {
4063 let ctx_type = args.first().map(|v| v.to_js_string()).unwrap_or_default();
4064 if ctx_type.eq_ignore_ascii_case("2d") {
4065 let (cw, ch) = match &this {
4069 Value::Object(o) => {
4070 let kind = o.borrow().kind.clone();
4071 if let ObjKind::DomElement(idx) = kind {
4072 let d = _it.dom.borrow();
4073 let w = d
4074 .get_attr(idx, "width")
4075 .and_then(|s| s.trim().parse::<u32>().ok())
4076 .unwrap_or(300);
4077 let h = d
4078 .get_attr(idx, "height")
4079 .and_then(|s| s.trim().parse::<u32>().ok())
4080 .unwrap_or(150);
4081 (w, h)
4082 } else {
4083 (300, 150)
4084 }
4085 }
4086 _ => (300, 150),
4087 };
4088 let c2d_id = crate::os_lib::canvas2d::create_context(cw, ch);
4089 if let (Some(id), Value::Object(o)) = (c2d_id, &this) {
4093 let kind = o.borrow().kind.clone();
4094 if let ObjKind::DomElement(idx) = kind {
4095 _it.dom
4096 .borrow_mut()
4097 .set_attr(idx, "_c2d_id", &alloc::format!("{}", id));
4098 }
4099 }
4100 let ctx = Obj::plain();
4101 {
4102 let mut b = ctx.borrow_mut();
4103 b.props.insert("canvas".into(), this);
4104 b.props.insert("fillStyle".into(), Value::str("#000000"));
4105 b.props.insert("strokeStyle".into(), Value::str("#000000"));
4106 b.props.insert("lineWidth".into(), Value::Number(1.0));
4107 b.props.insert("lineCap".into(), Value::str("butt"));
4108 b.props.insert("lineJoin".into(), Value::str("miter"));
4109 b.props.insert("miterLimit".into(), Value::Number(10.0));
4110 b.props.insert("lineDashOffset".into(), Value::Number(0.0));
4111 b.props.insert("font".into(), Value::str("10px sans-serif"));
4112 b.props.insert("textAlign".into(), Value::str("start"));
4113 b.props.insert("textBaseline".into(), Value::str("alphabetic"));
4114 b.props.insert("globalAlpha".into(), Value::Number(1.0));
4115 b.props.insert("globalCompositeOperation".into(), Value::str("source-over"));
4116 b.props.insert("shadowBlur".into(), Value::Number(0.0));
4117 b.props.insert("shadowColor".into(), Value::str("rgba(0, 0, 0, 0)"));
4118 b.props.insert("shadowOffsetX".into(), Value::Number(0.0));
4119 b.props.insert("shadowOffsetY".into(), Value::Number(0.0));
4120 b.props.insert("imageSmoothingEnabled".into(), Value::Bool(true));
4121 b.props.insert("direction".into(), Value::str("inherit"));
4122 b.props.insert("filter".into(), Value::str("none"));
4123 if let Some(id) = c2d_id {
4128 b.props.insert("_c2d_id".into(), Value::Number(id as f64));
4129 }
4130
4131 b.props.insert("fillRect".into(), nv("fillRect", canvas_fill_rect));
4139 b.props.insert("strokeRect".into(), nv("strokeRect", canvas_stroke_rect));
4140 b.props.insert("clearRect".into(), nv("clearRect", canvas_clear_rect));
4141
4142 b.props.insert("beginPath".into(), nv("beginPath", canvas_begin_path));
4143 b.props.insert("closePath".into(), nv("closePath", canvas_close_path));
4144 b.props.insert("moveTo".into(), nv("moveTo", canvas_move_to));
4145 b.props.insert("lineTo".into(), nv("lineTo", canvas_line_to));
4146 b.props.insert("arc".into(), nv("arc", canvas_arc));
4147 b.props.insert("arcTo".into(), nv("arcTo", canvas_arc_to));
4148 b.props.insert("bezierCurveTo".into(), nv("bezierCurveTo", canvas_bezier_curve_to));
4149 b.props.insert("quadraticCurveTo".into(), nv("quadraticCurveTo", canvas_quadratic_curve_to));
4150 b.props.insert("ellipse".into(), nv("ellipse", canvas_ellipse));
4151 b.props.insert("rect".into(), nv("rect", canvas_rect));
4152 b.props.insert("roundRect".into(), nv("roundRect", canvas_round_rect));
4153
4154 b.props.insert("fill".into(), nv("fill", canvas_fill));
4155 b.props.insert("stroke".into(), nv("stroke", canvas_stroke));
4156 b.props.insert("clip".into(), nv("clip", canvas_clip));
4157
4158 b.props.insert("fillText".into(), nv("fillText", canvas_fill_text));
4159 b.props.insert("strokeText".into(), nv("strokeText", canvas_stroke_text));
4160 b.props.insert("measureText".into(), nv("measureText", canvas_measure_text));
4161
4162 b.props.insert("drawImage".into(), nv("drawImage", canvas_draw_image));
4163 b.props.insert("getImageData".into(), nv("getImageData", canvas_get_image_data));
4164 b.props.insert("createImageData".into(), nv("createImageData", dom_canvas_create_image_data));
4165 b.props.insert("putImageData".into(), nv("putImageData", canvas_put_image_data));
4166 b.props.insert("isPointInPath".into(), nv("isPointInPath", dom_canvas_is_point_in_path));
4167 b.props.insert("isPointInStroke".into(), nv("isPointInStroke", dom_canvas_is_point_in_stroke));
4168 b.props.insert("getTransform".into(), nv("getTransform", dom_canvas_get_transform));
4169 b.props.insert("setLineDash".into(), nv("setLineDash", dom_canvas_set_line_dash));
4170 b.props.insert("getLineDash".into(), nv("getLineDash", dom_canvas_get_line_dash));
4171 b.props.insert("reset".into(), nv("reset", dom_canvas_reset));
4172
4173 b.props.insert("createLinearGradient".into(), nv("createLinearGradient", dom_canvas_create_linear_gradient));
4174 b.props.insert("createRadialGradient".into(), nv("createRadialGradient", dom_canvas_create_radial_gradient));
4175 b.props.insert("createConicGradient".into(), nv("createConicGradient", dom_canvas_create_conic_gradient));
4176 b.props.insert("createPattern".into(), nv("createPattern", dom_canvas_create_pattern));
4177
4178 b.props.insert("save".into(), nv("save", canvas_save));
4179 b.props.insert("restore".into(), nv("restore", canvas_restore));
4180 b.props.insert("scale".into(), nv("scale", canvas_scale));
4181 b.props.insert("rotate".into(), nv("rotate", canvas_rotate));
4182 b.props.insert("translate".into(), nv("translate", canvas_translate));
4183 b.props.insert("transform".into(), nv("transform", canvas_transform));
4184 b.props.insert("setTransform".into(), nv("setTransform", canvas_set_transform));
4185 b.props.insert("resetTransform".into(), nv("resetTransform", canvas_reset_transform));
4186 }
4187 return Ok(Value::Object(ctx));
4188 }
4189 Ok(Value::Null)
4193}
4194
4195pub fn dom_canvas_to_data_url(_it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
4197 let mime = args.first().map(|v| v.to_js_string()).unwrap_or_else(|| String::from("image/png"));
4198 let header = if mime.eq_ignore_ascii_case("image/jpeg") {
4199 "data:image/jpeg;base64,"
4200 } else if mime.eq_ignore_ascii_case("image/webp") {
4201 "data:image/webp;base64,"
4202 } else {
4203 "data:image/png;base64,"
4204 };
4205 let dummy_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
4207 Ok(Value::str(alloc::format!("{}{}", header, dummy_base64)))
4208}
4209
4210pub fn dom_canvas_to_blob(it: &mut Interp, _this: Value, args: &[Value]) -> Result<Value, Value> {
4212 let cb = args.first().cloned().unwrap_or(Value::Undefined);
4213 let mime = args.get(1).map(|v| v.to_js_string()).unwrap_or_else(|| String::from("image/png"));
4214 if matches!(cb, Value::Object(_)) {
4215 let blob = Obj::plain();
4216 {
4217 let mut b = blob.borrow_mut();
4218 b.props.insert("size".into(), Value::Number(68.0));
4219 b.props.insert("type".into(), Value::str(mime));
4220 }
4221 it.call_value(&cb, Value::Undefined, &[Value::Object(blob)])?;
4222 }
4223 Ok(Value::Undefined)
4224}
4225
4226
4227fn path2d_id(this: &Value) -> Option<u32> {
4236 match this {
4237 Value::Object(o) => match o.borrow().props.get("_path2d_id") {
4238 Some(Value::Number(n)) if *n >= 0.0 => Some(*n as u32),
4239 _ => None,
4240 },
4241 _ => None,
4242 }
4243}
4244
4245fn canvas_ctx_id(this: &Value) -> Option<u32> {
4247 match this {
4248 Value::Object(o) => match o.borrow().props.get("_c2d_id") {
4249 Some(Value::Number(n)) if *n >= 0.0 => Some(*n as u32),
4250 _ => None,
4251 },
4252 _ => None,
4253 }
4254}
4255
4256fn canvas_arg_f32(a: &[Value], i: usize) -> f32 {
4258 let v = a.get(i).map(|v| v.to_number()).unwrap_or(0.0);
4259 if v.is_finite() {
4260 v as f32
4261 } else {
4262 0.0
4263 }
4264}
4265
4266fn canvas_style_argb(this: &Value, key: &str) -> u32 {
4272 let s = match this {
4273 Value::Object(o) => o
4274 .borrow()
4275 .props
4276 .get(key)
4277 .map(|v| v.to_js_string())
4278 .unwrap_or_default(),
4279 _ => String::new(),
4280 };
4281 crate::os_lib::css::parse_color(&s).unwrap_or(0xFF00_0000)
4282}
4283
4284fn canvas_sync_state(this: &Value, ctx: &mut crate::os_lib::canvas2d::Canvas2dContext) {
4289 ctx.state.fill = canvas_style_argb(this, "fillStyle");
4290 ctx.state.stroke = canvas_style_argb(this, "strokeStyle");
4291 if let Value::Object(o) = this {
4292 let b = o.borrow();
4293 if let Some(Value::Number(n)) = b.props.get("globalAlpha") {
4294 if n.is_finite() {
4295 ctx.state.global_alpha = (*n as f32).clamp(0.0, 1.0);
4296 }
4297 }
4298 if let Some(Value::Number(n)) = b.props.get("lineWidth") {
4299 if n.is_finite() && *n > 0.0 {
4300 ctx.state.line_width = *n as f32;
4301 }
4302 }
4303 if let Some(Value::Str(s)) = b.props.get("lineCap") {
4305 if let Some(v) = crate::os_lib::canvas2d::parse_line_cap(s) {
4306 ctx.state.line_cap = v;
4307 }
4308 }
4309 if let Some(Value::Str(s)) = b.props.get("lineJoin") {
4310 if let Some(v) = crate::os_lib::canvas2d::parse_line_join(s) {
4311 ctx.state.line_join = v;
4312 }
4313 }
4314 if let Some(Value::Number(n)) = b.props.get("miterLimit") {
4315 if n.is_finite() && *n > 0.0 {
4316 ctx.state.miter_limit = *n as f32;
4317 }
4318 }
4319 ctx.state.line_dash.clear();
4322 if let Some(Value::Object(arr)) = b.props.get("_line_dash") {
4323 let ab = arr.borrow();
4324 if let ObjKind::Array(items) = &ab.kind {
4325 for v in items {
4326 ctx.state.line_dash.push(v.to_number() as f32);
4327 }
4328 }
4329 }
4330 if let Some(Value::Number(n)) = b.props.get("lineDashOffset") {
4331 if n.is_finite() {
4332 ctx.state.line_dash_offset = *n as f32;
4333 }
4334 }
4335 if let Some(Value::Str(s)) = b.props.get("fillStyle") {
4336 ctx.state.fill_str = (**s).clone();
4337 ctx.state.fill_gradient = None;
4338 ctx.state.fill_pattern = None;
4339 } else if let Some(Value::Object(style_obj)) = b.props.get("fillStyle") {
4340 let sb = style_obj.borrow();
4341 if let Some(Value::Number(gid)) = sb.props.get("_gradient_id") {
4342 ctx.state.fill_gradient = crate::os_lib::canvas2d::get_gradient(*gid as u32);
4343 ctx.state.fill_pattern = None;
4344 } else if let Some(Value::Number(pid)) = sb.props.get("_pattern_id") {
4345 ctx.state.fill_pattern = crate::os_lib::canvas2d::get_pattern(*pid as u32);
4346 ctx.state.fill_gradient = None;
4347 } else {
4348 ctx.state.fill_gradient = None;
4349 ctx.state.fill_pattern = None;
4350 }
4351 } else {
4352 ctx.state.fill_gradient = None;
4353 ctx.state.fill_pattern = None;
4354 }
4355 if let Some(Value::Str(s)) = b.props.get("strokeStyle") {
4356 ctx.state.stroke_str = (**s).clone();
4357 ctx.state.stroke_gradient = None;
4358 ctx.state.stroke_pattern = None;
4359 } else if let Some(Value::Object(style_obj)) = b.props.get("strokeStyle") {
4360 let sb = style_obj.borrow();
4361 if let Some(Value::Number(gid)) = sb.props.get("_gradient_id") {
4362 ctx.state.stroke_gradient = crate::os_lib::canvas2d::get_gradient(*gid as u32);
4363 ctx.state.stroke_pattern = None;
4364 } else if let Some(Value::Number(pid)) = sb.props.get("_pattern_id") {
4365 ctx.state.stroke_pattern = crate::os_lib::canvas2d::get_pattern(*pid as u32);
4366 ctx.state.stroke_gradient = None;
4367 } else {
4368 ctx.state.stroke_gradient = None;
4369 ctx.state.stroke_pattern = None;
4370 }
4371 } else {
4372 ctx.state.stroke_gradient = None;
4373 ctx.state.stroke_pattern = None;
4374 }
4375 if let Some(Value::Str(s)) = b.props.get("font") {
4376 ctx.state.font_str = (**s).clone();
4377 }
4378 if let Some(Value::Str(s)) = b.props.get("textAlign") {
4379 ctx.state.text_align_str = (**s).clone();
4380 }
4381 if let Some(Value::Str(s)) = b.props.get("textBaseline") {
4382 ctx.state.text_baseline_str = (**s).clone();
4383 }
4384 if let Some(Value::Bool(sm)) = b.props.get("imageSmoothingEnabled") {
4385 ctx.state.image_smoothing_enabled = *sm;
4386 }
4387 if let Some(Value::Str(s)) = b.props.get("shadowColor") {
4388 ctx.state.shadow_color_str = (**s).clone();
4389 if let Some(c) = crate::os_lib::css::parse_color(s) {
4390 ctx.state.shadow_color = c;
4391 }
4392 }
4393 if let Some(Value::Number(n)) = b.props.get("shadowBlur") {
4394 if n.is_finite() && *n >= 0.0 {
4395 ctx.state.shadow_blur = *n as f32;
4396 }
4397 }
4398 if let Some(Value::Number(n)) = b.props.get("shadowOffsetX") {
4399 if n.is_finite() {
4400 ctx.state.shadow_offset_x = *n as f32;
4401 }
4402 }
4403 if let Some(Value::Number(n)) = b.props.get("shadowOffsetY") {
4404 if n.is_finite() {
4405 ctx.state.shadow_offset_y = *n as f32;
4406 }
4407 }
4408 if let Some(Value::Str(s)) = b.props.get("filter") {
4409 ctx.state.filter = (**s).clone();
4410 }
4411 if let Some(Value::Str(s)) = b.props.get("globalCompositeOperation") {
4412 ctx.state.global_composite_operation_str = (**s).clone();
4413 if let Some(op) = crate::os_lib::canvas2d::parse_composite_operation(s) {
4414 ctx.state.global_composite_operation = op;
4415 }
4416 }
4417 if let Some(Value::Str(s)) = b.props.get("direction") {
4418 if let Some(dir) = crate::os_lib::canvas2d::parse_canvas_direction(s) {
4419 ctx.state.direction = dir;
4420 ctx.state.direction_str = (**s).clone();
4421 }
4422 }
4423 }
4424}
4425
4426pub fn canvas_fill_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4427 if let Some(id) = canvas_ctx_id(&this) {
4428 let (x, y, w, h) = (
4429 canvas_arg_f32(a, 0),
4430 canvas_arg_f32(a, 1),
4431 canvas_arg_f32(a, 2),
4432 canvas_arg_f32(a, 3),
4433 );
4434 crate::os_lib::canvas2d::with_context(id, |c| {
4435 canvas_sync_state(&this, c);
4436 c.fill_rect(x, y, w, h);
4437 });
4438 }
4439 Ok(Value::Undefined)
4440}
4441
4442pub fn canvas_stroke_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4443 if let Some(id) = canvas_ctx_id(&this) {
4444 let (x, y, w, h) = (
4445 canvas_arg_f32(a, 0),
4446 canvas_arg_f32(a, 1),
4447 canvas_arg_f32(a, 2),
4448 canvas_arg_f32(a, 3),
4449 );
4450 crate::os_lib::canvas2d::with_context(id, |c| {
4451 canvas_sync_state(&this, c);
4452 c.stroke_rect(x, y, w, h);
4453 });
4454 }
4455 Ok(Value::Undefined)
4456}
4457
4458pub fn canvas_clear_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4459 if let Some(id) = canvas_ctx_id(&this) {
4460 let (x, y, w, h) = (
4461 canvas_arg_f32(a, 0),
4462 canvas_arg_f32(a, 1),
4463 canvas_arg_f32(a, 2),
4464 canvas_arg_f32(a, 3),
4465 );
4466 crate::os_lib::canvas2d::with_context(id, |c| c.clear_rect(x, y, w, h));
4467 }
4468 Ok(Value::Undefined)
4469}
4470
4471pub fn canvas_begin_path(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
4472 if let Some(id) = canvas_ctx_id(&this) {
4473 crate::os_lib::canvas2d::with_context(id, |c| c.path.begin_path());
4474 }
4475 Ok(Value::Undefined)
4476}
4477
4478pub fn canvas_close_path(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
4479 if let Some(id) = canvas_ctx_id(&this) {
4480 crate::os_lib::canvas2d::with_context(id, |c| c.path.close_path());
4481 }
4482 Ok(Value::Undefined)
4483}
4484
4485pub fn canvas_move_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4486 if let Some(id) = canvas_ctx_id(&this) {
4487 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4488 crate::os_lib::canvas2d::with_context(id, |c| {
4489 let m = c.state.transform;
4490 c.path.move_to(&m, x, y);
4491 });
4492 }
4493 Ok(Value::Undefined)
4494}
4495
4496pub fn canvas_line_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4497 if let Some(id) = canvas_ctx_id(&this) {
4498 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4499 crate::os_lib::canvas2d::with_context(id, |c| {
4500 let m = c.state.transform;
4501 c.path.line_to(&m, x, y);
4502 });
4503 }
4504 Ok(Value::Undefined)
4505}
4506
4507pub fn canvas_rect(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4508 if let Some(id) = canvas_ctx_id(&this) {
4509 let (x, y, w, h) = (
4510 canvas_arg_f32(a, 0),
4511 canvas_arg_f32(a, 1),
4512 canvas_arg_f32(a, 2),
4513 canvas_arg_f32(a, 3),
4514 );
4515 crate::os_lib::canvas2d::with_context(id, |c| {
4516 let m = c.state.transform;
4517 c.path.rect(&m, x, y, w, h);
4518 });
4519 }
4520 Ok(Value::Undefined)
4521}
4522
4523pub fn canvas_arc(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4524 if let Some(id) = canvas_ctx_id(&this) {
4525 let (cx, cy, r) = (
4526 canvas_arg_f32(a, 0),
4527 canvas_arg_f32(a, 1),
4528 canvas_arg_f32(a, 2),
4529 );
4530 let start = canvas_arg_f32(a, 3);
4531 let end = canvas_arg_f32(a, 4);
4532 let ccw = a.get(5).map(|v| v.truthy()).unwrap_or(false);
4533 crate::os_lib::canvas2d::with_context(id, |c| {
4534 let m = c.state.transform;
4535 c.path.arc(&m, cx, cy, r, start, end, ccw);
4536 });
4537 }
4538 Ok(Value::Undefined)
4539}
4540
4541pub fn canvas_arc_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4542 if let Some(id) = canvas_ctx_id(&this) {
4543 let (x1, y1) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4544 let (x2, y2) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
4545 let radius = canvas_arg_f32(a, 4);
4546 crate::os_lib::canvas2d::with_context(id, |c| {
4547 let m = c.state.transform;
4548 c.path.arc_to(&m, x1, y1, x2, y2, radius);
4549 });
4550 }
4551 Ok(Value::Undefined)
4552}
4553
4554pub fn canvas_fill(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4555 if let Some(id) = canvas_ctx_id(&this) {
4556 if let Some(pid) = a.first().and_then(path2d_id) {
4557 let rule = match a.get(1).map(|v| v.to_js_string()) {
4558 Some(s) if s.eq_ignore_ascii_case("evenodd") => {
4559 crate::os_lib::canvas2d::FillRule::EvenOdd
4560 }
4561 _ => crate::os_lib::canvas2d::FillRule::NonZero,
4562 };
4563 if let Some(subpaths) = crate::os_lib::canvas2d::get_path2d_subpaths(pid) {
4564 crate::os_lib::canvas2d::with_context(id, |c| {
4565 canvas_sync_state(&this, c);
4566 c.fill_subpaths(&subpaths, rule);
4567 });
4568 }
4569 } else {
4570 let rule = match a.first().map(|v| v.to_js_string()) {
4572 Some(s) if s.eq_ignore_ascii_case("evenodd") => {
4573 crate::os_lib::canvas2d::FillRule::EvenOdd
4574 }
4575 _ => crate::os_lib::canvas2d::FillRule::NonZero,
4576 };
4577 crate::os_lib::canvas2d::with_context(id, |c| {
4578 canvas_sync_state(&this, c);
4579 c.fill(rule);
4580 });
4581 }
4582 }
4583 Ok(Value::Undefined)
4584}
4585
4586pub fn canvas_stroke(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4587 if let Some(id) = canvas_ctx_id(&this) {
4588 if let Some(pid) = a.first().and_then(path2d_id) {
4589 if let Some(subpaths) = crate::os_lib::canvas2d::get_path2d_subpaths(pid) {
4590 crate::os_lib::canvas2d::with_context(id, |c| {
4591 canvas_sync_state(&this, c);
4592 c.stroke_subpaths(&subpaths);
4593 });
4594 }
4595 } else {
4596 crate::os_lib::canvas2d::with_context(id, |c| {
4597 canvas_sync_state(&this, c);
4598 c.stroke_with_width();
4600 });
4601 }
4602 }
4603 Ok(Value::Undefined)
4604}
4605
4606
4607pub fn canvas_save(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
4609 if let Some(id) = canvas_ctx_id(&this) {
4610 crate::os_lib::canvas2d::with_context(id, |c| {
4611 canvas_sync_state(&this, c);
4614 c.save();
4615 });
4616 }
4617 Ok(Value::Undefined)
4618}
4619
4620pub fn canvas_restore(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
4625 if let Some(id) = canvas_ctx_id(&this) {
4626 let restored = crate::os_lib::canvas2d::with_context(id, |c| {
4627 c.restore();
4628 (
4629 c.state.global_alpha,
4630 c.state.line_width,
4631 c.state.fill_str.clone(),
4632 c.state.stroke_str.clone(),
4633 c.state.font_str.clone(),
4634 c.state.text_align_str.clone(),
4635 c.state.text_baseline_str.clone(),
4636 c.state.miter_limit,
4637 c.state.line_dash_offset,
4638 c.state.line_dash.clone(),
4639 c.state.image_smoothing_enabled,
4640 match c.state.line_cap {
4641 crate::os_lib::canvas2d::LineCap::Butt => "butt",
4642 crate::os_lib::canvas2d::LineCap::Round => "round",
4643 crate::os_lib::canvas2d::LineCap::Square => "square",
4644 },
4645 match c.state.line_join {
4646 crate::os_lib::canvas2d::LineJoin::Miter => "miter",
4647 crate::os_lib::canvas2d::LineJoin::Round => "round",
4648 crate::os_lib::canvas2d::LineJoin::Bevel => "bevel",
4649 },
4650 c.state.shadow_blur,
4651 c.state.shadow_color_str.clone(),
4652 c.state.shadow_offset_x,
4653 c.state.shadow_offset_y,
4654 c.state.filter.clone(),
4655 c.state.global_composite_operation_str.clone(),
4656 c.state.direction_str.clone(),
4657 )
4658 });
4659 if let (Some(r), Value::Object(o)) = (restored, &this) {
4660 let mut b = o.borrow_mut();
4661 b.props.insert("globalAlpha".into(), Value::Number(r.0 as f64));
4662 b.props.insert("lineWidth".into(), Value::Number(r.1 as f64));
4663 b.props.insert("fillStyle".into(), Value::str(&r.2));
4664 b.props.insert("strokeStyle".into(), Value::str(&r.3));
4665 b.props.insert("font".into(), Value::str(&r.4));
4666 b.props.insert("textAlign".into(), Value::str(&r.5));
4667 b.props.insert("textBaseline".into(), Value::str(&r.6));
4668 b.props.insert("miterLimit".into(), Value::Number(r.7 as f64));
4669 b.props.insert("lineDashOffset".into(), Value::Number(r.8 as f64));
4670 b.props.insert("imageSmoothingEnabled".into(), Value::Bool(r.10));
4671 b.props.insert("lineCap".into(), Value::str(r.11));
4672 b.props.insert("lineJoin".into(), Value::str(r.12));
4673 b.props.insert("shadowBlur".into(), Value::Number(r.13 as f64));
4674 b.props.insert("shadowColor".into(), Value::str(&r.14));
4675 b.props.insert("shadowOffsetX".into(), Value::Number(r.15 as f64));
4676 b.props.insert("shadowOffsetY".into(), Value::Number(r.16 as f64));
4677 b.props.insert("filter".into(), Value::str(&r.17));
4678 b.props.insert("globalCompositeOperation".into(), Value::str(&r.18));
4679 b.props.insert("direction".into(), Value::str(&r.19));
4680
4681 let dash_objs = r.9.iter().map(|d| Value::Number(*d as f64)).collect();
4682 b.props.insert("_line_dash".into(), Value::Object(Obj::array(dash_objs)));
4683 }
4684 }
4685 Ok(Value::Undefined)
4686}
4687
4688pub fn canvas_translate(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4689 if let Some(id) = canvas_ctx_id(&this) {
4690 let (tx, ty) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4691 crate::os_lib::canvas2d::with_context(id, |c| c.translate(tx, ty));
4692 }
4693 Ok(Value::Undefined)
4694}
4695
4696pub fn canvas_scale(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4697 if let Some(id) = canvas_ctx_id(&this) {
4698 let (sx, sy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4699 crate::os_lib::canvas2d::with_context(id, |c| c.scale(sx, sy));
4700 }
4701 Ok(Value::Undefined)
4702}
4703
4704pub fn canvas_rotate(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4705 if let Some(id) = canvas_ctx_id(&this) {
4706 let rad = canvas_arg_f32(a, 0);
4707 crate::os_lib::canvas2d::with_context(id, |c| c.rotate(rad));
4708 }
4709 Ok(Value::Undefined)
4710}
4711
4712pub fn canvas_transform(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4714 if let Some(id) = canvas_ctx_id(&this) {
4715 let m = crate::os_lib::canvas2d::Matrix {
4716 a: canvas_arg_f32(a, 0),
4717 b: canvas_arg_f32(a, 1),
4718 c: canvas_arg_f32(a, 2),
4719 d: canvas_arg_f32(a, 3),
4720 e: canvas_arg_f32(a, 4),
4721 f: canvas_arg_f32(a, 5),
4722 };
4723 crate::os_lib::canvas2d::with_context(id, |ctx| {
4724 let cur = ctx.state.transform;
4725 ctx.set_transform(cur.multiply(&m));
4726 });
4727 }
4728 Ok(Value::Undefined)
4729}
4730
4731pub fn canvas_set_transform(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4733 if let Some(id) = canvas_ctx_id(&this) {
4734 let m = crate::os_lib::canvas2d::Matrix {
4735 a: canvas_arg_f32(a, 0),
4736 b: canvas_arg_f32(a, 1),
4737 c: canvas_arg_f32(a, 2),
4738 d: canvas_arg_f32(a, 3),
4739 e: canvas_arg_f32(a, 4),
4740 f: canvas_arg_f32(a, 5),
4741 };
4742 crate::os_lib::canvas2d::with_context(id, |ctx| ctx.set_transform(m));
4743 }
4744 Ok(Value::Undefined)
4745}
4746
4747pub fn canvas_reset_transform(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
4748 if let Some(id) = canvas_ctx_id(&this) {
4749 crate::os_lib::canvas2d::with_context(id, |c| c.reset_transform());
4750 }
4751 Ok(Value::Undefined)
4752}
4753
4754pub fn canvas_bezier_curve_to(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4755 if let Some(id) = canvas_ctx_id(&this) {
4756 let (c1x, c1y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4757 let (c2x, c2y) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
4758 let (x, y) = (canvas_arg_f32(a, 4), canvas_arg_f32(a, 5));
4759 crate::os_lib::canvas2d::with_context(id, |c| {
4760 let m = c.state.transform;
4761 c.path.bezier_curve_to(&m, c1x, c1y, c2x, c2y, x, y);
4762 });
4763 }
4764 Ok(Value::Undefined)
4765}
4766
4767pub fn canvas_quadratic_curve_to(
4768 _it: &mut Interp,
4769 this: Value,
4770 a: &[Value],
4771) -> Result<Value, Value> {
4772 if let Some(id) = canvas_ctx_id(&this) {
4773 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4774 let (x, y) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
4775 crate::os_lib::canvas2d::with_context(id, |c| {
4776 let m = c.state.transform;
4777 c.path.quadratic_curve_to(&m, cx, cy, x, y);
4778 });
4779 }
4780 Ok(Value::Undefined)
4781}
4782
4783
4784pub fn canvas_ellipse(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4786 if let Some(id) = canvas_ctx_id(&this) {
4787 let (cx, cy) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4788 let (rx, ry) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
4789 let rot = canvas_arg_f32(a, 4);
4790 let start = canvas_arg_f32(a, 5);
4791 let end = canvas_arg_f32(a, 6);
4792 let ccw = a.get(7).map(|v| v.truthy()).unwrap_or(false);
4793 crate::os_lib::canvas2d::with_context(id, |c| {
4794 let m = c.state.transform;
4795 c.path.ellipse(&m, cx, cy, rx, ry, rot, start, end, ccw);
4796 });
4797 }
4798 Ok(Value::Undefined)
4799}
4800
4801pub fn canvas_round_rect(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4807 if let Some(id) = canvas_ctx_id(&this) {
4808 let (x, y) = (canvas_arg_f32(a, 0), canvas_arg_f32(a, 1));
4809 let (w, h) = (canvas_arg_f32(a, 2), canvas_arg_f32(a, 3));
4810 let radii = parse_round_rect_radii(it, a.get(4))?;
4811 crate::os_lib::canvas2d::with_context(id, |c| {
4812 let m = c.state.transform;
4813 c.path.round_rect(&m, x, y, w, h, radii);
4814 });
4815 }
4816 Ok(Value::Undefined)
4817}
4818
4819
4820pub fn canvas_clip(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4822 if let Some(id) = canvas_ctx_id(&this) {
4823 if let Some(pid) = a.first().and_then(path2d_id) {
4824 let rule = match a.get(1).map(|v| v.to_js_string()) {
4825 Some(s) if s.eq_ignore_ascii_case("evenodd") => {
4826 crate::os_lib::canvas2d::FillRule::EvenOdd
4827 }
4828 _ => crate::os_lib::canvas2d::FillRule::NonZero,
4829 };
4830 if let Some(subpaths) = crate::os_lib::canvas2d::get_path2d_subpaths(pid) {
4831 crate::os_lib::canvas2d::with_context(id, |c| {
4832 c.clip_subpaths(&subpaths, rule);
4833 });
4834 }
4835 } else {
4836 let rule = match a.first().map(|v| v.to_js_string()) {
4837 Some(s) if s.eq_ignore_ascii_case("evenodd") => {
4838 crate::os_lib::canvas2d::FillRule::EvenOdd
4839 }
4840 _ => crate::os_lib::canvas2d::FillRule::NonZero,
4841 };
4842 crate::os_lib::canvas2d::with_context(id, |c| {
4843 c.clip(rule);
4844 });
4845 }
4846 }
4847 Ok(Value::Undefined)
4848}
4849
4850
4851pub fn canvas_get_image_data(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4853 let (x, y) = (canvas_arg_f32(a, 0) as i32, canvas_arg_f32(a, 1) as i32);
4854 let w = (canvas_arg_f32(a, 2).abs() as usize).max(1);
4855 let h = (canvas_arg_f32(a, 3).abs() as usize).max(1);
4856 let cs = parse_image_data_color_space(a.get(4));
4857
4858 let items = if let Some(id) = canvas_ctx_id(&this) {
4859 crate::os_lib::canvas2d::with_context(id, |c| {
4860 let bytes = crate::os_lib::canvas2d::get_image_data(&c.surface, x, y, w as i32, h as i32);
4861 bytes.into_iter().map(|b| Value::Number(b as f64)).collect()
4862 }).unwrap_or_else(|| alloc::vec![Value::Number(0.0); w * h * 4])
4863 } else {
4864 alloc::vec![Value::Number(0.0); w * h * 4]
4865 };
4866
4867 Ok(make_image_data_object(w, h, items, cs))
4868}
4869
4870pub fn canvas_put_image_data(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4874 let Some(id) = canvas_ctx_id(&this) else {
4875 return Ok(Value::Undefined);
4876 };
4877 let Some(Value::Object(img)) = a.first() else {
4878 return Ok(Value::Undefined);
4879 };
4880 let (w, h, bytes) = {
4881 let b = img.borrow();
4882 let w = b
4883 .props
4884 .get("width")
4885 .map(|v| v.to_number() as i32)
4886 .unwrap_or(0);
4887 let h = b
4888 .props
4889 .get("height")
4890 .map(|v| v.to_number() as i32)
4891 .unwrap_or(0);
4892 let mut bytes: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
4893 if let Some(Value::Object(arr)) = b.props.get("data") {
4894 let ab = arr.borrow();
4895 if let ObjKind::Bytes(st) = &ab.kind {
4896 bytes = st.to_vec();
4897 }
4898 if let ObjKind::Array(items) = &ab.kind {
4899 bytes.reserve(items.len());
4900 for v in items {
4901 let n = v.to_number();
4902 bytes.push(if n.is_finite() {
4904 n.clamp(0.0, 255.0) as u8
4905 } else {
4906 0
4907 });
4908 }
4909 }
4910 }
4911 (w, h, bytes)
4912 };
4913 let (dx, dy) = (canvas_arg_f32(a, 1) as i32, canvas_arg_f32(a, 2) as i32);
4914 crate::os_lib::canvas2d::with_context(id, |c| {
4915 crate::os_lib::canvas2d::put_image_data(&mut c.surface, &bytes, w, h, dx, dy);
4916 });
4917 Ok(Value::Undefined)
4918}
4919
4920
4921enum DrawSrc {
4923 Image(alloc::sync::Arc<crate::os_lib::web_engine::DecodedImage>),
4925 Canvas(u32, u32, alloc::vec::Vec<u8>),
4927}
4928
4929fn draw_src_from(it: &mut Interp, v: &Value) -> Option<DrawSrc> {
4935 let Value::Object(o) = v else {
4936 return None;
4937 };
4938 let idx = match o.borrow().kind {
4939 ObjKind::DomElement(i) => Some(i),
4940 _ => None,
4941 }?;
4942 let (tag, src, cid) = {
4943 let dom = it.dom.borrow();
4944 let tag = dom.nodes.get(idx).map(|n| n.tag.clone()).unwrap_or_default();
4945 let src = dom.get_attr(idx, "src").unwrap_or_default();
4946 let cid = dom
4947 .get_attr(idx, "_c2d_id")
4948 .and_then(|s| s.parse::<u32>().ok());
4949 (tag, src, cid)
4950 };
4951 if tag == "canvas" {
4952 let id = cid?;
4953 return crate::os_lib::canvas2d::with_context(id, |c| {
4954 let (w, h) = (c.surface.width, c.surface.height);
4955 DrawSrc::Canvas(w, h, crate::os_lib::canvas2d::get_image_data(
4956 &c.surface, 0, 0, w as i32, h as i32,
4957 ))
4958 });
4959 }
4960 crate::os_lib::web_engine::shared_image(&src).map(DrawSrc::Image)
4961}
4962
4963pub fn canvas_draw_image(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
4966 let Some(id) = canvas_ctx_id(&this) else {
4967 return Ok(Value::Undefined);
4968 };
4969 let Some(src) = a.first().and_then(|v| draw_src_from(it, v)) else {
4970 return Ok(Value::Undefined);
4971 };
4972 let (sw, sh, bytes): (u32, u32, &[u8]) = match &src {
4973 DrawSrc::Image(img) => (img.width, img.height, &img.rgba),
4974 DrawSrc::Canvas(w, h, b) => (*w, *h, b),
4975 };
4976 let spec = match a.len() {
4978 0..=2 => return Ok(Value::Undefined),
4979 3 | 4 => crate::os_lib::canvas2d::BlitSpec {
4980 sx: 0.0,
4981 sy: 0.0,
4982 sw: sw as f32,
4983 sh: sh as f32,
4984 dx: canvas_arg_f32(a, 1),
4985 dy: canvas_arg_f32(a, 2),
4986 dw: sw as f32,
4987 dh: sh as f32,
4988 },
4989 5..=8 => crate::os_lib::canvas2d::BlitSpec {
4990 sx: 0.0,
4991 sy: 0.0,
4992 sw: sw as f32,
4993 sh: sh as f32,
4994 dx: canvas_arg_f32(a, 1),
4995 dy: canvas_arg_f32(a, 2),
4996 dw: canvas_arg_f32(a, 3),
4997 dh: canvas_arg_f32(a, 4),
4998 },
4999 _ => crate::os_lib::canvas2d::BlitSpec {
5000 sx: canvas_arg_f32(a, 1),
5001 sy: canvas_arg_f32(a, 2),
5002 sw: canvas_arg_f32(a, 3),
5003 sh: canvas_arg_f32(a, 4),
5004 dx: canvas_arg_f32(a, 5),
5005 dy: canvas_arg_f32(a, 6),
5006 dw: canvas_arg_f32(a, 7),
5007 dh: canvas_arg_f32(a, 8),
5008 },
5009 };
5010 let image = crate::os_lib::canvas2d::ImageSource {
5011 width: sw,
5012 height: sh,
5013 rgba: bytes,
5014 };
5015 crate::os_lib::canvas2d::with_context(id, |c| {
5016 canvas_sync_state(&this, c);
5017 c.sync_clip();
5018 let m = c.state.transform;
5019 let alpha = c.state.global_alpha;
5020 let smooth = c.state.image_smoothing_enabled;
5021 let filter = c.state.filter.clone();
5022 c.surface.draw_image_with_filter(&image, &spec, &m, alpha, smooth, &filter);
5023 });
5024 Ok(Value::Undefined)
5025}
5026
5027
5028struct OwnedGlyph {
5030 width: u32,
5031 height: u32,
5032 x_offset: i32,
5033 y_offset: i32,
5034 advance: f32,
5035 alpha: alloc::vec::Vec<u8>,
5036}
5037
5038fn glyphs_for(text: &str, size_px: u32) -> alloc::vec::Vec<OwnedGlyph> {
5046 let mut out = alloc::vec::Vec::new();
5047 let mut font_lock = crate::kernel::vector_font::GLOBAL_VECTOR_FONT.lock();
5048 let Some(font) = font_lock.as_mut() else {
5049 return out;
5050 };
5051 for c in text.chars() {
5052 if let Some(g) = font.get_glyph(c, size_px) {
5053 out.push(OwnedGlyph {
5054 width: g.width,
5055 height: g.height,
5056 x_offset: g.x_offset,
5057 y_offset: g.y_offset,
5058 advance: g.advance as f32,
5059 alpha: g.data.clone(),
5060 });
5061 }
5062 }
5063 out
5064}
5065
5066fn canvas_font_size(this: &Value) -> f32 {
5068 let Value::Object(o) = this else {
5069 return 10.0;
5070 };
5071 let b = o.borrow();
5072 let Some(Value::Str(s)) = b.props.get("font") else {
5073 return 10.0;
5074 };
5075 crate::os_lib::canvas2d::parse_font_size(s).unwrap_or(10.0)
5076}
5077
5078fn canvas_text_layout_params(
5084 this: &Value,
5085) -> (
5086 crate::os_lib::canvas2d::TextAlign,
5087 crate::os_lib::canvas2d::TextBaseline,
5088 crate::os_lib::canvas2d::CanvasDirection,
5089) {
5090 let mut al = crate::os_lib::canvas2d::TextAlign::Start;
5091 let mut bl = crate::os_lib::canvas2d::TextBaseline::Alphabetic;
5092 let mut dir = crate::os_lib::canvas2d::CanvasDirection::Inherit;
5093 if let Value::Object(o) = this {
5094 let b = o.borrow();
5095 if let Some(Value::Str(s)) = b.props.get("textAlign") {
5096 if let Some(v) = crate::os_lib::canvas2d::parse_text_align(s) {
5097 al = v;
5098 }
5099 }
5100 if let Some(Value::Str(s)) = b.props.get("textBaseline") {
5101 if let Some(v) = crate::os_lib::canvas2d::parse_text_baseline(s) {
5102 bl = v;
5103 }
5104 }
5105 if let Some(Value::Str(s)) = b.props.get("direction") {
5106 if let Some(v) = crate::os_lib::canvas2d::parse_canvas_direction(s) {
5107 dir = v;
5108 }
5109 }
5110 }
5111 (al, bl, dir)
5112}
5113
5114pub fn canvas_stroke_text(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5118 let Some(id) = canvas_ctx_id(&this) else {
5119 return Ok(Value::Undefined);
5120 };
5121 let text = a.first().map(|v| v.to_js_string()).unwrap_or_default();
5122 if text.is_empty() {
5123 return Ok(Value::Undefined);
5124 }
5125 let (x, y) = (canvas_arg_f32(a, 1), canvas_arg_f32(a, 2));
5126 if !x.is_finite() || !y.is_finite() {
5127 return Ok(Value::Undefined);
5128 }
5129 let size = canvas_font_size(&this);
5130 let glyphs = glyphs_for(&text, size as u32);
5131 if glyphs.is_empty() {
5132 return Ok(Value::Undefined);
5133 }
5134 let total: f32 = glyphs.iter().map(|g| g.advance).sum();
5135
5136 let (align, baseline, dir) = canvas_text_layout_params(&this);
5137 let x0 = x + crate::os_lib::canvas2d::align_offset_with_direction(align, total, dir);
5138 let y0 = y + crate::os_lib::canvas2d::baseline_offset(baseline, size);
5139
5140 crate::os_lib::canvas2d::with_context(id, |c| {
5141 canvas_sync_state(&this, c);
5142 c.sync_clip();
5143 let stroke_color = c.apply_alpha(c.state.stroke);
5144 let line_width = c.state.line_width;
5145 let m = c.state.transform;
5146 if c.state.has_shadow() {
5147 let shadow_color = c.shadow_color_argb();
5148 let mut pen = x0 + c.state.shadow_offset_x;
5149 let sy = y0 + c.state.shadow_offset_y;
5150 for g in &glyphs {
5151 let bm = crate::os_lib::canvas2d::GlyphBitmap {
5152 width: g.width,
5153 height: g.height,
5154 x_offset: g.x_offset,
5155 y_offset: g.y_offset,
5156 advance: g.advance,
5157 alpha: &g.alpha,
5158 };
5159 c.surface.draw_glyph_stroke(&bm, pen, sy, shadow_color, line_width, &m);
5160 pen += g.advance;
5161 }
5162 }
5163 let mut pen = x0;
5164 for g in &glyphs {
5165 let bm = crate::os_lib::canvas2d::GlyphBitmap {
5166 width: g.width,
5167 height: g.height,
5168 x_offset: g.x_offset,
5169 y_offset: g.y_offset,
5170 advance: g.advance,
5171 alpha: &g.alpha,
5172 };
5173 c.surface.draw_glyph_stroke(&bm, pen, y0, stroke_color, line_width, &m);
5174 pen += g.advance;
5175 }
5176 });
5177 Ok(Value::Undefined)
5178}
5179
5180pub fn canvas_fill_text(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5181 let Some(id) = canvas_ctx_id(&this) else {
5182 return Ok(Value::Undefined);
5183 };
5184 let text = a.first().map(|v| v.to_js_string()).unwrap_or_default();
5185 if text.is_empty() {
5186 return Ok(Value::Undefined);
5187 }
5188 let (x, y) = (canvas_arg_f32(a, 1), canvas_arg_f32(a, 2));
5189 if !x.is_finite() || !y.is_finite() {
5190 return Ok(Value::Undefined);
5191 }
5192 let size = canvas_font_size(&this);
5193 let glyphs = glyphs_for(&text, size as u32);
5194 if glyphs.is_empty() {
5195 return Ok(Value::Undefined);
5196 }
5197 let total: f32 = glyphs.iter().map(|g| g.advance).sum();
5198
5199 let (align, baseline, dir) = canvas_text_layout_params(&this);
5200 let x0 = x + crate::os_lib::canvas2d::align_offset_with_direction(align, total, dir);
5201 let y0 = y + crate::os_lib::canvas2d::baseline_offset(baseline, size);
5202
5203 crate::os_lib::canvas2d::with_context(id, |c| {
5204 canvas_sync_state(&this, c);
5205 c.sync_clip();
5206 let color = c.apply_alpha(c.state.fill);
5207 let m = c.state.transform;
5208 if c.state.has_shadow() {
5209 let shadow_color = c.shadow_color_argb();
5210 let mut pen = x0 + c.state.shadow_offset_x;
5211 let sy = y0 + c.state.shadow_offset_y;
5212 for g in &glyphs {
5213 let bm = crate::os_lib::canvas2d::GlyphBitmap {
5214 width: g.width,
5215 height: g.height,
5216 x_offset: g.x_offset,
5217 y_offset: g.y_offset,
5218 advance: g.advance,
5219 alpha: &g.alpha,
5220 };
5221 c.surface.draw_glyph(&bm, pen, sy, shadow_color, &m);
5222 pen += g.advance;
5223 }
5224 }
5225 let mut pen = x0;
5226 for g in &glyphs {
5227 let bm = crate::os_lib::canvas2d::GlyphBitmap {
5228 width: g.width,
5229 height: g.height,
5230 x_offset: g.x_offset,
5231 y_offset: g.y_offset,
5232 advance: g.advance,
5233 alpha: &g.alpha,
5234 };
5235 c.surface.draw_glyph(&bm, pen, y0, color, &m);
5236 pen += g.advance;
5237 }
5238 });
5239 Ok(Value::Undefined)
5240}
5241
5242pub fn canvas_measure_text(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5247 let text = a.first().map(|v| v.to_js_string()).unwrap_or_default();
5248 let size = canvas_font_size(&this);
5249 let glyphs = glyphs_for(&text, size as u32);
5250 if glyphs.is_empty() && !text.is_empty() {
5251 return dom_canvas_measure_text(it, this, a);
5253 }
5254 let width: f32 = glyphs.iter().map(|g| g.advance).sum();
5255 let obj = Obj::plain();
5256 {
5257 let mut b = obj.borrow_mut();
5258 b.props.insert("width".into(), Value::Number(width as f64));
5259 let ascent = glyphs
5261 .iter()
5262 .map(|g| -g.y_offset)
5263 .max()
5264 .unwrap_or(0)
5265 .max(0) as f64;
5266 let descent = glyphs
5267 .iter()
5268 .map(|g| g.y_offset + g.height as i32)
5269 .max()
5270 .unwrap_or(0)
5271 .max(0) as f64;
5272 b.props
5273 .insert("actualBoundingBoxAscent".into(), Value::Number(ascent));
5274 b.props
5275 .insert("actualBoundingBoxDescent".into(), Value::Number(descent));
5276 b.props.insert(
5277 "fontBoundingBoxAscent".into(),
5278 Value::Number((size * 0.85) as f64),
5279 );
5280 b.props.insert(
5281 "fontBoundingBoxDescent".into(),
5282 Value::Number((size * 0.15) as f64),
5283 );
5284 let (align, _, dir) = canvas_text_layout_params(&this);
5285 let align_off = crate::os_lib::canvas2d::align_offset_with_direction(align, width, dir);
5286 let actual_left = -align_off;
5287 let actual_right = width + align_off;
5288 b.props
5289 .insert("actualBoundingBoxLeft".into(), Value::Number(actual_left as f64));
5290 b.props.insert(
5291 "actualBoundingBoxRight".into(),
5292 Value::Number(actual_right as f64),
5293 );
5294 }
5295 Ok(Value::Object(obj))
5296}
5297
5298pub(crate) fn dom_attach_shadow(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5300 let host_idx = match this_dom_idx(&this) {
5301 Some(i) => i,
5302 None => return Err(it.type_error("attachShadow must be called on an Element")),
5303 };
5304
5305 let opt = arg(a, 0);
5306 let mode_str = match &opt {
5307 Value::Object(o) => o
5308 .borrow()
5309 .props
5310 .get("mode")
5311 .map(|v| v.to_js_string())
5312 .unwrap_or_default(),
5313 _ => alloc::string::String::new(),
5314 };
5315
5316 let mode = match crate::os_lib::shadow_dom::ShadowRootMode::from_str(&mode_str) {
5317 Some(m) => m,
5318 None => {
5319 return Err(it.type_error(
5320 "Failed to execute 'attachShadow' on 'Element': The provided value is not a valid enum value of type ShadowRootMode ('open' or 'closed')",
5321 ))
5322 }
5323 };
5324
5325 match it.dom.borrow_mut().attach_shadow(host_idx, mode) {
5326 Ok(root_idx) => {
5327 let root_obj = Obj::dom(root_idx);
5328 {
5329 let mut ro = root_obj.borrow_mut();
5330 ro.props.insert("mode".into(), Value::str(mode.as_str()));
5331 ro.props.insert("host".into(), this.clone());
5332 }
5333 Ok(Value::Object(root_obj))
5334 }
5335 Err(crate::os_lib::shadow_dom::AttachShadowError::AlreadyHasShadowRoot) => {
5336 Err(make_dom_exception(
5337 "InvalidStateError",
5338 "Shadow root cannot be created on a host which already hosts a shadow tree",
5339 ))
5340 }
5341 Err(crate::os_lib::shadow_dom::AttachShadowError::DisallowedTag) => {
5342 Err(make_dom_exception(
5343 "NotSupportedError",
5344 "This element does not support attachShadow",
5345 ))
5346 }
5347 Err(_) => Err(make_dom_exception(
5348 "NotSupportedError",
5349 "Failed to attach shadow root",
5350 )),
5351 }
5352}
5353
5354pub(crate) fn custom_elements_define(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5356 let name = arg(a, 0).to_js_string();
5357 let ctor = arg(a, 1);
5358
5359 if !crate::os_lib::custom_elements::is_valid_custom_element_name(&name) {
5360 return Err(it.type_error(alloc::format!("Failed to execute 'define' on 'CustomElementRegistry': \"{}\" is not a valid custom element name", name)));
5361 }
5362
5363 if !matches!(ctor, Value::Object(_)) {
5364 return Err(it.type_error("Failed to execute 'define' on 'CustomElementRegistry': The referenced script is not a constructor"));
5365 }
5366
5367 let mut observed_attrs = alloc::vec::Vec::new();
5370 let obs_val = it.get_property(&ctor, "observedAttributes")?;
5371 if !matches!(obs_val, Value::Undefined | Value::Null) {
5372 for item in array_like_items(it, &obs_val) {
5373 observed_attrs.push(item.to_js_string());
5374 }
5375 }
5376
5377 match it.dom.borrow_mut().custom_elements.define(&name, observed_attrs) {
5379 Ok(()) => {}
5380 Err(crate::os_lib::custom_elements::DefineError::AlreadyDefined) => {
5381 return Err(make_dom_exception(
5382 "NotSupportedError",
5383 &alloc::format!("Failed to execute 'define' on 'CustomElementRegistry': the name \"{}\" has already been used with this registry", name),
5384 ));
5385 }
5386 Err(crate::os_lib::custom_elements::DefineError::InvalidName) => {
5387 return Err(it.type_error(alloc::format!("\"{}\" is not a valid custom element name", name)));
5388 }
5389 }
5390
5391 if let Value::Object(reg_obj) = &this {
5393 if let Some(Value::Object(map)) = reg_obj.borrow().props.get("_registry") {
5394 map.borrow_mut().props.insert(name.clone(), ctor.clone());
5395 }
5396 }
5397
5398 let matching_indices: alloc::vec::Vec<usize> = {
5401 let mut dom = it.dom.borrow_mut();
5402 for n in dom.nodes.iter_mut().filter(|n| !n.is_text && n.tag.eq_ignore_ascii_case(&name)) {
5403 n.attrs.insert(alloc::string::String::from("_defined"), alloc::string::String::from("true"));
5404 }
5405 let root = 0;
5406 dom.subtree_elements(root)
5407 .into_iter()
5408 .filter(|&i| dom.nodes[i].tag.eq_ignore_ascii_case(&name))
5409 .collect()
5410 };
5411 for idx in matching_indices {
5412 it.ce_try_upgrade(idx, true);
5413 }
5414
5415 Ok(Value::Undefined)
5416}
5417
5418pub(crate) fn custom_elements_get(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5420 let name = arg(a, 0).to_js_string();
5421 if let Value::Object(reg_obj) = this {
5422 if let Some(Value::Object(map)) = reg_obj.borrow().props.get("_registry") {
5423 if let Some(ctor) = map.borrow().props.get(&name) {
5424 return Ok(ctor.clone());
5425 }
5426 }
5427 }
5428 Ok(Value::Undefined)
5429}
5430
5431pub(crate) fn custom_elements_when_defined(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
5433 let name = arg(a, 0).to_js_string();
5434 if !crate::os_lib::custom_elements::is_valid_custom_element_name(&name) {
5435 let err = it.type_error(alloc::format!("\"{}\" is not a valid custom element name", name));
5436 return Ok(rejected_promise(it, err));
5437 }
5438 let is_def = if let Value::Object(reg_obj) = &this {
5439 if let Some(Value::Object(map)) = reg_obj.borrow().props.get("_registry") {
5440 map.borrow().props.contains_key(&name)
5441 } else {
5442 false
5443 }
5444 } else {
5445 false
5446 };
5447
5448 if is_def {
5449 Ok(resolved_promise(it, Value::Undefined))
5450 } else {
5451 Ok(resolved_promise(it, Value::Undefined))
5452 }
5453}
5454
5455pub(crate) fn custom_elements_upgrade(_it: &mut Interp, _this: Value, _a: &[Value]) -> Result<Value, Value> {
5457 Ok(Value::Undefined)
5458}
5459
5460
5461
5462pub(crate) fn image_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
5464 let idx = it.dom.borrow_mut().create_element("img");
5465 for (i, name) in ["width", "height"].iter().enumerate() {
5466 if let Some(v) = a.get(i).filter(|v| !matches!(v, Value::Undefined)) {
5467 let n = v.to_number();
5468 if n.is_finite() {
5469 it.dom.borrow_mut().set_attr(idx, name, &alloc::format!("{}", n as i64));
5470 }
5471 }
5472 }
5473 Ok(Value::Object(Obj::dom(idx)))
5474}
5475
5476pub(crate) fn audio_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
5478 let idx = it.dom.borrow_mut().create_element("audio");
5479 it.dom.borrow_mut().set_attr(idx, "preload", "auto");
5480 if let Some(v) = a.first().filter(|v| !matches!(v, Value::Undefined)) {
5481 let src = v.to_js_string();
5482 it.dom.borrow_mut().set_attr(idx, "src", &src);
5483 }
5484 Ok(Value::Object(Obj::dom(idx)))
5485}