From 8e9751d3b6cd555119ca195e6751c2675beb3b7d Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 5 Feb 2017 11:27:45 +0000 Subject: Add EventTarget binding This adds the binding for EventTarget along with implementations for addEventListener() removeEventListener() and dispatchEvent() --- .../handlers/javascript/duktape/EventTarget.bnd | 277 +++++++++++++++++++++ content/handlers/javascript/duktape/Node.bnd | 1 + content/handlers/javascript/duktape/netsurf.bnd | 1 + 3 files changed, 279 insertions(+) create mode 100644 content/handlers/javascript/duktape/EventTarget.bnd diff --git a/content/handlers/javascript/duktape/EventTarget.bnd b/content/handlers/javascript/duktape/EventTarget.bnd new file mode 100644 index 000000000..92e2ac83e --- /dev/null +++ b/content/handlers/javascript/duktape/EventTarget.bnd @@ -0,0 +1,277 @@ +/* Event Target binding for browser using duktape and libdom + * + * Copyright 2016 Daniel Silverstone + * + * 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; +%} diff --git a/content/handlers/javascript/duktape/Node.bnd b/content/handlers/javascript/duktape/Node.bnd index f237c876a..f14cfc177 100644 --- a/content/handlers/javascript/duktape/Node.bnd +++ b/content/handlers/javascript/duktape/Node.bnd @@ -16,6 +16,7 @@ init Node(struct dom_node *node) %{ priv->node = node; dom_node_ref(node); + priv->parent.is_node = true; %} fini Node() diff --git a/content/handlers/javascript/duktape/netsurf.bnd b/content/handlers/javascript/duktape/netsurf.bnd index 4aca4752f..2a56cccb5 100644 --- a/content/handlers/javascript/duktape/netsurf.bnd +++ b/content/handlers/javascript/duktape/netsurf.bnd @@ -54,6 +54,7 @@ struct dom_html_br_element; }; +#include "EventTarget.bnd" #include "Console.bnd" #include "Window.bnd" #include "Document.bnd" -- cgit v1.2.3