use boa_engine::{Context, JsResult, JsValue, js_string};
use crate::tdom::node::{Node, NodeType};
use super::{node_wrapper, with_state};
pub(super) fn native_get_element_by_id(
_this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let id = args
.first()
.map(|v| v.to_string(context).map(|s| s.to_std_string_lossy()))
.transpose()?;
let Some(id) = id else {
return Ok(JsValue::null());
};
let Some(rc) = with_state() else {
return Ok(JsValue::null());
};
let st = rc.borrow();
let found = st
.arena
.iter()
.find(|(_, n)| n.attributes.get("id").map(String::as_str) == Some(id.as_str()))
.map(|(nid, _)| nid);
match found {
Some(node_id) => node_wrapper(context, node_id),
None => Ok(JsValue::null()),
}
}
pub(super) fn native_create_text_node(
_this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let data = args
.first()
.map(|v| v.to_string(context).map(|s| s.to_std_string_lossy()))
.transpose()?
.unwrap_or_default();
let Some(rc) = with_state() else {
return Ok(JsValue::null());
};
let mut st = rc.borrow_mut();
let text_id = st.arena.insert(Node::text(&data));
drop(st);
node_wrapper(context, text_id)
}
pub(super) fn native_create_comment(
_this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let data = args
.first()
.map(|v| v.to_string(context).map(|s| s.to_std_string_lossy()))
.transpose()?
.unwrap_or_default();
let Some(rc) = with_state() else {
return Ok(JsValue::null());
};
let mut st = rc.borrow_mut();
let comment_id = st.arena.insert(Node::comment(&data));
drop(st);
node_wrapper(context, comment_id)
}
pub(super) fn native_create_document_fragment(
_this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let Some(rc) = with_state() else {
return Ok(JsValue::null());
};
let mut st = rc.borrow_mut();
let fragment_id = st.arena.insert(Node::fragment());
drop(st);
node_wrapper(context, fragment_id)
}
pub(super) fn native_get_document_element(
_this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
find_descendant_by_type(context, NodeType::Html)
}
pub(super) fn native_get_document_body(
_this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
find_descendant_by_type(context, NodeType::Body)
}
pub(super) fn native_get_document_head(
_this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
find_descendant_by_type(context, NodeType::Head)
}
pub(super) fn native_get_document_doctype(
_this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
find_descendant_by_type(context, NodeType::DocumentType)
}
fn find_descendant_by_type(context: &mut Context, node_type: NodeType) -> JsResult<JsValue> {
let Some(rc) = with_state() else {
return Ok(JsValue::null());
};
let st = rc.borrow();
let found = Node::descendants(&st.arena, st.root_id)
.find(|(_, n)| n.node_type == node_type)
.map(|(id, _)| id);
match found {
Some(id) => node_wrapper(context, id),
None => Ok(JsValue::null()),
}
}
pub(super) fn native_get_document_title(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
let Some(rc) = with_state() else {
return Ok(js_string!().into());
};
let st = rc.borrow();
let title = Node::descendants(&st.arena, st.root_id)
.find(|(_, n)| n.node_type == NodeType::Title)
.map(|(id, _)| {
Node::descendants(&st.arena, id)
.filter(|(_, n)| n.node_type == NodeType::Text)
.map(|(_, n)| n.data.as_deref().unwrap_or_default())
.collect::<String>()
})
.unwrap_or_default();
Ok(js_string!(title).into())
}
pub(super) fn native_set_document_title(
_this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let value = args
.first()
.map(|v| v.to_string(context).map(|s| s.to_std_string_lossy()))
.transpose()?
.unwrap_or_default();
let Some(rc) = with_state() else {
return Ok(JsValue::undefined());
};
let mut st = rc.borrow_mut();
let title_id = Node::descendants(&st.arena, st.root_id)
.find(|(_, n)| n.node_type == NodeType::Title)
.map(|(id, _)| id);
if let Some(id) = title_id {
let text_id = st
.arena
.get(id)
.map(|n| n.children.clone())
.unwrap_or_default()
.into_iter()
.find(|&c| st.arena.get(c).map(|n| n.node_type) == Some(NodeType::Text));
match text_id {
Some(text_id) => {
if let Some(t) = st.arena.get_mut(text_id) {
t.data = Some(value);
}
}
None => {
let text_id = st.arena.insert(Node::text(&value));
if let Some(title) = st.arena.get_mut(id) {
title.children.push(text_id);
}
if let Some(t) = st.arena.get_mut(text_id) {
t.parent = Some(id);
}
}
}
super::mark_dirty(&mut st, id);
return Ok(JsValue::undefined());
}
let host = Node::descendants(&st.arena, st.root_id)
.find(|(_, n)| n.node_type == NodeType::Head)
.map(|(id, _)| id)
.unwrap_or(st.root_id);
let title_id = st.arena.insert(Node::from(NodeType::Title));
let text_id = st.arena.insert(Node::text(&value));
if let Some(title) = st.arena.get_mut(title_id) {
title.children.push(text_id);
}
if let Some(t) = st.arena.get_mut(text_id) {
t.parent = Some(title_id);
}
if let Some(h) = st.arena.get_mut(host) {
h.children.push(title_id);
}
if let Some(t) = st.arena.get_mut(title_id) {
t.parent = Some(host);
}
super::mark_dirty(&mut st, host);
Ok(JsValue::undefined())
}
pub(super) fn native_create_element(
_this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let tag = args
.first()
.map(|v| v.to_string(context).map(|s| s.to_std_string_lossy()))
.transpose()?;
let tag = tag.unwrap_or_else(|| "div".to_string());
let node_type = crate::html::html_parse::tag_name_to_node_type(&tag).unwrap_or(NodeType::Div);
let Some(rc) = with_state() else {
return Ok(JsValue::null());
};
let mut st = rc.borrow_mut();
let node_id = st.arena.insert(Node::from(node_type));
drop(st);
node_wrapper(context, node_id)
}
pub(super) fn native_get_character_set(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Ok(js_string!("UTF-8").into())
}
pub(super) fn native_get_content_type(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Ok(js_string!("text/html").into())
}
pub(super) fn native_get_compat_mode(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Ok(js_string!("CSS1Compat").into())
}
pub(super) fn native_get_document_uri(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
let Some(rc) = with_state() else {
return Ok(js_string!().into());
};
let st = rc.borrow();
let uri = crate::tdom::node::document_base_href(&st.arena, st.root_id).unwrap_or_default();
Ok(js_string!(uri).into())
}
pub(super) fn native_has_focus(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Ok(JsValue::from(true))
}
pub(super) fn native_get_visibility_state(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Ok(js_string!("visible").into())
}
pub(super) fn native_get_hidden(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Ok(JsValue::from(false))
}