summaryrefslogtreecommitdiff
path: root/content/handlers/javascript/duktape/dukky.c
diff options
context:
space:
mode:
Diffstat (limited to 'content/handlers/javascript/duktape/dukky.c')
-rw-r--r--content/handlers/javascript/duktape/dukky.c606
1 files changed, 481 insertions, 125 deletions
diff --git a/content/handlers/javascript/duktape/dukky.c b/content/handlers/javascript/duktape/dukky.c
index 8cfeb3985..a780b0067 100644
--- a/content/handlers/javascript/duktape/dukky.c
+++ b/content/handlers/javascript/duktape/dukky.c
@@ -19,7 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
+/**
+ * \file
* Duktapeish implementation of javascript engine functions.
*/
@@ -37,6 +38,8 @@
#include "javascript/content.h"
#include "duktape/binding.h"
+#include "duktape/generics.js.inc"
+#include "duktape/polyfill.js.inc"
#include "duktape.h"
#include "dukky.h"
@@ -47,6 +50,30 @@
#define HANDLER_LISTENER_MAGIC MAGIC(HANDLER_LISTENER_MAP)
#define HANDLER_MAGIC MAGIC(HANDLER_MAP)
#define EVENT_LISTENER_JS_MAGIC MAGIC(EVENT_LISTENER_JS_MAP)
+#define GENERICS_MAGIC MAGIC(GENERICS_TABLE)
+#define THREAD_MAP MAGIC(THREAD_MAP)
+
+/**
+ * dukky javascript heap
+ */
+struct jsheap {
+ duk_context *ctx; /**< duktape base context */
+ duk_uarridx_t next_thread; /**< monotonic thread counter */
+ bool pending_destroy; /**< Whether this heap is pending destruction */
+ unsigned int live_threads; /**< number of live threads */
+ uint64_t exec_start_time;
+};
+
+/**
+ * dukky javascript thread
+ */
+struct jsthread {
+ bool pending_destroy; /**< Whether this thread is pending destruction */
+ unsigned int in_use; /**< The number of times this thread is in use */
+ jsheap *heap; /**< The heap this thread belongs to */
+ duk_context *ctx; /**< The duktape thread context */
+ duk_uarridx_t thread_idx; /**< The thread number */
+};
static duk_ret_t dukky_populate_object(duk_context *ctx, void *udata)
{
@@ -56,17 +83,20 @@ static duk_ret_t dukky_populate_object(duk_context *ctx, void *udata)
/* ... obj args protoname */
duk_get_global_string(ctx, PROTO_MAGIC);
/* .. obj args protoname prototab */
- duk_insert(ctx, -2);
- /* ... obj args prototab protoname */
+ duk_dup(ctx, -2);
+ /* ... obj args protoname prototab protoname */
duk_get_prop(ctx, -2);
- /* ... obj args prototab {proto/undefined} */
+ /* ... obj args protoname prototab {proto/undefined} */
if (duk_is_undefined(ctx, -1)) {
- NSLOG(netsurf, INFO,
- "RuhRoh, couldn't find a prototype, HTMLUnknownElement it is");
+ NSLOG(dukky, WARNING,
+ "Unable to find dukky prototype for `%s` - falling back to HTMLUnknownElement",
+ duk_get_string(ctx, -3) + 2 /* Skip the two unprintables */);
duk_pop(ctx);
duk_push_string(ctx, PROTO_NAME(HTMLUNKNOWNELEMENT));
duk_get_prop(ctx, -2);
}
+ /* ... obj args protoname prototab proto */
+ duk_remove(ctx, -3);
/* ... obj args prototab proto */
duk_dup(ctx, -1);
/* ... obj args prototab proto proto */
@@ -78,7 +108,7 @@ static duk_ret_t dukky_populate_object(duk_context *ctx, void *udata)
/* ... initfn obj[proto] args prototab proto */
duk_pop_2(ctx);
/* ... initfn obj[proto] args */
- NSLOG(netsurf, INFO, "Call the init function");
+ NSLOG(dukky, DEEPDEBUG, "Call the init function");
duk_call(ctx, nargs + 1);
return 1; /* The object */
}
@@ -86,7 +116,7 @@ static duk_ret_t dukky_populate_object(duk_context *ctx, void *udata)
duk_ret_t dukky_create_object(duk_context *ctx, const char *name, int args)
{
duk_ret_t ret;
- NSLOG(netsurf, INFO, "name=%s nargs=%d", name + 2, args);
+ NSLOG(dukky, DEEPDEBUG, "name=%s nargs=%d", name + 2, args);
/* ... args */
duk_push_object(ctx);
/* ... args obj */
@@ -107,7 +137,7 @@ duk_ret_t dukky_create_object(duk_context *ctx, const char *name, int args)
if ((ret = duk_safe_call(ctx, dukky_populate_object, NULL, args + 3, 1))
!= DUK_EXEC_SUCCESS)
return ret;
- NSLOG(netsurf, INFO, "created");
+ NSLOG(dukky, DEEPDEBUG, "created");
return DUK_EXEC_SUCCESS;
}
@@ -147,7 +177,7 @@ dukky_push_node_stacked(duk_context *ctx)
if (duk_safe_call(ctx, dukky_populate_object, NULL, 4, 1)
!= DUK_EXEC_SUCCESS) {
duk_set_top(ctx, top_at_fail);
- NSLOG(netsurf, INFO, "Boo and also hiss");
+ NSLOG(dukky, ERROR, "Failed to populate object prototype");
return false;
}
/* ... nodeptr klass nodes node */
@@ -163,6 +193,12 @@ dukky_push_node_stacked(duk_context *ctx)
/* ... node nodeptr klass nodes */
duk_pop_3(ctx);
/* ... node */
+ if (NSLOG_COMPILED_MIN_LEVEL <= NSLOG_LEVEL_DEEPDEBUG) {
+ duk_dup(ctx, -1);
+ const char * what = duk_safe_to_string(ctx, -1);
+ NSLOG(dukky, DEEPDEBUG, "Created: %s", what);
+ duk_pop(ctx);
+ }
return true;
}
@@ -205,7 +241,7 @@ static void dukky_html_element_class_from_tag_type(dom_html_element_type type,
SET_HTML_CLASS(LINK)
break;
case DOM_HTML_ELEMENT_TYPE_BUTTON:
- SET_HTML_CLASS(BUTTOM)
+ SET_HTML_CLASS(BUTTON)
break;
case DOM_HTML_ELEMENT_TYPE_INPUT:
SET_HTML_CLASS(INPUT)
@@ -344,9 +380,12 @@ static void dukky_html_element_class_from_tag_type(dom_html_element_type type,
case DOM_HTML_ELEMENT_TYPE_ISINDEX:
SET_HTML_CLASS(ISINDEX)
break;
+ case DOM_HTML_ELEMENT_TYPE_CANVAS:
+ SET_HTML_CLASS(CANVAS)
+ break;
case DOM_HTML_ELEMENT_TYPE__COUNT:
assert(type != DOM_HTML_ELEMENT_TYPE__COUNT);
- /* fallthrough */
+ fallthrough;
case DOM_HTML_ELEMENT_TYPE__UNKNOWN:
SET_HTML_CLASS(UNKNOWN)
break;
@@ -385,14 +424,14 @@ dukky_push_node_klass(duk_context *ctx, struct dom_node *node)
err = dom_node_get_namespace(node, &namespace);
if (err != DOM_NO_ERR) {
/* Feck it, element */
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, ERROR,
"dom_node_get_namespace() failed");
duk_push_string(ctx, PROTO_NAME(ELEMENT));
break;
}
if (namespace == NULL) {
/* No namespace, -> element */
- NSLOG(netsurf, INFO, "no namespace");
+ NSLOG(dukky, DEBUG, "no namespace");
duk_push_string(ctx, PROTO_NAME(ELEMENT));
break;
}
@@ -442,7 +481,7 @@ dukky_push_node_klass(duk_context *ctx, struct dom_node *node)
duk_bool_t
dukky_push_node(duk_context *ctx, struct dom_node *node)
{
- JS_LOG("Pushing node %p", node);
+ NSLOG(dukky, DEEPDEBUG, "Pushing node %p", node);
/* First check if we can find the node */
/* ... */
duk_get_global_string(ctx, NODE_MAGIC);
@@ -457,7 +496,12 @@ dukky_push_node(duk_context *ctx, struct dom_node *node)
/* ... node nodes */
duk_pop(ctx);
/* ... node */
- JS_LOG("Found it memoised");
+ if (NSLOG_COMPILED_MIN_LEVEL <= NSLOG_LEVEL_DEEPDEBUG) {
+ duk_dup(ctx, -1);
+ const char * what = duk_safe_to_string(ctx, -1);
+ NSLOG(dukky, DEEPDEBUG, "Found it memoised: %s", what);
+ duk_pop(ctx);
+ }
return true;
}
/* ... nodes undefined */
@@ -522,22 +566,14 @@ static void *dukky_realloc_function(void *udata, void *ptr, duk_size_t size)
return realloc(ptr, size);
}
+
static void dukky_free_function(void *udata, void *ptr)
{
if (ptr != NULL)
free(ptr);
}
-
-/**************************************** js.h ******************************/
-struct jscontext {
- duk_context *ctx;
- duk_context *thread;
- uint64_t exec_start_time;
-};
-
-#define CTX (ctx->thread)
-
+/* exported interface documented in js.h */
void js_initialise(void)
{
/** TODO: Forces JS on for our testing, needs changing before a release
@@ -549,21 +585,22 @@ void js_initialise(void)
javascript_init();
}
+
+/* exported interface documented in js.h */
void js_finalise(void)
{
/* NADA for now */
}
-#define DUKKY_NEW_PROTOTYPE(klass, uklass, klass_name) \
- dukky_create_prototype(ctx, dukky_##klass##___proto, PROTO_NAME(uklass), klass_name)
-nserror js_newcontext(int timeout, jscallback *cb, void *cbctx,
- jscontext **jsctx)
+/* exported interface documented in js.h */
+nserror
+js_newheap(int timeout, jsheap **heap)
{
duk_context *ctx;
- jscontext *ret = calloc(1, sizeof(*ret));
- *jsctx = NULL;
- NSLOG(netsurf, INFO, "Creating new duktape javascript context");
+ jsheap *ret = calloc(1, sizeof(*ret));
+ *heap = NULL;
+ NSLOG(dukky, DEBUG, "Creating new duktape javascript heap");
if (ret == NULL) return NSERROR_NOMEM;
ctx = ret->ctx = duk_create_heap(
dukky_alloc_function,
@@ -579,28 +616,62 @@ nserror js_newcontext(int timeout, jscallback *cb, void *cbctx,
duk_put_global_string(ctx, PROTO_MAGIC);
/* Create prototypes here */
dukky_create_prototypes(ctx);
+ /* Now create the thread map */
+ duk_push_object(ctx);
+ duk_put_global_string(ctx, THREAD_MAP);
- *jsctx = ret;
+ *heap = ret;
return NSERROR_OK;
}
-void js_destroycontext(jscontext *ctx)
+
+static void dukky_destroyheap(jsheap *heap)
+{
+ assert(heap->pending_destroy == true);
+ assert(heap->live_threads == 0);
+ NSLOG(dukky, DEBUG, "Destroying duktape javascript context");
+ duk_destroy_heap(heap->ctx);
+ free(heap);
+}
+
+/* exported interface documented in js.h */
+void js_destroyheap(jsheap *heap)
{
- NSLOG(netsurf, INFO, "Destroying duktape javascript context");
- duk_destroy_heap(ctx->ctx);
- free(ctx);
+ heap->pending_destroy = true;
+ if (heap->live_threads == 0) {
+ dukky_destroyheap(heap);
+ }
}
-jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv)
+/* Just for here, the CTX is in ret, not thread */
+#define CTX (ret->ctx)
+
+/* exported interface documented in js.h */
+nserror js_newthread(jsheap *heap, void *win_priv, void *doc_priv, jsthread **thread)
{
- assert(ctx != NULL);
- /* Pop any active thread off */
- NSLOG(netsurf, INFO,
- "Yay, new compartment, win_priv=%p, doc_priv=%p", win_priv,
- doc_priv);
- duk_set_top(ctx->ctx, 0);
- duk_push_thread(ctx->ctx);
- ctx->thread = duk_require_context(ctx->ctx, -1);
+ jsthread *ret;
+ assert(heap != NULL);
+ assert(heap->pending_destroy == false);
+
+ ret = calloc(1, sizeof (*ret));
+ if (ret == NULL) {
+ NSLOG(dukky, ERROR, "Unable to allocate new JS thread structure");
+ return NSERROR_NOMEM;
+ }
+
+ NSLOG(dukky, DEBUG,
+ "New javascript/duktape thread, win_priv=%p, doc_priv=%p",
+ win_priv, doc_priv);
+
+ /* create new thread */
+ duk_get_global_string(heap->ctx, THREAD_MAP); /* ... threads */
+ duk_push_thread(heap->ctx); /* ... threads thread */
+ ret->heap = heap;
+ ret->ctx = duk_require_context(heap->ctx, -1);
+ ret->thread_idx = heap->next_thread++;
+ duk_put_prop_index(heap->ctx, -2, ret->thread_idx);
+ heap->live_threads++;
+ duk_pop(heap->ctx); /* ... */
duk_push_int(CTX, 0);
duk_push_int(CTX, 1);
duk_push_int(CTX, 2);
@@ -624,19 +695,152 @@ jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv)
duk_push_object(CTX);
duk_put_global_string(CTX, EVENT_MAGIC);
- return (jsobject *)ctx;
+ /* Now load the polyfills */
+ /* ... */
+ duk_push_string(CTX, "polyfill.js");
+ /* ..., polyfill.js */
+ if (duk_pcompile_lstring_filename(CTX, DUK_COMPILE_EVAL,
+ (const char *)polyfill_js, polyfill_js_len) != 0) {
+ NSLOG(dukky, CRITICAL, "%s", duk_safe_to_string(CTX, -1));
+ NSLOG(dukky, CRITICAL, "Unable to compile polyfill.js, thread aborted");
+ js_destroythread(ret);
+ return NSERROR_INIT_FAILED;
+ }
+ /* ..., (generics.js) */
+ if (dukky_pcall(CTX, 0, true) != 0) {
+ NSLOG(dukky, CRITICAL, "Unable to run polyfill.js, thread aborted");
+ js_destroythread(ret);
+ return NSERROR_INIT_FAILED;
+ }
+ /* ..., result */
+ duk_pop(CTX);
+ /* ... */
+
+ /* Now load the NetSurf table in */
+ /* ... */
+ duk_push_string(CTX, "generics.js");
+ /* ..., generics.js */
+ if (duk_pcompile_lstring_filename(CTX, DUK_COMPILE_EVAL,
+ (const char *)generics_js, generics_js_len) != 0) {
+ NSLOG(dukky, CRITICAL, "%s", duk_safe_to_string(CTX, -1));
+ NSLOG(dukky, CRITICAL, "Unable to compile generics.js, thread aborted");
+ js_destroythread(ret);
+ return NSERROR_INIT_FAILED;
+ }
+ /* ..., (generics.js) */
+ if (dukky_pcall(CTX, 0, true) != 0) {
+ NSLOG(dukky, CRITICAL, "Unable to run generics.js, thread aborted");
+ js_destroythread(ret);
+ return NSERROR_INIT_FAILED;
+ }
+ /* ..., result */
+ duk_pop(CTX);
+ /* ... */
+ duk_push_global_object(CTX);
+ /* ..., Win */
+ duk_get_prop_string(CTX, -1, "NetSurf");
+ /* ..., Win, NetSurf */
+ duk_put_global_string(CTX, GENERICS_MAGIC);
+ /* ..., Win */
+ duk_del_prop_string(CTX, -1, "NetSurf");
+ duk_pop(CTX);
+ /* ... */
+
+ dukky_log_stack_frame(CTX, "New thread created");
+ NSLOG(dukky, DEBUG, "New thread is %p in heap %p", thread, heap);
+ *thread = ret;
+
+ return NSERROR_OK;
}
-static duk_ret_t eval_top_string(duk_context *ctx, void *udata)
+/* Now switch to the long term CTX behaviour */
+#undef CTX
+#define CTX (thread->ctx)
+
+/* exported interface documented in js.h */
+nserror js_closethread(jsthread *thread)
{
- duk_eval(ctx);
- return 0;
+ /* We can always close down a thread, it might just confuse
+ * the code running, though we don't mind since we're in the
+ * process of destruction at this point
+ */
+ duk_int_t top = duk_get_top(CTX);
+
+ /* Closing down the extant thread */
+ NSLOG(dukky, DEBUG, "Closing down extant thread %p in heap %p", thread, thread->heap);
+ duk_get_global_string(CTX, MAGIC(closedownThread));
+ dukky_pcall(CTX, 0, true);
+
+ /* Restore whatever stack we had */
+ duk_set_top(CTX, top);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Destroy a Duktape thread
+ */
+static void dukky_destroythread(jsthread *thread)
+{
+ jsheap *heap = thread->heap;
+
+ assert(thread->in_use == 0);
+ assert(thread->pending_destroy == true);
+
+ /* Closing down the extant thread */
+ NSLOG(dukky, DEBUG, "Closing down extant thread %p in heap %p", thread, heap);
+ duk_get_global_string(CTX, MAGIC(closedownThread));
+ dukky_pcall(CTX, 0, true);
+
+ /* Now delete the thread from the heap */
+ duk_get_global_string(heap->ctx, THREAD_MAP); /* ... threads */
+ duk_del_prop_index(heap->ctx, -1, thread->thread_idx);
+ duk_pop(heap->ctx); /* ... */
+
+ /* We can now free the thread object */
+ free(thread);
+
+ /* Finally give the heap a chance to clean up */
+ duk_gc(heap->ctx, 0);
+ duk_gc(heap->ctx, DUK_GC_COMPACT);
+ heap->live_threads--;
+
+ /* And if the heap should now go, blow it away */
+ if (heap->pending_destroy == true && heap->live_threads == 0) {
+ dukky_destroyheap(heap);
+ }
+}
+
+/* exported interface documented in js.h */
+void js_destroythread(jsthread *thread)
+{
+ thread->pending_destroy = true;
+ if (thread->in_use == 0) {
+ dukky_destroythread(thread);
+ }
+}
+
+static void dukky_enter_thread(jsthread *thread)
+{
+ assert(thread != NULL);
+ thread->in_use++;
+}
+
+static void dukky_leave_thread(jsthread *thread)
+{
+ assert(thread != NULL);
+ assert(thread->in_use > 0);
+
+ thread->in_use--;
+ if (thread->in_use == 0 && thread->pending_destroy == true) {
+ dukky_destroythread(thread);
+ }
}
duk_bool_t dukky_check_timeout(void *udata)
{
#define JS_EXEC_TIMEOUT_MS 10000 /* 10 seconds */
- jscontext *ctx = (jscontext *) udata;
+ jsheap *heap = (jsheap *) udata;
uint64_t now;
(void) nsu_getmonotonic_ms(&now);
@@ -645,41 +849,161 @@ duk_bool_t dukky_check_timeout(void *udata)
* so only test for execution timeout if we've recorded a
* start time.
*/
- return ctx->exec_start_time != 0 &&
- now > (ctx->exec_start_time + JS_EXEC_TIMEOUT_MS);
+ return heap->exec_start_time != 0 &&
+ now > (heap->exec_start_time + JS_EXEC_TIMEOUT_MS);
}
-bool js_exec(jscontext *ctx, const char *txt, size_t txtlen)
+static void dukky_dump_error(duk_context *ctx)
{
- assert(ctx);
- if (txt == NULL || txtlen == 0) return false;
- duk_set_top(CTX, 0);
- duk_push_lstring(CTX, txt, txtlen);
-
- (void) nsu_getmonotonic_ms(&ctx->exec_start_time);
- if (duk_safe_call(CTX, eval_top_string, NULL, 1, 1) == DUK_EXEC_ERROR) {
- duk_get_prop_string(CTX, 0, "name");
- duk_get_prop_string(CTX, 0, "message");
- duk_get_prop_string(CTX, 0, "fileName");
- duk_get_prop_string(CTX, 0, "lineNumber");
- duk_get_prop_string(CTX, 0, "stack");
- NSLOG(netsurf, INFO, "Uncaught error in JS: %s: %s",
- duk_safe_to_string(CTX, 1), duk_safe_to_string(CTX, 2));
- NSLOG(netsurf, INFO, " was at: %s line %s",
- duk_safe_to_string(CTX, 3), duk_safe_to_string(CTX, 4));
- NSLOG(netsurf, INFO, " Stack trace: %s",
- duk_safe_to_string(CTX, 5));
+ /* stack is ..., errobj */
+ duk_dup_top(ctx);
+ /* ..., errobj, errobj */
+ NSLOG(jserrors, WARNING, "Uncaught error in JS: %s", duk_safe_to_stacktrace(ctx, -1));
+ /* ..., errobj, errobj.stackstring */
+ duk_pop(ctx);
+ /* ..., errobj */
+}
+
+static void dukky_reset_start_time(duk_context *ctx)
+{
+ duk_memory_functions funcs;
+ jsheap *heap;
+ duk_get_memory_functions(ctx, &funcs);
+ heap = funcs.udata;
+ (void) nsu_getmonotonic_ms(&heap->exec_start_time);
+}
+
+duk_int_t dukky_pcall(duk_context *ctx, duk_size_t argc, bool reset_timeout)
+{
+ if (reset_timeout) {
+ dukky_reset_start_time(ctx);
+ }
+
+ duk_int_t ret = duk_pcall(ctx, argc);
+ if (ret) {
+ /* Something went wrong calling this... */
+ dukky_dump_error(ctx);
+ }
+
+ return ret;
+}
+
+
+void dukky_push_generics(duk_context *ctx, const char *generic)
+{
+ /* ... */
+ duk_get_global_string(ctx, GENERICS_MAGIC);
+ /* ..., generics */
+ duk_get_prop_string(ctx, -1, generic);
+ /* ..., generics, generic */
+ duk_remove(ctx, -2);
+ /* ..., generic */
+}
+
+static duk_int_t dukky_push_context_dump(duk_context *ctx, void *udata)
+{
+ duk_push_context_dump(ctx);
+ return 1;
+}
+
+void dukky_log_stack_frame(duk_context *ctx, const char * reason)
+{
+ if (duk_safe_call(ctx, dukky_push_context_dump, NULL, 0, 1) != 0) {
+ duk_pop(ctx);
+ duk_push_string(ctx, "[???]");
+ }
+ NSLOG(dukky, DEEPDEBUG, "%s, stack is: %s", reason, duk_safe_to_string(ctx, -1));
+ duk_pop(ctx);
+}
+
+
+/* exported interface documented in js.h */
+bool
+js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name)
+{
+ bool ret = false;
+ assert(thread);
+
+ if (txt == NULL || txtlen == 0) {
+ return false;
+ }
+
+ if (thread->pending_destroy) {
+ NSLOG(dukky, DEEPDEBUG, "Skipping exec call because thread is dead");
return false;
}
+
+ dukky_enter_thread(thread);
+
+ duk_set_top(CTX, 0);
+ NSLOG(dukky, DEEPDEBUG, "Running %"PRIsizet" bytes from %s", txtlen, name);
+ /* NSLOG(dukky, DEEPDEBUG, "\n%s\n", txt); */
+
+ dukky_reset_start_time(CTX);
+ if (name != NULL) {
+ duk_push_string(CTX, name);
+ } else {
+ duk_push_string(CTX, "?unknown source?");
+ }
+ if (duk_pcompile_lstring_filename(CTX,
+ DUK_COMPILE_EVAL,
+ (const char *)txt,
+ txtlen) != 0) {
+ NSLOG(dukky, DEBUG, "Failed to compile JavaScript input");
+ goto handle_error;
+ }
+
+ if (duk_pcall(CTX, 0/*nargs*/) == DUK_EXEC_ERROR) {
+ NSLOG(dukky, DEBUG, "Failed to execute JavaScript");
+ goto handle_error;
+ }
+
if (duk_get_top(CTX) == 0) duk_push_boolean(CTX, false);
- NSLOG(netsurf, INFO, "Returning %s",
+ NSLOG(dukky, DEEPDEBUG, "Returning %s",
duk_get_boolean(CTX, 0) ? "true" : "false");
- return duk_get_boolean(CTX, 0);
+ ret = duk_get_boolean(CTX, 0);
+ goto out;
+
+handle_error:
+ dukky_dump_error(CTX);
+out:
+ dukky_leave_thread(thread);
+ return ret;
+}
+
+static const char* dukky_event_proto(dom_event *evt)
+{
+ const char *ret = PROTO_NAME(EVENT);
+ dom_string *type = NULL;
+ dom_exception err;
+
+ err = dom_event_get_type(evt, &type);
+ if (err != DOM_NO_ERR) {
+ goto out;
+ }
+
+ if (dom_string_isequal(type, corestring_dom_keydown)) {
+ ret = PROTO_NAME(KEYBOARDEVENT);
+ goto out;
+ } else if (dom_string_isequal(type, corestring_dom_keyup)) {
+ ret = PROTO_NAME(KEYBOARDEVENT);
+ goto out;
+ } else if (dom_string_isequal(type, corestring_dom_keypress)) {
+ ret = PROTO_NAME(KEYBOARDEVENT);
+ goto out;
+ }
+
+out:
+ if (type != NULL) {
+ dom_string_unref(type);
+ }
+
+ return ret;
}
/*** New style event handling ***/
-static void dukky_push_event(duk_context *ctx, dom_event *evt)
+void dukky_push_event(duk_context *ctx, dom_event *evt)
{
/* ... */
duk_get_global_string(ctx, EVENT_MAGIC);
@@ -693,7 +1017,7 @@ static void dukky_push_event(duk_context *ctx, dom_event *evt)
duk_pop(ctx);
/* ... events */
duk_push_pointer(ctx, evt);
- if (dukky_create_object(ctx, PROTO_NAME(EVENT), 1) != DUK_EXEC_SUCCESS) {
+ if (dukky_create_object(ctx, dukky_event_proto(evt), 1) != DUK_EXEC_SUCCESS) {
/* ... events err */
duk_pop(ctx);
/* ... events */
@@ -721,9 +1045,18 @@ static void dukky_push_handler_code_(duk_context *ctx, dom_string *name,
dom_exception exc;
dom_node_type ntype;
- /* Currently safe since libdom has no event targets which are not
- * nodes. Reconsider this as and when we work out how to have
- * window do stuff
+ /* If et is NULL, then we're actually dealing with the Window object
+ * which has no default handlers and no way to assign handlers
+ * which aren't directly stored in the HANDLER_MAGIC
+ */
+ if (et == NULL) {
+ duk_push_lstring(ctx, "", 0);
+ return;
+ }
+
+ /* The rest of this assumes et is a proper event target and expands
+ * out from there based on the assumption that all valid event targets
+ * are nodes.
*/
exc = dom_node_get_node_type(et, &ntype);
if (exc != DOM_NO_ERR) {
@@ -788,7 +1121,7 @@ bool dukky_get_current_value_of_event_handler(duk_context *ctx,
/* ... node fullhandlersrc filename */
if (duk_pcompile(ctx, DUK_COMPILE_FUNCTION) != 0) {
/* ... node err */
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"Unable to proceed with handler, could not compile");
duk_pop_2(ctx);
return false;
@@ -809,9 +1142,7 @@ bool dukky_get_current_value_of_event_handler(duk_context *ctx,
static void dukky_generic_event_handler(dom_event *evt, void *pw)
{
- duk_memory_functions funcs;
duk_context *ctx = (duk_context *)pw;
- jscontext *jsctx;
dom_string *name;
dom_exception exc;
dom_event_target *targ;
@@ -819,31 +1150,27 @@ static void dukky_generic_event_handler(dom_event *evt, void *pw)
duk_uarridx_t idx;
event_listener_flags flags;
- /* Retrieve the JS context from the Duktape context */
- duk_get_memory_functions(ctx, &funcs);
- jsctx = funcs.udata;
-
- NSLOG(netsurf, INFO, "WOOP WOOP, An event:");
+ NSLOG(dukky, DEBUG, "Handling an event in duktape interface...");
exc = dom_event_get_type(evt, &name);
if (exc != DOM_NO_ERR) {
- NSLOG(netsurf, INFO, "Unable to find the event name");
+ NSLOG(dukky, DEBUG, "Unable to find the event name");
return;
}
- NSLOG(netsurf, INFO, "Event's name is %*s", dom_string_length(name),
+ NSLOG(dukky, DEBUG, "Event's name is %*s", (int)dom_string_length(name),
dom_string_data(name));
exc = dom_event_get_event_phase(evt, &phase);
if (exc != DOM_NO_ERR) {
- NSLOG(netsurf, INFO, "Unable to get event phase");
+ NSLOG(dukky, WARNING, "Unable to get event phase");
return;
}
- NSLOG(netsurf, INFO, "Event phase is: %s (%d)",
+ NSLOG(dukky, DEBUG, "Event phase is: %s (%d)",
phase == DOM_CAPTURING_PHASE ? "capturing" : phase == DOM_AT_TARGET ? "at-target" : phase == DOM_BUBBLING_PHASE ? "bubbling" : "unknown",
(int)phase);
exc = dom_event_get_current_target(evt, &targ);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
- NSLOG(netsurf, INFO, "Unable to find the event target");
+ NSLOG(dukky, DEBUG, "Unable to find the event target");
return;
}
@@ -857,7 +1184,7 @@ static void dukky_generic_event_handler(dom_event *evt, void *pw)
if (dukky_push_node(ctx, (dom_node *)targ) == false) {
dom_string_unref(name);
dom_node_unref(targ);
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"Unable to push JS node representation?!");
return;
}
@@ -870,15 +1197,15 @@ static void dukky_generic_event_handler(dom_event *evt, void *pw)
/* ... handler node */
dukky_push_event(ctx, evt);
/* ... handler node event */
- (void) nsu_getmonotonic_ms(&jsctx->exec_start_time);
+ dukky_reset_start_time(ctx);
if (duk_pcall_method(ctx, 1) != 0) {
/* Failed to run the method */
/* ... err */
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"OH NOES! An error running a callback. Meh.");
exc = dom_event_stop_immediate_propagation(evt);
if (exc != DOM_NO_ERR)
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"WORSE! could not stop propagation");
duk_get_prop_string(ctx, -1, "name");
duk_get_prop_string(ctx, -2, "message");
@@ -886,13 +1213,13 @@ static void dukky_generic_event_handler(dom_event *evt, void *pw)
duk_get_prop_string(ctx, -4, "lineNumber");
duk_get_prop_string(ctx, -5, "stack");
/* ... err name message fileName lineNumber stack */
- NSLOG(netsurf, INFO, "Uncaught error in JS: %s: %s",
+ NSLOG(dukky, DEBUG, "Uncaught error in JS: %s: %s",
duk_safe_to_string(ctx, -5),
duk_safe_to_string(ctx, -4));
- NSLOG(netsurf, INFO, " was at: %s line %s",
+ NSLOG(dukky, INFO, " was at: %s line %s",
duk_safe_to_string(ctx, -3),
duk_safe_to_string(ctx, -2));
- NSLOG(netsurf, INFO, " Stack trace: %s",
+ NSLOG(dukky, INFO, " Stack trace: %s",
duk_safe_to_string(ctx, -1));
duk_pop_n(ctx, 6);
@@ -969,15 +1296,15 @@ handle_extras:
/* ... copy handler callback node */
dukky_push_event(ctx, evt);
/* ... copy handler callback node event */
- (void) nsu_getmonotonic_ms(&jsctx->exec_start_time);
+ dukky_reset_start_time(ctx);
if (duk_pcall_method(ctx, 1) != 0) {
/* Failed to run the method */
/* ... copy handler err */
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"OH NOES! An error running a callback. Meh.");
exc = dom_event_stop_immediate_propagation(evt);
if (exc != DOM_NO_ERR)
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"WORSE! could not stop propagation");
duk_get_prop_string(ctx, -1, "name");
duk_get_prop_string(ctx, -2, "message");
@@ -985,14 +1312,14 @@ handle_extras:
duk_get_prop_string(ctx, -4, "lineNumber");
duk_get_prop_string(ctx, -5, "stack");
/* ... err name message fileName lineNumber stack */
- NSLOG(netsurf, INFO, "Uncaught error in JS: %s: %s",
+ NSLOG(dukky, DEBUG, "Uncaught error in JS: %s: %s",
duk_safe_to_string(ctx, -5),
duk_safe_to_string(ctx, -4));
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
" was at: %s line %s",
duk_safe_to_string(ctx, -3),
duk_safe_to_string(ctx, -2));
- NSLOG(netsurf, INFO, " Stack trace: %s",
+ NSLOG(dukky, DEBUG, " Stack trace: %s",
duk_safe_to_string(ctx, -1));
duk_pop_n(ctx, 7);
@@ -1023,8 +1350,14 @@ void dukky_register_event_listener_for(duk_context *ctx,
dom_exception exc;
/* ... */
- if (dukky_push_node(ctx, (struct dom_node *)ele) == false)
- return;
+ if (ele == NULL) {
+ /* A null element is the Window object */
+ duk_push_global_object(ctx);
+ } else {
+ /* Non null elements must be pushed as a node object */
+ if (dukky_push_node(ctx, (struct dom_node *)ele) == false)
+ return;
+ }
/* ... node */
duk_get_prop_string(ctx, -1, HANDLER_LISTENER_MAGIC);
/* ... node handlers */
@@ -1045,18 +1378,26 @@ void dukky_register_event_listener_for(duk_context *ctx,
/* ... node handlers */
duk_pop_2(ctx);
/* ... */
+ if (ele == NULL) {
+ /* Nothing more to do, Window doesn't register in the
+ * normal event listener flow
+ */
+ return;
+ }
+
+ /* Otherwise add an event listener to the element */
exc = dom_event_listener_create(dukky_generic_event_handler, ctx,
&listen);
if (exc != DOM_NO_ERR) return;
exc = dom_event_target_add_event_listener(
ele, name, listen, capture);
if (exc != DOM_NO_ERR) {
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"Unable to register listener for %p.%*s", ele,
- dom_string_length(name), dom_string_data(name));
+ (int)dom_string_length(name), dom_string_data(name));
} else {
- NSLOG(netsurf, INFO, "have registered listener for %p.%*s",
- ele, dom_string_length(name), dom_string_data(name));
+ NSLOG(dukky, DEBUG, "have registered listener for %p.%*s",
+ ele, (int)dom_string_length(name), dom_string_data(name));
}
dom_event_listener_unref(listen);
}
@@ -1127,9 +1468,9 @@ void dukky_shuffle_array(duk_context *ctx, duk_uarridx_t idx)
}
-void js_handle_new_element(jscontext *ctx, struct dom_element *node)
+void js_handle_new_element(jsthread *thread, struct dom_element *node)
{
- assert(ctx);
+ assert(thread);
assert(node);
dom_namednodemap *map;
dom_exception exc;
@@ -1152,6 +1493,8 @@ void js_handle_new_element(jscontext *ctx, struct dom_element *node)
if (exc != DOM_NO_ERR) return;
if (map == NULL) return;
+ dukky_enter_thread(thread);
+
exc = dom_namednodemap_get_length(map, &siz);
if (exc != DOM_NO_ERR) goto out;
@@ -1201,11 +1544,14 @@ out:
dom_node_unref(attr);
dom_namednodemap_unref(map);
+
+ dukky_leave_thread(thread);
}
-void js_event_cleanup(jscontext *ctx, struct dom_event *evt)
+void js_event_cleanup(jsthread *thread, struct dom_event *evt)
{
- assert(ctx);
+ assert(thread);
+ dukky_enter_thread(thread);
/* ... */
duk_get_global_string(CTX, EVENT_MAGIC);
/* ... EVENT_MAP */
@@ -1215,15 +1561,16 @@ void js_event_cleanup(jscontext *ctx, struct dom_event *evt)
/* ... EVENT_MAP */
duk_pop(CTX);
/* ... */
+ dukky_leave_thread(thread);
}
-bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, struct dom_node *target)
+bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc, struct dom_node *target)
{
dom_exception exc;
dom_event *evt;
dom_event_target *body;
- NSLOG(netsurf, INFO, "Event: %s (doc=%p, target=%p)", type, doc,
+ NSLOG(dukky, DEBUG, "Event: %s (doc=%p, target=%p)", type, doc,
target);
/** @todo Make this more generic, this only handles load and only
@@ -1253,6 +1600,7 @@ bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, s
dom_event_unref(evt);
return true;
}
+ dukky_enter_thread(thread);
/* ... */
duk_get_global_string(CTX, HANDLER_MAGIC);
/* ... handlers */
@@ -1269,16 +1617,22 @@ bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, s
exc = dom_html_document_get_body(doc, &body);
if (exc != DOM_NO_ERR) {
dom_event_unref(evt);
+ dukky_leave_thread(thread);
return true;
}
dukky_push_node(CTX, (struct dom_node *)body);
/* ... handlers bodynode */
if (dukky_get_current_value_of_event_handler(
CTX, corestring_dom_load, body) == false) {
+ /* Unref the body, we don't need it any more */
+ dom_node_unref(body);
/* ... handlers */
duk_pop(CTX);
+ dukky_leave_thread(thread);
return true;
}
+ /* Unref the body, we don't need it any more */
+ dom_node_unref(body);
/* ... handlers handler bodynode */
duk_pop(CTX);
}
@@ -1291,11 +1645,11 @@ bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, s
/* ... handler Window */
dukky_push_event(CTX, evt);
/* ... handler Window event */
- (void) nsu_getmonotonic_ms(&ctx->exec_start_time);
+ dukky_reset_start_time(CTX);
if (duk_pcall_method(CTX, 1) != 0) {
/* Failed to run the handler */
/* ... err */
- NSLOG(netsurf, INFO,
+ NSLOG(dukky, DEBUG,
"OH NOES! An error running a handler. Meh.");
duk_get_prop_string(CTX, -1, "name");
duk_get_prop_string(CTX, -2, "message");
@@ -1303,25 +1657,27 @@ bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, s
duk_get_prop_string(CTX, -4, "lineNumber");
duk_get_prop_string(CTX, -5, "stack");
/* ... err name message fileName lineNumber stack */
- NSLOG(netsurf, INFO, "Uncaught error in JS: %s: %s",
+ NSLOG(dukky, DEBUG, "Uncaught error in JS: %s: %s",
duk_safe_to_string(CTX, -5),
duk_safe_to_string(CTX, -4));
- NSLOG(netsurf, INFO, " was at: %s line %s",
+ NSLOG(dukky, DEBUG, " was at: %s line %s",
duk_safe_to_string(CTX, -3),
duk_safe_to_string(CTX, -2));
- NSLOG(netsurf, INFO, " Stack trace: %s",
+ NSLOG(dukky, DEBUG, " Stack trace: %s",
duk_safe_to_string(CTX, -1));
duk_pop_n(CTX, 6);
/* ... */
- js_event_cleanup(ctx, evt);
+ js_event_cleanup(thread, evt);
dom_event_unref(evt);
+ dukky_leave_thread(thread);
return true;
}
/* ... result */
duk_pop(CTX);
/* ... */
- js_event_cleanup(ctx, evt);
+ js_event_cleanup(thread, evt);
dom_event_unref(evt);
+ dukky_leave_thread(thread);
return true;
}