netsurf/content/handlers/javascript/duktape/EventTarget.bnd
2017-09-06 18:45:35 +01:00

279 lines
7.5 KiB
Plaintext

/* 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;
%}