webcomponent/
lib.rs

1#![no_std]
2use js::*;
3#[macro_use]
4extern crate alloc;
5use alloc::string::String;
6use alloc::sync::Arc;
7use alloc::vec::Vec;
8use spin::Mutex;
9
10pub type HTMLElement = f64;
11
12#[derive(Copy, Clone)]
13struct Destructable {
14    function: Option<JSFunction>,
15}
16
17pub trait CustomElement {
18    fn new(element: HTMLElement) -> Self
19    where
20        Self: core::marker::Sized + core::marker::Sync + core::marker::Send + 'static;
21    fn register(name: &str)
22    where
23        Self: core::marker::Sized + core::marker::Sync + core::marker::Send + 'static,
24    {
25        let construct = create_callback_1(|element| {
26            let el = Arc::new(Mutex::new(Self::new(element.into())));
27            let el1 = el.clone();
28            let el2 = el.clone();
29            let el3 = el.clone();
30
31            let destruct_connect = Arc::new(Mutex::new(Destructable { function: None }));
32            let connect = create_callback_0(move || {
33                el1.lock().connected();
34            });
35            destruct_connect.lock().function = Some(connect.into());
36
37            let destruct_attribute_change = Arc::new(Mutex::new(Destructable { function: None }));
38            let attribute_change = create_callback_3(move |name_obj, old_obj, new_obj| {
39                let name = cstr_to_string(name_obj as i32);
40                let old = if old_obj == -1.0 {
41                    None
42                } else {
43                    Some(cstr_to_string(old_obj as i32))
44                };
45                let new = if new_obj == -1.0 {
46                    None
47                } else {
48                    Some(cstr_to_string(new_obj as i32))
49                };
50                el3.lock().attribute_changed(name, old, new);
51            });
52
53            destruct_attribute_change.lock().function = Some(connect.into());
54
55            let destruct_disconnect = Arc::new(Mutex::new(Destructable { function: None }));
56            let destruct_disconnect2 = destruct_disconnect.clone();
57            let disconnect = create_callback_0(move || {
58                el2.lock().disconnected();
59                remove_callback(destruct_connect.lock().function.as_ref().unwrap().into());
60                remove_callback(destruct_disconnect.lock().function.as_ref().unwrap().into());
61                remove_callback(
62                    destruct_attribute_change
63                        .lock()
64                        .function
65                        .as_ref()
66                        .unwrap()
67                        .into(),
68                );
69            });
70            destruct_disconnect2.lock().function = Some(disconnect.into());
71
72            lazy_static::lazy_static! {
73                static ref FN: JSFunction= {
74                register_function(
75                    r#"function(e,a,b,c){
76                        e.addHooks(a,b,c);
77                    }"#,
78                )
79            };};
80            FN.invoke_4(element, connect, disconnect, attribute_change);
81        });
82        let attrs = Self::observed_attributes().join(",");
83        lazy_static::lazy_static! {
84            static ref FN: JSFunction= {
85            register_function(
86                r#"function(construct,elementNamePtr, elementNameLen,attrNamesPtr,attrNamesLen){
87                    const elementName = this.readUtf8FromMemory(elementNamePtr,elementNameLen);
88                    const attrNames = this.readUtf8FromMemory(attrNamesPtr,attrNamesLen);
89                    let attrs = attrNames.split(",");
90                    class GeneratedCustomElement extends HTMLElement {
91                      constructor() {
92                          super();
93                          construct(this);
94                      }
95    
96                      static get observedAttributes() {
97                        return attrs;
98                      }
99    
100                      connectedCallback() {
101                        self.connect();
102                      }
103    
104                      disconnectedCallback() {
105                        self.disconnect();
106                      }
107    
108                      attributeChangedCallback(attributeName, oldValue, newValue) {
109                        self.attributeChange(attributeName,oldValue,newValue)
110                      }
111    
112                      addHooks(connect,disconnect,attributeChange){
113                        self.connect = connect;
114                        self.disconnect = disconnect;
115                        self.attributeChange = attributeChange;
116                      }
117                    }
118    
119                    // tell the dom to associate it with an html tag name
120                    customElements.define(elementName, GeneratedCustomElement);
121                  }"#,
122            )
123        };};
124        FN.invoke_5(
125            construct,
126            name.as_ptr() as u32,
127            name.len() as u32,
128            attrs.as_ptr() as u32,
129            attrs.len() as u32,
130        );
131    }
132
133    fn observed_attributes() -> Vec<&'static str> {
134        vec![]
135    }
136
137    fn created(&mut self) {}
138    fn connected(&mut self) {}
139    fn disconnected(&mut self) {}
140    fn attribute_changed(
141        &mut self,
142        _name: String,
143        _old_value: Option<String>,
144        _new_value: Option<String>,
145    ) {
146    }
147}