pub(crate) fn get_external_references() -> Vec<v8::ExternalReference> {
vec![
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(event_target_add_event_listener),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(event_target_remove_event_listener),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(event_target_dispatch_event),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(event_stop_propagation),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(event_stop_immediate_propagation),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(event_prevent_default),
},
]
}
pub(crate) fn register_bindings(scope: &mut v8::PinScope, bindings: v8::Local<v8::Object>) {
let name = v8::String::new(scope, "eventTargetAddEventListener").unwrap();
let value = v8::Function::new(scope, event_target_add_event_listener).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "eventTargetRemoveEventListener").unwrap();
let value = v8::Function::new(scope, event_target_remove_event_listener).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "eventTargetDispatchEvent").unwrap();
let value = v8::Function::new(scope, event_target_dispatch_event).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "eventStopPropagation").unwrap();
let value = v8::Function::new(scope, event_stop_propagation).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "eventStopImmediatePropagation").unwrap();
let value = v8::Function::new(scope, event_stop_immediate_propagation).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "eventPreventDefault").unwrap();
let value = v8::Function::new(scope, event_prevent_default).unwrap();
bindings.set(scope, name.into(), value.into());
}
#[inline]
fn event_target_add_event_listener(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
if args.length() < 3 {
return;
}
let target = args.get(0);
let type_arg = args.get(1);
let listener = args.get(2);
if !listener.is_function() {
return;
}
let target_obj = match v8::Local::<v8::Object>::try_from(target) {
Ok(obj) => obj,
Err(_) => return,
};
let listener_func = v8::Local::<v8::Function>::try_from(listener).unwrap();
let type_key = if type_arg.is_string() {
v8::Local::<v8::String>::try_from(type_arg).unwrap()
} else {
v8::tc_scope!(let tc, scope);
match type_arg.to_string(tc) {
Some(s) => s,
None => return,
}
};
let listeners_key = {
let isolate_state = crate::isolate_state::IsolateState::get(scope);
let string_cache_ref = isolate_state.borrow().string_cache.clone();
let mut cache = string_cache_ref.borrow_mut();
crate::get_or_create_cached_string!(scope, cache, listeners, "__listeners__")
};
let listeners_map = match target_obj.get(scope, listeners_key.into()) {
Some(val) if val.is_map() => v8::Local::<v8::Map>::try_from(val).unwrap(),
_ => {
let new_map = v8::Map::new(scope);
target_obj.set(scope, listeners_key.into(), new_map.into());
new_map
}
};
match listeners_map.get(scope, type_key.into()) {
Some(arr) if arr.is_array() => {
let array = v8::Local::<v8::Array>::try_from(arr).unwrap();
let length = array.length();
array.set_index(scope, length, listener_func.into());
}
_ => {
let array = v8::Array::new(scope, 4);
array.set_index(scope, 0, listener_func.into());
listeners_map.set(scope, type_key.into(), array.into());
}
}
}
#[inline]
fn event_target_remove_event_listener(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
if args.length() < 3 {
return;
}
let target = args.get(0);
let type_arg = args.get(1);
let listener = args.get(2);
if !listener.is_function() {
return;
}
let target_obj = match v8::Local::<v8::Object>::try_from(target) {
Ok(obj) => obj,
Err(_) => return,
};
let type_key = if type_arg.is_string() {
v8::Local::<v8::String>::try_from(type_arg).unwrap()
} else {
v8::tc_scope!(let tc, scope);
match type_arg.to_string(tc) {
Some(s) => s,
None => return,
}
};
let listeners_key = {
let isolate_state = crate::isolate_state::IsolateState::get(scope);
let string_cache_ref = isolate_state.borrow().string_cache.clone();
let mut cache = string_cache_ref.borrow_mut();
crate::get_or_create_cached_string!(scope, cache, listeners, "__listeners__")
};
let listeners_map = match target_obj.get(scope, listeners_key.into()) {
Some(val) if val.is_map() => v8::Local::<v8::Map>::try_from(val).unwrap(),
_ => return,
};
let listeners_array = match listeners_map.get(scope, type_key.into()) {
Some(val) if val.is_array() => v8::Local::<v8::Array>::try_from(val).unwrap(),
_ => return,
};
let listener_func = v8::Local::<v8::Function>::try_from(listener).unwrap();
let length = listeners_array.length();
let new_array = v8::Array::new(scope, 0);
let mut new_index = 0;
let mut removed = false;
for i in 0..length {
if let Some(item) = listeners_array.get_index(scope, i)
&& item.is_function()
{
let item_func = v8::Local::<v8::Function>::try_from(item).unwrap();
if !removed && item_func.strict_equals(listener_func.into()) {
removed = true;
continue;
}
new_array.set_index(scope, new_index, item);
new_index += 1;
}
}
listeners_map.set(scope, type_key.into(), new_array.into());
}
#[inline]
fn event_target_dispatch_event(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
if args.length() < 2 {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
let target = args.get(0);
let event = args.get(1);
let event_obj = match v8::Local::<v8::Object>::try_from(event) {
Ok(obj) => obj,
Err(_) => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
let target_obj = match v8::Local::<v8::Object>::try_from(target) {
Ok(obj) => obj,
Err(_) => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
let isolate_state = crate::isolate_state::IsolateState::get(scope);
let string_cache_ref = isolate_state.borrow().string_cache.clone();
let mut cache = string_cache_ref.borrow_mut();
let current_target_key =
crate::get_or_create_cached_string!(scope, cache, current_target, "__currentTarget__");
let target_key = crate::get_or_create_cached_string!(scope, cache, target, "__target__");
let type_key = crate::get_or_create_cached_string!(scope, cache, type_, "type");
let listeners_key =
crate::get_or_create_cached_string!(scope, cache, listeners, "__listeners__");
let stop_immediate_key = crate::get_or_create_cached_string!(
scope,
cache,
stop_immediate_propagation,
"__stopImmediatePropagation__"
);
let default_prevented_key = crate::get_or_create_cached_string!(
scope,
cache,
default_prevented,
"__defaultPrevented__"
);
drop(cache);
event_obj.set(scope, current_target_key.into(), target);
let existing_target = event_obj.get(scope, target_key.into());
if existing_target.is_none() || existing_target.unwrap().is_null_or_undefined() {
event_obj.set(scope, target_key.into(), target);
}
let type_str_key = v8::String::new_external_onebyte_static(scope, b"__typeStr__").unwrap();
let type_key_lookup = if let Some(cached) = event_obj.get(scope, type_str_key.into()) {
if cached.is_string() {
v8::Local::<v8::String>::try_from(cached).unwrap()
} else {
let type_val = match event_obj.get(scope, type_key.into()) {
Some(val) => val,
None => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
let type_v8_str_opt = {
v8::tc_scope!(let tc, scope);
type_val.to_string(tc)
};
let type_v8_str = match type_v8_str_opt {
Some(s) => s,
None => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
event_obj.set(scope, type_str_key.into(), type_v8_str.into());
type_v8_str
}
} else {
let type_val = match event_obj.get(scope, type_key.into()) {
Some(val) => val,
None => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
let type_v8_str_opt = {
v8::tc_scope!(let tc, scope);
type_val.to_string(tc)
};
let type_v8_str = match type_v8_str_opt {
Some(s) => s,
None => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
event_obj.set(scope, type_str_key.into(), type_v8_str.into());
type_v8_str
};
let listeners_map = match target_obj.get(scope, listeners_key.into()) {
Some(val) if val.is_map() => v8::Local::<v8::Map>::try_from(val).unwrap(),
_ => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
let listeners_array = match listeners_map.get(scope, type_key_lookup.into()) {
Some(val) if val.is_array() => v8::Local::<v8::Array>::try_from(val).unwrap(),
_ => {
rv.set(v8::Boolean::new(scope, true).into());
return;
}
};
let length = listeners_array.length();
let event_arg = [event];
for i in 0..length {
if let Some(item) = listeners_array.get_index(scope, i)
&& item.is_function()
{
let listener_func = v8::Local::<v8::Function>::try_from(item).unwrap();
listener_func.call(scope, target, &event_arg);
if let Some(stop_val) = event_obj.get(scope, stop_immediate_key.into())
&& stop_val.is_true()
{
break;
}
}
}
let default_prevented = event_obj
.get(scope, default_prevented_key.into())
.map(|v| v.is_true())
.unwrap_or(false);
rv.set(v8::Boolean::new(scope, !default_prevented).into());
}
fn event_stop_propagation(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
if args.length() < 1 {
return;
}
let event = args.get(0);
let event_obj = match v8::Local::<v8::Object>::try_from(event) {
Ok(obj) => obj,
Err(_) => return,
};
let isolate_state = crate::isolate_state::IsolateState::get(scope);
let string_cache_ref = isolate_state.borrow().string_cache.clone();
let mut cache = string_cache_ref.borrow_mut();
let key =
crate::get_or_create_cached_string!(scope, cache, stop_propagation, "__stopPropagation__");
let value = v8::Boolean::new(scope, true);
event_obj.set(scope, key.into(), value.into());
}
#[inline]
fn event_stop_immediate_propagation(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
if args.length() < 1 {
return;
}
let event = args.get(0);
let event_obj = match v8::Local::<v8::Object>::try_from(event) {
Ok(obj) => obj,
Err(_) => return,
};
let isolate_state = crate::isolate_state::IsolateState::get(scope);
let string_cache_ref = isolate_state.borrow().string_cache.clone();
let mut cache = string_cache_ref.borrow_mut();
let key = crate::get_or_create_cached_string!(
scope,
cache,
stop_immediate_propagation,
"__stopImmediatePropagation__"
);
let value = v8::Boolean::new(scope, true);
event_obj.set(scope, key.into(), value.into());
let prop_key =
crate::get_or_create_cached_string!(scope, cache, stop_propagation, "__stopPropagation__");
event_obj.set(scope, prop_key.into(), value.into());
}
fn event_prevent_default(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
if args.length() < 1 {
return;
}
let event = args.get(0);
let event_obj = match v8::Local::<v8::Object>::try_from(event) {
Ok(obj) => obj,
Err(_) => return,
};
let isolate_state = crate::isolate_state::IsolateState::get(scope);
let string_cache_ref = isolate_state.borrow().string_cache.clone();
let mut cache = string_cache_ref.borrow_mut();
let cancelable_key =
crate::get_or_create_cached_string!(scope, cache, cancelable, "cancelable");
let is_cancelable = event_obj
.get(scope, cancelable_key.into())
.map(|v| v.is_true())
.unwrap_or(false);
if is_cancelable {
let key = crate::get_or_create_cached_string!(
scope,
cache,
default_prevented,
"__defaultPrevented__"
);
let value = v8::Boolean::new(scope, true);
event_obj.set(scope, key.into(), value.into());
}
}