mirror of
https://github.com/netsurf-browser/netsurf
synced 2025-02-28 12:24:18 +03:00
Add EventTarget binding
This adds the binding for EventTarget along with implementations for addEventListener() removeEventListener() and dispatchEvent()
This commit is contained in:
parent
2858aec1c2
commit
8e9751d3b6
277
content/handlers/javascript/duktape/EventTarget.bnd
Normal file
277
content/handlers/javascript/duktape/EventTarget.bnd
Normal file
@ -0,0 +1,277 @@
|
||||
/* 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) {
|
||||
LOG("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;
|
||||
%}
|
@ -16,6 +16,7 @@ init Node(struct dom_node *node)
|
||||
%{
|
||||
priv->node = node;
|
||||
dom_node_ref(node);
|
||||
priv->parent.is_node = true;
|
||||
%}
|
||||
|
||||
fini Node()
|
||||
|
@ -54,6 +54,7 @@ struct dom_html_br_element;
|
||||
|
||||
};
|
||||
|
||||
#include "EventTarget.bnd"
|
||||
#include "Console.bnd"
|
||||
#include "Window.bnd"
|
||||
#include "Document.bnd"
|
||||
|
Loading…
x
Reference in New Issue
Block a user