You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
7.5 KiB
279 lines
7.5 KiB
/* Event Target binding for browser using duktape and libdom
|
|
*
|
|
* Copyright 2016 Daniel Silverstone <dsilvers@digital-scurf.org>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* Released under the terms of the MIT License,
|
|
* http://www.opensource.org/licenses/mit-license
|
|
*/
|
|
|
|
class EventTarget {
|
|
private bool is_node;
|
|
private bool capture_registered;
|
|
private bool bubbling_registered;
|
|
};
|
|
|
|
prologue EventTarget()
|
|
%{
|
|
|
|
static event_listener_flags event_listener_pop_options(duk_context *ctx)
|
|
{
|
|
event_listener_flags ret = ELF_NONE;
|
|
/* ... options */
|
|
duk_get_prop_string(ctx, -1, "capture");
|
|
if (duk_to_boolean(ctx, -1))
|
|
ret |= ELF_CAPTURE;
|
|
duk_pop(ctx);
|
|
duk_get_prop_string(ctx, -1, "passive");
|
|
if (duk_to_boolean(ctx, -1))
|
|
ret |= ELF_PASSIVE;
|
|
duk_pop(ctx);
|
|
duk_get_prop_string(ctx, -1, "once");
|
|
if (duk_to_boolean(ctx, -1))
|
|
ret |= ELF_ONCE;
|
|
duk_pop_2(ctx);
|
|
/* ... */
|
|
return ret;
|
|
}
|
|
|
|
static void event_target_register_listener(duk_context *ctx,
|
|
event_listener_flags flags)
|
|
{
|
|
/* ... listeners callback */
|
|
/* If the given callback with the given flags is already present,
|
|
* we do not re-add it, otherwise we need to add to listeners
|
|
* a tuple of the callback and flags
|
|
*/
|
|
duk_uarridx_t idx = 0;
|
|
while (duk_get_prop_index(ctx, -1, idx)) {
|
|
/* ... listeners callback candidate */
|
|
duk_get_prop_index(ctx, -1, 0);
|
|
duk_get_prop_index(ctx, -2, 1);
|
|
/* ... listeners callback candidate candidatecallback candidateflags */
|
|
if (duk_strict_equals(ctx, -1, -3) &&
|
|
duk_get_int(ctx, -1) == (duk_int_t)flags) {
|
|
/* already present, nothing to do */
|
|
duk_pop_n(ctx, 5);
|
|
/* ... */
|
|
return;
|
|
}
|
|
/* ... listeners callback candidate candidatecallback candidateflags */
|
|
duk_pop_3(ctx);
|
|
/* ... listeners callback */
|
|
idx++;
|
|
}
|
|
/* ... listeners callback undefined */
|
|
duk_pop(ctx);
|
|
/* ... listeners callback */
|
|
duk_push_array(ctx);
|
|
/* ... listeners callback newcandidate */
|
|
duk_insert(ctx, -2);
|
|
/* ... listeners newcandidate callback */
|
|
duk_put_prop_index(ctx, -2, 0);
|
|
/* ... listeners newcandidate */
|
|
duk_push_int(ctx, (duk_int_t)flags);
|
|
/* ... listeners newcandidate flags */
|
|
duk_put_prop_index(ctx, -2, 1);
|
|
/* ... listeners newcandidate */
|
|
duk_put_prop_index(ctx, -2, idx);
|
|
/* ... listeners */
|
|
duk_pop(ctx);
|
|
/* ... */
|
|
}
|
|
|
|
static void event_target_unregister_listener(duk_context *ctx,
|
|
event_listener_flags flags)
|
|
{
|
|
/* ... listeners callback */
|
|
/* If the given callback with the given flags is present,
|
|
* we remove it and shuffle the rest up.
|
|
*/
|
|
duk_uarridx_t idx = 0;
|
|
while (duk_get_prop_index(ctx, -1, idx)) {
|
|
/* ... listeners callback candidate */
|
|
duk_get_prop_index(ctx, -1, 0);
|
|
duk_get_prop_index(ctx, -2, 1);
|
|
/* ... listeners callback candidate candidatecallback candidateflags */
|
|
if (duk_strict_equals(ctx, -1, -3) &&
|
|
duk_get_int(ctx, -1) == (duk_int_t)flags) {
|
|
/* present */
|
|
duk_pop(ctx);
|
|
/* ... listeners callback candidate candidatecallback */
|
|
duk_put_prop_index(ctx, -2, 2);
|
|
/* ... listeners callback candidate */
|
|
duk_pop(ctx);
|
|
/* ... listeners callback */
|
|
duk_push_int(ctx, idx);
|
|
/* ... listeners callback found_at */
|
|
break;
|
|
}
|
|
/* ... listeners callback candidate candidatecallback candidateflags */
|
|
duk_pop_3(ctx);
|
|
/* ... listeners callback */
|
|
idx++;
|
|
}
|
|
/* ... listeners callback undefined/found_at */
|
|
if (duk_is_undefined(ctx, -1)) {
|
|
/* not found, clean up and come out */
|
|
duk_pop_3(ctx);
|
|
return;
|
|
}
|
|
idx = duk_to_int(ctx, -1);
|
|
duk_pop_2(ctx);
|
|
/* ... listeners */
|
|
dukky_shuffle_array(ctx, idx);
|
|
/* ... listeners */
|
|
duk_pop(ctx);
|
|
/* ... */
|
|
}
|
|
|
|
|
|
%}
|
|
|
|
init EventTarget()
|
|
%{
|
|
priv->is_node = false;
|
|
priv->capture_registered = false;
|
|
priv->bubbling_registered = false;
|
|
%}
|
|
|
|
method EventTarget::addEventListener()
|
|
%{
|
|
dom_exception exc;
|
|
event_listener_flags flags = ELF_NONE;
|
|
/* Incoming stack is: type callback [options] */
|
|
if (duk_get_top(ctx) < 2) return 0; /* Bad arguments */
|
|
if (duk_get_top(ctx) > 3) return 0; /* Bad arguments */
|
|
if (duk_get_top(ctx) == 2) {
|
|
duk_push_object(ctx);
|
|
/* type callback options */
|
|
}
|
|
if (duk_get_type(ctx, -1) != DUK_TYPE_OBJECT) {
|
|
/* legacy support, if not object, it's the capture value */
|
|
duk_push_object(ctx);
|
|
/* ... capture options */
|
|
duk_insert(ctx, -2);
|
|
/* ... options capture */
|
|
duk_put_prop_string(ctx, -2, "capture");
|
|
/* ... options */
|
|
}
|
|
/* type callback options */
|
|
flags = event_listener_pop_options(ctx);
|
|
/* type callback */
|
|
duk_dup(ctx, -2);
|
|
/* type callback type */
|
|
duk_push_this(ctx);
|
|
/* type callback type this(=EventTarget) */
|
|
if (dukky_event_target_push_listeners(ctx, false) && priv->is_node) {
|
|
/* Take a moment to register a JS callback */
|
|
duk_size_t ev_ty_l;
|
|
const char *ev_ty = duk_to_lstring(ctx, -3, &ev_ty_l);
|
|
dom_string *ev_ty_s;
|
|
exc = dom_string_create((const uint8_t*)ev_ty, ev_ty_l,
|
|
&ev_ty_s);
|
|
if (exc != DOM_NO_ERR) {
|
|
NSLOG(netsurf, INFO,
|
|
"Oh dear, failed to create dom_string in addEventListener()");
|
|
return 0;
|
|
}
|
|
dukky_register_event_listener_for(
|
|
ctx, (dom_element *)((node_private_t *)priv)->node,
|
|
ev_ty_s,
|
|
!!(flags & ELF_CAPTURE));
|
|
dom_string_unref(ev_ty_s);
|
|
}
|
|
/* type callback typelisteners */
|
|
duk_insert(ctx, -2);
|
|
/* type typelisteners callback */
|
|
event_target_register_listener(ctx, flags);
|
|
/* type */
|
|
return 0;
|
|
%}
|
|
|
|
method EventTarget::removeEventListener()
|
|
%{
|
|
event_listener_flags flags = ELF_NONE;
|
|
/* Incoming stack is: type callback [options] */
|
|
if (duk_get_top(ctx) < 2) return 0; /* Bad arguments */
|
|
if (duk_get_top(ctx) > 3) return 0; /* Bad arguments */
|
|
if (duk_get_top(ctx) == 2) {
|
|
duk_push_object(ctx);
|
|
/* type callback options */
|
|
}
|
|
if (duk_get_type(ctx, -1) != DUK_TYPE_OBJECT) {
|
|
/* legacy support, if not object, it's the capture value */
|
|
duk_push_object(ctx);
|
|
/* ... capture options */
|
|
duk_insert(ctx, -2);
|
|
/* ... options capture */
|
|
duk_put_prop_string(ctx, -2, "capture");
|
|
/* ... options */
|
|
}
|
|
/* type callback options */
|
|
flags = event_listener_pop_options(ctx);
|
|
/* type callback */
|
|
duk_dup(ctx, -2);
|
|
/* type callback type */
|
|
duk_push_this(ctx);
|
|
/* type callback type this(=EventTarget) */
|
|
if (dukky_event_target_push_listeners(ctx, true)) {
|
|
/* nothing to do because the listener wasn't there at all */
|
|
duk_pop_3(ctx);
|
|
return 0;
|
|
}
|
|
/* type callback typelisteners */
|
|
duk_insert(ctx, -2);
|
|
/* type typelisteners callback */
|
|
event_target_unregister_listener(ctx, flags);
|
|
/* type */
|
|
return 0;
|
|
%}
|
|
|
|
|
|
|
|
method EventTarget::dispatchEvent()
|
|
%{
|
|
dom_exception exc;
|
|
if (!dukky_instanceof(ctx, 0, PROTO_NAME(EVENT))) return 0;
|
|
|
|
duk_get_prop_string(ctx, 0, PRIVATE_MAGIC);
|
|
event_private_t *evpriv = duk_get_pointer(ctx, -1);
|
|
duk_pop(ctx);
|
|
|
|
dom_event *evt = evpriv->evt;
|
|
|
|
/* Dispatch event logic, see:
|
|
* https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
|
|
*/
|
|
bool in_dispatch;
|
|
if (dom_event_in_dispatch(evt, &in_dispatch) != DOM_NO_ERR) return 0;
|
|
if (in_dispatch) {
|
|
/** \todo Raise InvalidStateException */
|
|
return 0;
|
|
}
|
|
|
|
bool is_initialised;
|
|
if (dom_event_is_initialised(evt, &is_initialised) != DOM_NO_ERR) return 0;
|
|
if (is_initialised == false) {
|
|
/** \todo Raise InvalidStateException */
|
|
return 0;
|
|
}
|
|
|
|
if (dom_event_set_is_trusted(evt, false) != DOM_NO_ERR) return 0;
|
|
|
|
/** \todo work out how to dispatch against non-node things */
|
|
if (priv->is_node == false) return 0;
|
|
|
|
bool success;
|
|
/* Event prepared, dispatch against ourselves */
|
|
exc = dom_event_target_dispatch_event(
|
|
((node_private_t *)priv)->node,
|
|
evt,
|
|
&success);
|
|
if (exc != DOM_NO_ERR) return 0; /**< \todo raise correct exception */
|
|
|
|
duk_push_boolean(ctx, success);
|
|
return 1;
|
|
%}
|