1use super::*;
10
11fn builtin_member(it: &mut Interp, this: &Value, name: &str) -> Result<Value, Value> {
13 if let Some(idx) = this_dom_idx(this) {
14 return Ok(it.dom_builtin_get(idx, name));
15 }
16 let host = match this {
17 Value::Object(o) => match &o.borrow().kind {
18 ObjKind::Host(tag) => Some(tag.clone()),
19 _ => None,
20 },
21 _ => None,
22 };
23 match host {
24 Some(tag) => Ok(it.dom_get_property(crate::os_lib::js::interp::DomDisp::Host(tag), name)),
25 None => Err(it.type_error(alloc::format!("Illegal invocation: {}", name))),
26 }
27}
28
29fn forward(it: &mut Interp, this: Value, a: &[Value], name: &str) -> Result<Value, Value> {
31 let f = builtin_member(it, &this, name)?;
32 if !matches!(&f, Value::Object(o) if o.borrow().is_callable()) {
33 return Err(it.type_error(alloc::format!("{} is not a function", name)));
34 }
35 it.call_value(&f, this, a)
36}
37
38macro_rules! forwarders {
39 ($($fname:ident => $key:literal),* $(,)?) => {
40 $(
41 fn $fname(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
42 forward(it, this, a, $key)
43 }
44 )*
45 const FORWARDS: &[(&str, NativeFn)] = &[$(($key, $fname)),*];
47 };
48}
49
50forwarders! {
51 f_append_child => "appendChild",
52 f_insert_before => "insertBefore",
53 f_remove_child => "removeChild",
54 f_replace_child => "replaceChild",
55 f_clone_node => "cloneNode",
56 f_contains => "contains",
57 f_has_child_nodes => "hasChildNodes",
58 f_get_root_node => "getRootNode",
59 f_is_equal_node => "isEqualNode",
60 f_compare_document_position => "compareDocumentPosition",
61 f_normalize => "normalize",
62 f_get_attribute => "getAttribute",
63 f_set_attribute => "setAttribute",
64 f_remove_attribute => "removeAttribute",
65 f_has_attribute => "hasAttribute",
66 f_toggle_attribute => "toggleAttribute",
67 f_get_attribute_names => "getAttributeNames",
68 f_has_attributes => "hasAttributes",
69 f_query_selector => "querySelector",
70 f_query_selector_all => "querySelectorAll",
71 f_get_elements_by_tag_name => "getElementsByTagName",
72 f_get_elements_by_class_name => "getElementsByClassName",
73 f_closest => "closest",
74 f_matches => "matches",
75 f_get_bounding_client_rect => "getBoundingClientRect",
76 f_get_client_rects => "getClientRects",
77 f_attach_shadow => "attachShadow",
78 f_insert_adjacent_html => "insertAdjacentHTML",
79 f_insert_adjacent_element => "insertAdjacentElement",
80 f_insert_adjacent_text => "insertAdjacentText",
81 f_remove => "remove",
82 f_append => "append",
83 f_prepend => "prepend",
84 f_after => "after",
85 f_before => "before",
86 f_replace_with => "replaceWith",
87 f_replace_children => "replaceChildren",
88 f_scroll_into_view => "scrollIntoView",
89 f_focus => "focus",
90 f_blur => "blur",
91 f_click => "click",
92}
93
94fn forward_get(it: &mut Interp, this: Value, name: &str) -> Result<Value, Value> {
96 builtin_member(it, &this, name)
97}
98
99fn forward_set(it: &mut Interp, this: Value, a: &[Value], name: &str) -> Result<Value, Value> {
101 if let Some(idx) = this_dom_idx(&this) {
102 it.dom_builtin_set(idx, name, arg(a, 0));
103 return Ok(Value::Undefined);
104 }
105 let host = match &this {
106 Value::Object(o) => match &o.borrow().kind {
107 ObjKind::Host(tag) => Some(tag.clone()),
108 _ => None,
109 },
110 _ => None,
111 };
112 match host {
113 Some(tag) => {
114 it.dom_set_property(crate::os_lib::js::interp::DomDisp::Host(tag), name, arg(a, 0));
115 Ok(Value::Undefined)
116 }
117 None => Err(it.type_error(alloc::format!("Illegal invocation: {}", name))),
118 }
119}
120
121macro_rules! forwarders_doc {
122 ($($fname:ident => $key:literal),* $(,)?) => {
123 $(
124 fn $fname(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
125 forward(it, this, a, $key)
126 }
127 )*
128 const DOC_FORWARDS: &[(&str, NativeFn)] = &[$(($key, $fname)),*];
130 };
131}
132
133macro_rules! accessors {
134 ($($node:literal $gname:ident $sname:ident => $key:literal $writable:literal),* $(,)?) => {
135 $(
136 fn $gname(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
137 forward_get(it, this, $key)
138 }
139 fn $sname(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
140 forward_set(it, this, a, $key)
141 }
142 )*
143 const ACCESSORS: &[(bool, &str, NativeFn, NativeFn, bool)] = &[$(($node, $key, $gname, $sname, $writable)),*];
145 };
146}
147
148accessors! {
152 true g_parent_node s_parent_node => "parentNode" false,
153 true g_parent_element s_parent_element => "parentElement" false,
154 true g_first_child s_first_child => "firstChild" false,
155 true g_last_child s_last_child => "lastChild" false,
156 true g_previous_sibling s_previous_sibling => "previousSibling" false,
157 true g_next_sibling s_next_sibling => "nextSibling" false,
158 true g_child_nodes s_child_nodes => "childNodes" false,
159 true g_is_connected s_is_connected => "isConnected" false,
160 true g_owner_document s_owner_document => "ownerDocument" false,
161 true g_node_type s_node_type => "nodeType" false,
162 true g_node_name s_node_name => "nodeName" false,
163 true g_text_content s_text_content => "textContent" true,
164 false g_children s_children => "children" false,
165 false g_first_element_child s_first_element_child => "firstElementChild" false,
166 false g_last_element_child s_last_element_child => "lastElementChild" false,
167 false g_previous_element_sibling s_previous_element_sibling => "previousElementSibling" false,
168 false g_next_element_sibling s_next_element_sibling => "nextElementSibling" false,
169 false g_child_element_count s_child_element_count => "childElementCount" false,
170 false g_inner_html s_inner_html => "innerHTML" true,
171 false g_outer_html s_outer_html => "outerHTML" true,
172 false g_class_name s_class_name => "className" true,
173 false g_id s_id => "id" true,
174 false g_slot s_slot => "slot" true,
175 false g_shadow_root s_shadow_root => "shadowRoot" false,
176 false g_assigned_slot s_assigned_slot => "assignedSlot" false,
177 false g_tag_name s_tag_name => "tagName" false,
178 false g_local_name s_local_name => "localName" false,
179}
180
181forwarders_doc! {
182 d_create_element => "createElement",
183 d_create_element_ns => "createElementNS",
184 d_create_text_node => "createTextNode",
185 d_create_comment => "createComment",
186 d_create_document_fragment => "createDocumentFragment",
187 d_import_node => "importNode",
188 d_adopt_node => "adoptNode",
189 d_get_element_by_id => "getElementById",
190 d_create_tree_walker => "createTreeWalker",
191 d_create_event => "createEvent",
192 d_create_range => "createRange",
193 d_element_from_point => "elementFromPoint",
194}
195
196const NODE_METHODS: &[&str] = &[
198 "appendChild",
199 "insertBefore",
200 "removeChild",
201 "replaceChild",
202 "cloneNode",
203 "contains",
204 "hasChildNodes",
205 "getRootNode",
206 "isEqualNode",
207 "compareDocumentPosition",
208 "normalize",
209];
210
211pub(crate) fn install(node_proto: &ObjRef, element_proto: &ObjRef) {
213 for (name, f) in FORWARDS {
214 let target = if NODE_METHODS.contains(name) { node_proto } else { element_proto };
215 let mut b = target.borrow_mut();
216 if !b.props.contains_key(*name) {
217 b.props.insert(String::from(*name), nv(name, *f));
218 }
219 }
220 for (on_node, name, g, set, writable) in ACCESSORS {
221 let target = if *on_node { node_proto } else { element_proto };
222 let mut b = target.borrow_mut();
223 if !b.accessors.contains_key(name) && !b.props.contains_key(*name) {
224 b.accessors.insert(
225 String::from(*name),
226 Accessor { get: Some(nv(name, *g)), set: if *writable { Some(nv(name, *set)) } else { None } },
227 );
228 }
229 }
230}
231
232pub(crate) fn named_node_map_get(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
234 let name = arg(a, 0).to_js_string();
235 if let Value::Object(o) = &this {
236 if let Some(v @ Value::Object(_)) = o.borrow().props.get(&name) {
237 return Ok(v.clone());
238 }
239 }
240 Ok(Value::Null)
241}
242
243pub(crate) fn named_node_map_item(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
245 let i = arg(a, 0).to_number();
246 if let Value::Object(o) = &this {
247 if let ObjKind::Array(items) = &o.borrow().kind {
248 if i >= 0.0 {
249 if let Some(v) = items.get(i as usize) {
250 return Ok(v.clone());
251 }
252 }
253 }
254 }
255 Ok(Value::Null)
256}
257
258pub(crate) fn install_document(doc_proto: &ObjRef) {
260 let mut b = doc_proto.borrow_mut();
261 for (name, f) in DOC_FORWARDS {
262 if !b.props.contains_key(*name) {
263 b.props.insert(String::from(*name), nv(name, *f));
264 }
265 }
266}
267
268pub(crate) fn install_fragment(df_proto: &ObjRef) {
271 const NAMES: &[&str] = &["querySelector", "querySelectorAll", "append", "prepend", "replaceChildren"];
272 let mut b = df_proto.borrow_mut();
273 for (name, f) in FORWARDS {
274 if NAMES.contains(name) && !b.props.contains_key(*name) {
275 b.props.insert(String::from(*name), nv(name, *f));
276 }
277 }
278 for (_, name, g, _, _) in ACCESSORS {
279 if matches!(*name, "children" | "firstElementChild" | "lastElementChild" | "childElementCount") && !b.accessors.contains_key(name) {
280 b.accessors.insert(String::from(*name), Accessor { get: Some(nv(name, *g)), set: None });
281 }
282 }
283}