1#![allow(dead_code)]
8
9use alloc::{
10 collections::BTreeMap,
11 string::{String, ToString},
12 vec::Vec,
13};
14
15use super::events::{EventDispatcher, EventType, NodeId};
16
17#[derive(Debug, Clone)]
23pub struct TimerEntry {
24 pub id: u32,
26 pub callback_id: usize,
28 pub fire_at: u64,
30 pub interval: u64,
32 pub cancelled: bool,
34}
35
36pub struct TimerQueue {
38 timers: Vec<TimerEntry>,
40 next_id: u32,
42 current_tick: u64,
44}
45
46impl Default for TimerQueue {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl TimerQueue {
53 pub fn new() -> Self {
54 Self {
55 timers: Vec::new(),
56 next_id: 1,
57 current_tick: 0,
58 }
59 }
60
61 pub fn set_timeout(&mut self, callback_id: usize, delay_ticks: u64) -> u32 {
64 let id = self.next_id;
65 self.next_id += 1;
66 self.timers.push(TimerEntry {
67 id,
68 callback_id,
69 fire_at: self.current_tick + delay_ticks,
70 interval: 0,
71 cancelled: false,
72 });
73 id
74 }
75
76 pub fn set_interval(&mut self, callback_id: usize, interval_ticks: u64) -> u32 {
79 let id = self.next_id;
80 self.next_id += 1;
81 self.timers.push(TimerEntry {
82 id,
83 callback_id,
84 fire_at: self.current_tick + interval_ticks,
85 interval: interval_ticks,
86 cancelled: false,
87 });
88 id
89 }
90
91 pub fn clear_timer(&mut self, timer_id: u32) -> bool {
93 for timer in &mut self.timers {
94 if timer.id == timer_id {
95 timer.cancelled = true;
96 return true;
97 }
98 }
99 false
100 }
101
102 pub fn tick(&mut self) -> Vec<usize> {
104 self.current_tick += 1;
105 let mut expired = Vec::new();
106
107 let mut i = 0;
108 while i < self.timers.len() {
109 if self.timers[i].cancelled {
110 self.timers.swap_remove(i);
111 continue;
112 }
113 if self.timers[i].fire_at <= self.current_tick {
114 expired.push(self.timers[i].callback_id);
115 if self.timers[i].interval > 0 {
116 self.timers[i].fire_at = self.current_tick + self.timers[i].interval;
118 i += 1;
119 } else {
120 self.timers.swap_remove(i);
121 }
122 } else {
123 i += 1;
124 }
125 }
126
127 expired
128 }
129
130 pub fn pending_count(&self) -> usize {
132 self.timers.iter().filter(|t| !t.cancelled).count()
133 }
134
135 pub fn current_tick(&self) -> u64 {
137 self.current_tick
138 }
139}
140
141#[derive(Debug, Clone, Default)]
147pub struct DomNode {
148 pub id: NodeId,
150 pub tag: String,
152 pub element_id: String,
154 pub class_list: Vec<String>,
156 pub text_content: String,
158 pub inner_html_content: String,
160 pub attributes: BTreeMap<String, String>,
162 pub children: Vec<NodeId>,
164 pub parent: Option<NodeId>,
166 pub style: BTreeMap<String, String>,
168}
169
170impl DomNode {
171 pub fn new(id: NodeId, tag: &str) -> Self {
172 Self {
173 id,
174 tag: tag.to_string(),
175 ..Default::default()
176 }
177 }
178}
179
180pub struct DomApi {
186 nodes: Vec<DomNode>,
188 id_map: BTreeMap<String, NodeId>,
190 pub event_dispatcher: EventDispatcher,
192 pub timer_queue: TimerQueue,
194 pub console_output: Vec<String>,
196 pub navigation_requests: Vec<String>,
198}
199
200impl Default for DomApi {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206impl DomApi {
207 pub fn new() -> Self {
208 let root = DomNode::new(0, "html");
210 Self {
211 nodes: alloc::vec![root],
212 id_map: BTreeMap::new(),
213 event_dispatcher: EventDispatcher::new(),
214 timer_queue: TimerQueue::new(),
215 console_output: Vec::new(),
216 navigation_requests: Vec::new(),
217 }
218 }
219
220 pub fn get_node(&self, id: NodeId) -> Option<&DomNode> {
222 self.nodes.get(id)
223 }
224
225 pub fn get_node_mut(&mut self, id: NodeId) -> Option<&mut DomNode> {
227 self.nodes.get_mut(id)
228 }
229
230 pub fn node_count(&self) -> usize {
232 self.nodes.len()
233 }
234
235 pub fn get_element_by_id(&self, element_id: &str) -> Option<NodeId> {
239 self.id_map.get(element_id).copied()
240 }
241
242 pub fn create_element(&mut self, tag: &str) -> NodeId {
244 let id = self.nodes.len();
245 self.nodes.push(DomNode::new(id, tag));
246 id
247 }
248
249 pub fn create_text_node(&mut self, text: &str) -> NodeId {
251 let id = self.nodes.len();
252 let mut node = DomNode::new(id, "#text");
253 node.text_content = text.to_string();
254 self.nodes.push(node);
255 id
256 }
257
258 pub fn query_selector(&self, selector: &str) -> Option<NodeId> {
260 if let Some(id_part) = selector.strip_prefix('#') {
261 return self.get_element_by_id(id_part);
262 }
263 for node in &self.nodes {
264 if node.tag == selector {
265 return Some(node.id);
266 }
267 }
268 None
269 }
270
271 pub fn get_attribute(&self, node_id: NodeId, name: &str) -> Option<String> {
275 self.nodes
276 .get(node_id)
277 .and_then(|n| n.attributes.get(name).cloned())
278 }
279
280 pub fn set_attribute(&mut self, node_id: NodeId, name: &str, value: &str) {
282 if let Some(node) = self.nodes.get_mut(node_id) {
283 node.attributes.insert(name.to_string(), value.to_string());
284 if name == "id" {
285 let old_id = node.element_id.clone();
286 if !old_id.is_empty() {
287 self.id_map.remove(&old_id);
288 }
289 node.element_id = value.to_string();
290 self.id_map.insert(value.to_string(), node_id);
291 }
292 }
293 }
294
295 pub fn append_child(&mut self, parent_id: NodeId, child_id: NodeId) -> bool {
297 if parent_id >= self.nodes.len() || child_id >= self.nodes.len() {
298 return false;
299 }
300 if parent_id == child_id {
301 return false;
302 }
303
304 if let Some(old_parent) = self.nodes[child_id].parent {
306 if let Some(parent_node) = self.nodes.get_mut(old_parent) {
307 parent_node.children.retain(|&c| c != child_id);
308 }
309 }
310
311 self.nodes[parent_id].children.push(child_id);
312 self.nodes[child_id].parent = Some(parent_id);
313
314 self.event_dispatcher
315 .node_tree_mut()
316 .set_parent(child_id, parent_id);
317
318 true
319 }
320
321 pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) -> bool {
323 if let Some(parent) = self.nodes.get_mut(parent_id) {
324 let before = parent.children.len();
325 parent.children.retain(|&c| c != child_id);
326 if parent.children.len() < before {
327 if let Some(child) = self.nodes.get_mut(child_id) {
328 child.parent = None;
329 }
330 return true;
331 }
332 }
333 false
334 }
335
336 pub fn get_text_content(&self, node_id: NodeId) -> String {
338 self.nodes
339 .get(node_id)
340 .map(|n| n.text_content.clone())
341 .unwrap_or_default()
342 }
343
344 pub fn set_text_content(&mut self, node_id: NodeId, text: &str) {
346 if let Some(node) = self.nodes.get_mut(node_id) {
347 node.text_content = text.to_string();
348 node.children.clear();
349 }
350 }
351
352 pub fn get_inner_html(&self, node_id: NodeId) -> String {
354 self.nodes
355 .get(node_id)
356 .map(|n| n.inner_html_content.clone())
357 .unwrap_or_default()
358 }
359
360 pub fn set_inner_html(&mut self, node_id: NodeId, html: &str) {
362 if let Some(node) = self.nodes.get_mut(node_id) {
363 node.inner_html_content = html.to_string();
364 node.children.clear();
365 }
366 }
367
368 pub fn set_style_property(&mut self, node_id: NodeId, name: &str, value: &str) {
370 if let Some(node) = self.nodes.get_mut(node_id) {
371 node.style.insert(name.to_string(), value.to_string());
372 }
373 }
374
375 pub fn get_style_property(&self, node_id: NodeId, name: &str) -> Option<String> {
377 self.nodes
378 .get(node_id)
379 .and_then(|n| n.style.get(name).cloned())
380 }
381
382 pub fn add_event_listener(
386 &mut self,
387 node_id: NodeId,
388 event_type: EventType,
389 callback_id: usize,
390 ) {
391 self.event_dispatcher
392 .add_event_listener(node_id, event_type, callback_id, false);
393 }
394
395 pub fn remove_event_listener(
397 &mut self,
398 node_id: NodeId,
399 event_type: EventType,
400 callback_id: usize,
401 ) {
402 self.event_dispatcher
403 .remove_event_listener(node_id, event_type, callback_id, false);
404 }
405
406 pub fn console_log(&mut self, message: &str) {
410 self.console_output.push(message.to_string());
411 }
412
413 pub fn console_error(&mut self, message: &str) {
415 self.console_output
416 .push(alloc::format!("[ERROR] {}", message));
417 }
418
419 pub fn window_alert(&mut self, message: &str) {
423 self.console_output
424 .push(alloc::format!("[ALERT] {}", message));
425 }
426
427 pub fn register_element_id(&mut self, node_id: NodeId, element_id: &str) {
429 if let Some(node) = self.nodes.get_mut(node_id) {
430 node.element_id = element_id.to_string();
431 }
432 self.id_map.insert(element_id.to_string(), node_id);
433 }
434}
435
436#[cfg(test)]
441mod tests {
442 use alloc::{string::ToString, vec};
443
444 use super::*;
445
446 #[test]
447 fn test_dom_api_new() {
448 let api = DomApi::new();
449 assert_eq!(api.node_count(), 1);
450 assert_eq!(api.get_node(0).unwrap().tag, "html");
451 }
452
453 #[test]
454 fn test_create_element() {
455 let mut api = DomApi::new();
456 let div = api.create_element("div");
457 assert_eq!(div, 1);
458 assert_eq!(api.get_node(div).unwrap().tag, "div");
459 }
460
461 #[test]
462 fn test_create_text_node() {
463 let mut api = DomApi::new();
464 let text = api.create_text_node("Hello");
465 assert_eq!(api.get_node(text).unwrap().text_content, "Hello");
466 }
467
468 #[test]
469 fn test_get_element_by_id() {
470 let mut api = DomApi::new();
471 let div = api.create_element("div");
472 api.register_element_id(div, "main");
473 assert_eq!(api.get_element_by_id("main"), Some(div));
474 assert_eq!(api.get_element_by_id("nonexistent"), None);
475 }
476
477 #[test]
478 fn test_set_attribute() {
479 let mut api = DomApi::new();
480 let div = api.create_element("div");
481 api.set_attribute(div, "class", "container");
482 assert_eq!(
483 api.get_attribute(div, "class"),
484 Some("container".to_string())
485 );
486 }
487
488 #[test]
489 fn test_set_attribute_id() {
490 let mut api = DomApi::new();
491 let div = api.create_element("div");
492 api.set_attribute(div, "id", "my-div");
493 assert_eq!(api.get_element_by_id("my-div"), Some(div));
494 }
495
496 #[test]
497 fn test_append_child() {
498 let mut api = DomApi::new();
499 let parent = api.create_element("div");
500 let child = api.create_element("span");
501 assert!(api.append_child(parent, child));
502 assert_eq!(api.get_node(parent).unwrap().children, vec![child]);
503 assert_eq!(api.get_node(child).unwrap().parent, Some(parent));
504 }
505
506 #[test]
507 fn test_remove_child() {
508 let mut api = DomApi::new();
509 let parent = api.create_element("div");
510 let child = api.create_element("span");
511 api.append_child(parent, child);
512 assert!(api.remove_child(parent, child));
513 assert!(api.get_node(parent).unwrap().children.is_empty());
514 assert_eq!(api.get_node(child).unwrap().parent, None);
515 }
516
517 #[test]
518 fn test_text_content() {
519 let mut api = DomApi::new();
520 let div = api.create_element("div");
521 api.set_text_content(div, "Hello World");
522 assert_eq!(api.get_text_content(div), "Hello World");
523 }
524
525 #[test]
526 fn test_inner_html() {
527 let mut api = DomApi::new();
528 let div = api.create_element("div");
529 api.set_inner_html(div, "<p>Test</p>");
530 assert_eq!(api.get_inner_html(div), "<p>Test</p>");
531 }
532
533 #[test]
534 fn test_style_property() {
535 let mut api = DomApi::new();
536 let div = api.create_element("div");
537 api.set_style_property(div, "color", "red");
538 assert_eq!(
539 api.get_style_property(div, "color"),
540 Some("red".to_string())
541 );
542 assert_eq!(api.get_style_property(div, "margin"), None);
543 }
544
545 #[test]
546 fn test_query_selector_tag() {
547 let mut api = DomApi::new();
548 let _div = api.create_element("div");
549 let p = api.create_element("p");
550 assert_eq!(api.query_selector("p"), Some(p));
551 }
552
553 #[test]
554 fn test_query_selector_id() {
555 let mut api = DomApi::new();
556 let div = api.create_element("div");
557 api.register_element_id(div, "main");
558 assert_eq!(api.query_selector("#main"), Some(div));
559 }
560
561 #[test]
562 fn test_console_log() {
563 let mut api = DomApi::new();
564 api.console_log("test message");
565 assert_eq!(api.console_output.len(), 1);
566 assert_eq!(api.console_output[0], "test message");
567 }
568
569 #[test]
570 fn test_console_error() {
571 let mut api = DomApi::new();
572 api.console_error("oh no");
573 assert_eq!(api.console_output[0], "[ERROR] oh no");
574 }
575
576 #[test]
577 fn test_window_alert() {
578 let mut api = DomApi::new();
579 api.window_alert("hello");
580 assert_eq!(api.console_output[0], "[ALERT] hello");
581 }
582
583 #[test]
584 fn test_timer_set_timeout() {
585 let mut tq = TimerQueue::new();
586 let id = tq.set_timeout(42, 5);
587 assert_eq!(id, 1);
588 assert_eq!(tq.pending_count(), 1);
589 }
590
591 #[test]
592 fn test_timer_fires() {
593 let mut tq = TimerQueue::new();
594 tq.set_timeout(42, 3);
595 assert!(tq.tick().is_empty());
596 assert!(tq.tick().is_empty());
597 let expired = tq.tick();
598 assert_eq!(expired, vec![42]);
599 assert_eq!(tq.pending_count(), 0);
600 }
601
602 #[test]
603 fn test_timer_interval() {
604 let mut tq = TimerQueue::new();
605 tq.set_interval(99, 2);
606 assert!(tq.tick().is_empty());
607 let e2 = tq.tick();
608 assert_eq!(e2, vec![99]);
609 assert!(tq.tick().is_empty());
610 let e4 = tq.tick();
611 assert_eq!(e4, vec![99]);
612 }
613
614 #[test]
615 fn test_timer_cancel() {
616 let mut tq = TimerQueue::new();
617 let id = tq.set_timeout(42, 5);
618 assert!(tq.clear_timer(id));
619 for _ in 0..10 {
620 assert!(tq.tick().is_empty());
621 }
622 }
623
624 #[test]
625 fn test_timer_cancel_nonexistent() {
626 let mut tq = TimerQueue::new();
627 assert!(!tq.clear_timer(999));
628 }
629
630 #[test]
631 fn test_timer_queue_default() {
632 let tq = TimerQueue::default();
633 assert_eq!(tq.pending_count(), 0);
634 assert_eq!(tq.current_tick(), 0);
635 }
636
637 #[test]
638 fn test_append_child_self() {
639 let mut api = DomApi::new();
640 let div = api.create_element("div");
641 assert!(!api.append_child(div, div));
642 }
643
644 #[test]
645 fn test_reparent_child() {
646 let mut api = DomApi::new();
647 let p1 = api.create_element("div");
648 let p2 = api.create_element("div");
649 let child = api.create_element("span");
650 api.append_child(p1, child);
651 api.append_child(p2, child);
652 assert!(api.get_node(p1).unwrap().children.is_empty());
653 assert_eq!(api.get_node(p2).unwrap().children, vec![child]);
654 }
655
656 #[test]
657 fn test_add_event_listener_via_api() {
658 let mut api = DomApi::new();
659 let div = api.create_element("div");
660 api.add_event_listener(div, EventType::Click, 100);
661 assert_eq!(api.event_dispatcher.listeners_for(div).len(), 1);
662 }
663
664 #[test]
665 fn test_remove_event_listener_via_api() {
666 let mut api = DomApi::new();
667 let div = api.create_element("div");
668 api.add_event_listener(div, EventType::Click, 100);
669 api.remove_event_listener(div, EventType::Click, 100);
670 assert_eq!(api.event_dispatcher.listeners_for(div).len(), 0);
671 }
672
673 #[test]
674 fn test_dom_node_default() {
675 let node = DomNode::default();
676 assert!(node.tag.is_empty());
677 assert!(node.children.is_empty());
678 assert!(node.parent.is_none());
679 }
680}