diff options
Diffstat (limited to 'content/handlers/javascript/duktape/dukky.c')
-rw-r--r-- | content/handlers/javascript/duktape/dukky.c | 606 |
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; } |