1use super::*;
4
5fn range_set_start(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
7 let node = arg(a, 0);
8 let offset = arg(a, 1).to_number().max(0.0) as usize;
9 if let Value::Object(ref o) = t {
10 let mut b = o.borrow_mut();
11 b.props.insert("startContainer".into(), node.clone());
12 b.props.insert("startOffset".into(), Value::Number(offset as f64));
13
14 let end_node = b.props.get("endContainer").cloned().unwrap_or(Value::Null);
15 let end_offset = b.props.get("endOffset").map(|v| v.to_number().max(0.0) as usize).unwrap_or(0);
16
17 let must_collapse = matches!(end_node, Value::Null) || (node.strict_eq(&end_node) && offset > end_offset);
18 if must_collapse {
19 b.props.insert("endContainer".into(), node.clone());
20 b.props.insert("endOffset".into(), Value::Number(offset as f64));
21 b.props.insert("collapsed".into(), Value::Bool(true));
22 } else {
23 let collapsed = node.strict_eq(&end_node) && offset == end_offset;
24 b.props.insert("collapsed".into(), Value::Bool(collapsed));
25 }
26 b.props.insert("commonAncestorContainer".into(), node);
27 }
28 Ok(Value::Undefined)
29}
30
31fn range_set_end(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
32 let node = arg(a, 0);
33 let offset = arg(a, 1).to_number().max(0.0) as usize;
34 if let Value::Object(ref o) = t {
35 let mut b = o.borrow_mut();
36 b.props.insert("endContainer".into(), node.clone());
37 b.props.insert("endOffset".into(), Value::Number(offset as f64));
38
39 let start_node = b.props.get("startContainer").cloned().unwrap_or(Value::Null);
40 let start_offset = b.props.get("startOffset").map(|v| v.to_number().max(0.0) as usize).unwrap_or(0);
41
42 let must_collapse = matches!(start_node, Value::Null) || (node.strict_eq(&start_node) && offset < start_offset);
43 if must_collapse {
44 b.props.insert("startContainer".into(), node.clone());
45 b.props.insert("startOffset".into(), Value::Number(offset as f64));
46 b.props.insert("collapsed".into(), Value::Bool(true));
47 } else {
48 let collapsed = node.strict_eq(&start_node) && offset == start_offset;
49 b.props.insert("collapsed".into(), Value::Bool(collapsed));
50 }
51 b.props.insert("commonAncestorContainer".into(), node);
52 }
53 Ok(Value::Undefined)
54}
55
56fn range_collapse(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
57 let to_start = a.first().map(|v| v.truthy()).unwrap_or(false);
59 if let Value::Object(ref o) = t {
60 let mut b = o.borrow_mut();
61 if to_start {
62 let sc = b.props.get("startContainer").cloned().unwrap_or(Value::Null);
63 let so = b.props.get("startOffset").cloned().unwrap_or(Value::Number(0.0));
64 b.props.insert("endContainer".into(), sc);
65 b.props.insert("endOffset".into(), so);
66 } else {
67 let ec = b.props.get("endContainer").cloned().unwrap_or(Value::Null);
68 let eo = b.props.get("endOffset").cloned().unwrap_or(Value::Number(0.0));
69 b.props.insert("startContainer".into(), ec);
70 b.props.insert("startOffset".into(), eo);
71 }
72 b.props.insert("collapsed".into(), Value::Bool(true));
73 }
74 Ok(Value::Undefined)
75}
76
77fn range_select_node_contents(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
78 let node = arg(a, 0);
79 if let Value::Object(ref o) = t {
80 let mut b = o.borrow_mut();
81 b.props.insert("startContainer".into(), node.clone());
82 b.props.insert("startOffset".into(), Value::Number(0.0));
83 b.props.insert("endContainer".into(), node.clone());
84 let len = if let Value::Object(ref no) = node {
85 let nb = no.borrow();
86 if let Some(Value::Object(ref ch)) = nb.props.get("childNodes") {
87 ch.borrow().props.get("length").map(|l| l.to_number() as usize).unwrap_or(0)
88 } else if let Some(ref s) = nb.props.get("textContent") {
89 s.to_js_string().len()
90 } else {
91 0
92 }
93 } else {
94 0
95 };
96 b.props.insert("endOffset".into(), Value::Number(len as f64));
97 b.props.insert("collapsed".into(), Value::Bool(len == 0));
98 b.props.insert("commonAncestorContainer".into(), node);
99 }
100 Ok(Value::Undefined)
101}
102
103fn range_clone_range(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
104 let new_r = document_create_range(it, Value::Undefined, &[])?;
105 if let (Value::Object(ref src), Value::Object(ref dst)) = (&t, &new_r) {
106 let sb = src.borrow();
107 let mut db = dst.borrow_mut();
108 for k in &[
109 "startContainer", "startOffset", "endContainer", "endOffset", "collapsed", "commonAncestorContainer"
110 ] {
111 if let Some(v) = sb.props.get(*k) {
112 db.props.insert((*k).into(), v.clone());
113 }
114 }
115 }
116 Ok(new_r)
117}
118
119fn range_compare_boundary_points(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
120 let how = arg(a, 0).to_number() as u16;
121 let source = arg(a, 1);
122 if let (Value::Object(ref this_obj), Value::Object(ref src_obj)) = (&t, &source) {
123 let tb = this_obj.borrow();
124 let sb = src_obj.borrow();
125 let (this_node, this_offset) = match how {
126 crate::os_lib::dom_range::START_TO_START | crate::os_lib::dom_range::END_TO_START => {
127 (tb.props.get("startContainer"), tb.props.get("startOffset"))
128 }
129 _ => (tb.props.get("endContainer"), tb.props.get("endOffset")),
130 };
131 let (src_node, src_offset) = match how {
132 crate::os_lib::dom_range::START_TO_START | crate::os_lib::dom_range::START_TO_END => {
133 (sb.props.get("startContainer"), sb.props.get("startOffset"))
134 }
135 _ => (sb.props.get("endContainer"), sb.props.get("endOffset")),
136 };
137 let to = this_offset.map(|v| v.to_number() as usize).unwrap_or(0);
138 let so = src_offset.map(|v| v.to_number() as usize).unwrap_or(0);
139 if let (Some(tn), Some(sn)) = (this_node, src_node) {
140 if tn.strict_eq(sn) {
141 let res = match to.cmp(&so) {
142 core::cmp::Ordering::Less => -1.0,
143 core::cmp::Ordering::Equal => 0.0,
144 core::cmp::Ordering::Greater => 1.0,
145 };
146 return Ok(Value::Number(res));
147 }
148 }
149 }
150 Ok(Value::Number(0.0))
151}
152
153fn range_is_point_in_range(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
154 let node = arg(a, 0);
155 let offset = arg(a, 1).to_number().max(0.0) as usize;
156 if let Value::Object(ref o) = t {
157 let b = o.borrow();
158 let start_node = b.props.get("startContainer");
159 let start_offset = b.props.get("startOffset").map(|v| v.to_number() as usize).unwrap_or(0);
160 let end_node = b.props.get("endContainer");
161 let end_offset = b.props.get("endOffset").map(|v| v.to_number() as usize).unwrap_or(0);
162
163 if matches!(node, Value::Null | Value::Undefined) {
164 return Ok(Value::Bool(false));
165 }
166
167 if let (Some(sn), Some(en)) = (start_node, end_node) {
168 if matches!(sn, Value::Null | Value::Undefined) || matches!(en, Value::Null | Value::Undefined) {
169 return Ok(Value::Bool(false));
170 }
171 if sn.strict_eq(&node) && en.strict_eq(&node) {
172 return Ok(Value::Bool(offset >= start_offset && offset <= end_offset));
173 }
174 }
175 }
176 Ok(Value::Bool(false))
177}
178
179fn range_create_contextual_fragment(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
180 let html = arg(a, 0).to_js_string();
181 let start_node_idx = if let Value::Object(ref o) = this {
182 o.borrow()
183 .props
184 .get("startContainer")
185 .and_then(|v| crate::os_lib::js::builtins::dom::this_dom_idx(v))
186 } else {
187 None
188 };
189 let frag_idx = it.dom.borrow_mut().create_contextual_fragment(start_node_idx, &html);
190 Ok(Value::Object(Obj::dom(frag_idx)))
191}
192
193pub(crate) fn document_create_range(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
195 let r = Obj::plain();
196 {
197 let mut b = r.borrow_mut();
198 b.props.insert(
199 "START_TO_START".into(),
200 Value::Number(crate::os_lib::dom_range::START_TO_START as f64),
201 );
202 b.props.insert(
203 "START_TO_END".into(),
204 Value::Number(crate::os_lib::dom_range::START_TO_END as f64),
205 );
206 b.props.insert(
207 "END_TO_END".into(),
208 Value::Number(crate::os_lib::dom_range::END_TO_END as f64),
209 );
210 b.props.insert(
211 "END_TO_START".into(),
212 Value::Number(crate::os_lib::dom_range::END_TO_START as f64),
213 );
214
215 b.props.insert(
216 "selectNodeContents".into(),
217 nv("selectNodeContents", range_select_node_contents),
218 );
219 b.props.insert("collapse".into(), nv("collapse", range_collapse));
220 b.props.insert("setStart".into(), nv("setStart", range_set_start));
221 b.props.insert("setEnd".into(), nv("setEnd", range_set_end));
222 b.props.insert("cloneRange".into(), nv("cloneRange", range_clone_range));
223 b.props.insert(
224 "compareBoundaryPoints".into(),
225 nv("compareBoundaryPoints", range_compare_boundary_points),
226 );
227 b.props.insert(
228 "isPointInRange".into(),
229 nv("isPointInRange", range_is_point_in_range),
230 );
231 b.props.insert(
232 "createContextualFragment".into(),
233 nv("createContextualFragment", range_create_contextual_fragment),
234 );
235
236 b.props.insert(
237 "getBoundingClientRect".into(),
238 nv("getBoundingClientRect", |_, _, _| {
239 let rect = Obj::plain();
240 for k in &[
241 "top", "left", "bottom", "right", "width", "height", "x", "y",
242 ] {
243 rect.borrow_mut()
244 .props
245 .insert(k.to_string(), Value::Number(0.0));
246 }
247 Ok(Value::Object(rect))
248 }),
249 );
250 b.props.insert("startContainer".into(), Value::Null);
251 b.props.insert("endContainer".into(), Value::Null);
252 b.props.insert("startOffset".into(), Value::Number(0.0));
253 b.props.insert("endOffset".into(), Value::Number(0.0));
254 b.props.insert("collapsed".into(), Value::Bool(true));
255 b.props.insert("commonAncestorContainer".into(), Value::Null);
256
257 for m in [
258 "setStartBefore",
259 "setStartAfter",
260 "setEndBefore",
261 "setEndAfter",
262 "selectNode",
263 "deleteContents",
264 "insertNode",
265 "surroundContents",
266 "detach",
267 ] {
268 b.props.insert(m.into(), nv(m, dom_noop));
269 }
270 b.props.insert(
271 "extractContents".into(),
272 nv("extractContents", |it: &mut Interp, _t: Value, _a: &[Value]| {
273 document_create_range(it, Value::Undefined, &[])
274 }),
275 );
276 b.props.insert(
277 "cloneContents".into(),
278 nv("cloneContents", |it: &mut Interp, _t: Value, _a: &[Value]| {
279 document_create_range(it, Value::Undefined, &[])
280 }),
281 );
282 b.props.insert(
283 "toString".into(),
284 nv("toString", |_, _, _| Ok(Value::str(""))),
285 );
286 b.props
287 .insert("comparePoint".into(), nv("comparePoint", |_, _, _| Ok(Value::Number(0.0))));
288 b.props.insert(
289 "intersectsNode".into(),
290 nv("intersectsNode", |_, _, _| Ok(Value::Bool(false))),
291 );
292 }
293 Ok(Value::Object(r))
294}
295
296fn selection_add_range(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
297 let r = arg(a, 0);
298 if let Value::Object(ref sel_obj) = t {
299 let mut sb = sel_obj.borrow_mut();
300 sb.props.insert("_range".into(), r.clone());
301 sb.props.insert("rangeCount".into(), Value::Number(1.0));
302 if let Value::Object(ref ro) = r {
303 let rb = ro.borrow();
304 let sc = rb.props.get("startContainer").cloned().unwrap_or(Value::Null);
305 let so = rb.props.get("startOffset").cloned().unwrap_or(Value::Number(0.0));
306 let ec = rb.props.get("endContainer").cloned().unwrap_or(Value::Null);
307 let eo = rb.props.get("endOffset").cloned().unwrap_or(Value::Number(0.0));
308 let collapsed = rb.props.get("collapsed").map(|v| v.truthy()).unwrap_or(true);
309
310 sb.props.insert("anchorNode".into(), sc);
311 sb.props.insert("anchorOffset".into(), so);
312 sb.props.insert("focusNode".into(), ec);
313 sb.props.insert("focusOffset".into(), eo);
314 sb.props.insert("isCollapsed".into(), Value::Bool(collapsed));
315 sb.props.insert("type".into(), Value::str(if collapsed { "Caret" } else { "Range" }));
316 }
317 }
318 Ok(Value::Undefined)
319}
320
321fn selection_remove_all_ranges(_it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
322 if let Value::Object(ref sel_obj) = t {
323 let mut sb = sel_obj.borrow_mut();
324 sb.props.insert("_range".into(), Value::Null);
325 sb.props.insert("rangeCount".into(), Value::Number(0.0));
326 sb.props.insert("anchorNode".into(), Value::Null);
327 sb.props.insert("anchorOffset".into(), Value::Number(0.0));
328 sb.props.insert("focusNode".into(), Value::Null);
329 sb.props.insert("focusOffset".into(), Value::Number(0.0));
330 sb.props.insert("isCollapsed".into(), Value::Bool(true));
331 sb.props.insert("type".into(), Value::str("None"));
332 }
333 Ok(Value::Undefined)
334}
335
336fn selection_remove_range(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
337 let r = arg(a, 0);
338 if let Value::Object(ref sel_obj) = t {
339 let mut sb = sel_obj.borrow_mut();
340 if let Some(cur) = sb.props.get("_range") {
341 if cur.strict_eq(&r) {
342 sb.props.insert("_range".into(), Value::Null);
343 sb.props.insert("rangeCount".into(), Value::Number(0.0));
344 sb.props.insert("anchorNode".into(), Value::Null);
345 sb.props.insert("anchorOffset".into(), Value::Number(0.0));
346 sb.props.insert("focusNode".into(), Value::Null);
347 sb.props.insert("focusOffset".into(), Value::Number(0.0));
348 sb.props.insert("isCollapsed".into(), Value::Bool(true));
349 sb.props.insert("type".into(), Value::str("None"));
350 }
351 }
352 }
353 Ok(Value::Undefined)
354}
355
356fn selection_get_range_at(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
357 let idx = arg(a, 0).to_number() as i64;
358 if let Value::Object(ref sel_obj) = t {
359 let sb = sel_obj.borrow();
360 let rc = sb.props.get("rangeCount").map(|v| v.to_number() as i64).unwrap_or(0);
361 if idx == 0 && rc > 0 {
362 if let Some(r) = sb.props.get("_range") {
363 if !matches!(r, Value::Null | Value::Undefined) {
364 return Ok(r.clone());
365 }
366 }
367 }
368 return Err(it.error(alloc::format!(
369 "Failed to execute 'getRangeAt' on 'Selection': The index provided ({}) is greater than the maximum bound ({}).",
370 idx,
371 if rc > 0 { rc - 1 } else { 0 }
372 )));
373 }
374 Err(it.error("Illegal invocation"))
375}
376
377pub(crate) fn document_get_selection(it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
378 if let Some(ref sel) = it.dom.borrow().selection {
379 return Ok(sel.clone());
380 }
381 let sel = Obj::plain();
382 {
383 let mut b = sel.borrow_mut();
384 b.props
385 .insert("removeAllRanges".into(), nv("removeAllRanges", selection_remove_all_ranges));
386 b.props.insert("addRange".into(), nv("addRange", selection_add_range));
387 b.props.insert("removeRange".into(), nv("removeRange", selection_remove_range));
388 b.props.insert("getRangeAt".into(), nv("getRangeAt", selection_get_range_at));
389 b.props.insert("_range".into(), Value::Null);
390 b.props.insert(
391 "toString".into(),
392 nv("toString", |_, _, _| Ok(Value::str(""))),
393 );
394 b.props.insert("rangeCount".into(), Value::Number(0.0));
395 b.props.insert("isCollapsed".into(), Value::Bool(true));
396 b.props.insert("collapse".into(), nv("collapse", dom_noop));
397 b.props.insert("collapseToStart".into(), nv("collapseToStart", dom_noop));
398 b.props.insert("collapseToEnd".into(), nv("collapseToEnd", dom_noop));
399 b.props.insert("extend".into(), nv("extend", dom_noop));
400 b.props.insert("empty".into(), nv("empty", dom_noop));
401 b.props
402 .insert("selectAllChildren".into(), nv("selectAllChildren", dom_noop));
403 b.props
404 .insert("setBaseAndExtent".into(), nv("setBaseAndExtent", dom_noop));
405 b.props
406 .insert("deleteFromDocument".into(), nv("deleteFromDocument", dom_noop));
407 b.props.insert(
408 "containsNode".into(),
409 nv("containsNode", |_, _, _| Ok(Value::Bool(false))),
410 );
411 b.props.insert("anchorNode".into(), Value::Null);
412 b.props.insert("anchorOffset".into(), Value::Number(0.0));
413 b.props.insert("focusNode".into(), Value::Null);
414 b.props.insert("focusOffset".into(), Value::Number(0.0));
415 b.props.insert("type".into(), Value::str("None"));
416 }
417 let res = Value::Object(sel);
418 it.dom.borrow_mut().selection = Some(res.clone());
419 Ok(res)
420}
421
422pub(crate) fn document_get_elements_by_class_name(
423 it: &mut Interp,
424 _t: Value,
425 a: &[Value],
426) -> Result<Value, Value> {
427 let raw = arg(a, 0).to_js_string();
428 let sel_classes: Vec<String> = raw
429 .split_whitespace()
430 .map(|c| alloc::format!(".{}", c))
431 .collect();
432 let sel = if sel_classes.is_empty() {
433 alloc::format!(".__invalid_class__")
434 } else {
435 sel_classes.concat()
436 };
437 let items: Vec<Value> = it
438 .dom
439 .borrow()
440 .query_all(&sel)
441 .into_iter()
442 .map(|i| Value::Object(Obj::dom(i)))
443 .collect();
444 Ok(Value::Object(Obj::array(items)))
445}
446pub(crate) fn document_get_elements_by_tag_name(
447 it: &mut Interp,
448 _t: Value,
449 a: &[Value],
450) -> Result<Value, Value> {
451 let tag = arg(a, 0).to_js_string();
452 let items: Vec<Value> = it
453 .dom
454 .borrow()
455 .query_all(&tag)
456 .into_iter()
457 .map(|i| Value::Object(Obj::dom(i)))
458 .collect();
459 Ok(Value::Object(Obj::array(items)))
460}
461pub(crate) fn document_get_elements_by_tag_name_ns(
465 it: &mut Interp,
466 _t: Value,
467 a: &[Value],
468) -> Result<Value, Value> {
469 let tag = if a.len() >= 2 {
470 arg(a, 1).to_js_string()
471 } else {
472 arg(a, 0).to_js_string()
473 };
474 let items: Vec<Value> = it
475 .dom
476 .borrow()
477 .query_all(&tag)
478 .into_iter()
479 .map(|i| Value::Object(Obj::dom(i)))
480 .collect();
481 Ok(Value::Object(Obj::array(items)))
482}
483pub(crate) fn document_get_elements_by_name(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
487 let name = arg(a, 0).to_js_string();
488 let sel = alloc::format!("[name=\"{name}\"]");
489 let items: Vec<Value> = it
490 .dom
491 .borrow()
492 .query_all(&sel)
493 .into_iter()
494 .map(|i| Value::Object(Obj::dom(i)))
495 .collect();
496 Ok(Value::Object(Obj::array(items)))
497}
498
499pub(crate) fn document_exec_command(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
501 let cmd = arg(a, 0).to_js_string().to_ascii_lowercase();
502 match cmd.as_str() {
503 "copy" | "cut" | "paste" | "selectall" | "bold" | "italic" | "underline" | "delete" | "inserttext" => Ok(Value::Bool(true)),
504 _ => Ok(Value::Bool(false)),
505 }
506}
507
508pub(crate) fn doc_adopt_node(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
510 let node = arg(a, 0);
511 if matches!(node, Value::Object(_)) {
512 if let Ok(remove_fn) = it.get_property(&node, "remove") {
513 it.call_listener(&remove_fn, node.clone(), &[], "adoptNode: node.remove()");
514 }
515 Ok(node)
516 } else {
517 Ok(Value::Null)
518 }
519}
520
521pub(crate) fn doc_import_node(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
523 let node = arg(a, 0);
524 let deep = arg(a, 1).truthy();
525 if matches!(node, Value::Object(_)) {
526 if let Ok(clone_fn) = it.get_property(&node, "cloneNode") {
527 if let Ok(cloned) = it.call_value(&clone_fn, node.clone(), &[Value::Bool(deep)]) {
528 return Ok(cloned);
529 }
530 }
531 Ok(node)
532 } else {
533 Ok(Value::Null)
534 }
535}