mod attrs;
mod builtins;
mod console;
mod content;
mod document;
mod events;
mod fetch;
mod global_attrs;
mod mutation;
mod style;
mod timers;
mod traverse;
mod view;
mod wrapper;
pub(crate) use builtins::install_builtins;
#[allow(unused_imports)]
pub(crate) use events::{
add_listener, clear_attr_handler, clear_node_attr_handler, clear_node_listeners,
get_attr_handler, get_listeners, make_event_object, parse_event_type, remove_listener,
set_attr_handler,
};
pub(crate) use fetch::response_object;
pub(crate) use view::{get_window_listeners, mql_last_matches, mql_matches_now, mql_query};
use boa_engine::{
Context, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, js_string,
object::{FunctionObjectBuilder, ObjectInitializer, builtins::JsFunction},
};
use slotmap::Key;
use crate::tdom::node::{NodeArena, NodeId, NodeType};
use super::ScriptState;
use wrapper::make_node_wrapper;
pub(crate) fn with_state() -> Option<std::rc::Rc<std::cell::RefCell<ScriptState>>> {
super::ENGINE_STATE.with(|s| s.borrow().clone())
}
pub(crate) fn mark_dirty(st: &mut ScriptState, node_id: NodeId) {
st.dirty.nodes.push(node_id);
}
pub(crate) fn node_id_to_f64(id: NodeId) -> f64 {
Key::data(&id).as_ffi() as f64
}
pub(crate) fn f64_to_node_id(v: f64) -> NodeId {
u64_to_node_id(v as u64)
}
pub(crate) fn u64_to_node_id(v: u64) -> NodeId {
slotmap::KeyData::from_ffi(v).into()
}
fn this_node_id(this: &JsValue, context: &mut Context) -> JsResult<NodeId> {
let obj = this
.to_object(context)
.map_err(|_| JsNativeError::typ().with_message("expected an element object"))?;
let v = obj.get(js_string!("_nodeId"), context)?;
let ffi = v
.to_number(context)
.map_err(|_| JsNativeError::typ().with_message("element has no _nodeId"))?;
Ok(f64_to_node_id(ffi))
}
fn js_fn(context: &mut Context, native: NativeFunction) -> JsFunction {
FunctionObjectBuilder::new(context.realm(), native).build()
}
pub(crate) fn node_wrapper(context: &mut Context, node_id: NodeId) -> JsResult<JsValue> {
let key = node_id_to_f64(node_id);
let registry = registry_elements(context)?;
let cached = registry.get(key, context)?;
if !cached.is_undefined() {
return Ok(cached);
}
let wrapper = make_node_wrapper(context, node_id)?;
registry.set(key, wrapper.clone(), false, context)?;
Ok(wrapper)
}
fn registry_elements(context: &mut Context) -> JsResult<JsObject> {
let global = context.global_object();
let obj = global.get(js_string!("__htuiElements"), context)?;
match obj.to_object(context) {
Ok(o) => Ok(o),
Err(_) => {
let mut init = ObjectInitializer::new(context);
let o = init.build();
global.set(js_string!("__htuiElements"), o.clone(), false, context)?;
Ok(o)
}
}
}
pub(super) fn wrapper_is_cached(context: &mut Context, node_id: NodeId) -> JsResult<bool> {
let key = node_id_to_f64(node_id);
let cached = registry_elements(context)?.get(key, context)?;
Ok(!cached.is_undefined())
}
fn collect_text(arena: &NodeArena, node_id: NodeId) -> String {
let Some(node) = arena.get(node_id) else {
return String::new();
};
if node.node_type == NodeType::Text {
return node.data.clone().unwrap_or_default();
}
node.text_content(arena)
}