diff options
Diffstat (limited to 'content/handlers/javascript/duktape/Window.bnd')
-rw-r--r-- | content/handlers/javascript/duktape/Window.bnd | 611 |
1 files changed, 595 insertions, 16 deletions
diff --git a/content/handlers/javascript/duktape/Window.bnd b/content/handlers/javascript/duktape/Window.bnd index 3f680d47d..a5ff2002d 100644 --- a/content/handlers/javascript/duktape/Window.bnd +++ b/content/handlers/javascript/duktape/Window.bnd @@ -11,24 +11,295 @@ class Window { private struct browser_window * win; private struct html_content * htmlc; + private struct window_schedule_s * schedule_ring; + private bool closed_down; prologue %{ +#include "utils/corestrings.h" #include "utils/nsurl.h" #include "netsurf/browser_window.h" #include "content/hlcache.h" -#include "render/html.h" -#include "render/html_internal.h" +#include "html/html.h" +#include "html/private.h" +#include "desktop/gui_internal.h" +#include "netsurf/misc.h" +#include "utils/ring.h" +#include "netsurf/inttypes.h" + +#define WINDOW_CALLBACKS MAGIC(WindowCallbacks) +#define HANDLER_MAGIC MAGIC(HANDLER_MAP) + +static size_t next_handle = 0; + +typedef struct window_schedule_s { + window_private_t *owner; + duk_context *ctx; + struct window_schedule_s *r_next; + struct window_schedule_s *r_prev; + size_t handle; + int repeat_timeout; + bool running; +} window_schedule_t; + +static void window_remove_callback_bits(duk_context *ctx, size_t handle) { + /* stack is ... */ + duk_push_global_object(ctx); + duk_get_prop_string(ctx, -1, WINDOW_CALLBACKS); + /* stack is ..., win, cbt */ + duk_push_int(ctx, (duk_int_t)handle); + /* ..., win, cbt, handle */ + duk_del_prop(ctx, -2); + /* ..., win, cbt */ + duk_pop_2(ctx); + /* ... */ +} + +static void +window_call_callback(duk_context *ctx, size_t handle, bool clear_entry) +{ + NSLOG(dukky, DEEPDEBUG, "ctx=%p, handle=%"PRIsizet, ctx, handle); + /* Stack is ... */ + duk_push_global_object(ctx); + /* ..., win */ + duk_get_prop_string(ctx, -1, WINDOW_CALLBACKS); + /* ..., win, cbt */ + duk_push_int(ctx, (duk_int_t)handle); + /* ..., win, cbt, handle */ + duk_get_prop(ctx, -2); + /* ..., win, cbt, cbo */ + //dukky_log_stack_frame(ctx, "On entry to callback"); + /* ..., win, cbt, cbo */ + /* What we want to do is call cbo.func passing all of cbo.args */ + duk_get_prop_string(ctx, -1, "func"); + duk_get_prop_string(ctx, -2, "args"); + /* ..., win, cbt, cbo, func, argarr */ + duk_size_t arrlen = duk_get_length(ctx, -1); + for (duk_size_t i = 0; i < arrlen; ++i) { + duk_push_int(ctx, (duk_int_t)i); + duk_get_prop(ctx, -(2+i)); + } + /* ..., win, cbt, cbo, func, argarr, args... */ + duk_remove(ctx, -(arrlen+1)); + /* ..., win, cbt, cbo, func, args... */ + //dukky_log_stack_frame(ctx, "Just before call"); + (void) dukky_pcall(ctx, arrlen, true); + /* ..., win, cbt, cbo, retval */ + if (clear_entry) { + NSLOG(dukky, DEEPDEBUG, "Not recurring callback, removing from cbt"); + duk_pop_n(ctx, 2); + /* ..., win, cbt */ + duk_push_int(ctx, (duk_int_t)handle); + /* ..., win, cbt, handle */ + duk_del_prop(ctx, -2); + /* ..., win, cbt */ + duk_pop_n(ctx, 2); + } else { + duk_pop_n(ctx, 4); + } + /* ... */ + //dukky_log_stack_frame(ctx, "On leaving callback"); +} + + +static void +window_schedule_callback(void *p) +{ + window_schedule_t *priv = (window_schedule_t *)p; + + NSLOG(dukky, DEEPDEBUG, + "Entered window scheduler callback: %"PRIsizet, priv->handle); + + priv->running = true; + window_call_callback(priv->ctx, + priv->handle, + priv->repeat_timeout == 0); + priv->running = false; + + if (priv->repeat_timeout > 0) { + /* Reschedule */ + NSLOG(dukky, DEEPDEBUG, + "Rescheduling repeating callback %"PRIsizet, + priv->handle); + guit->misc->schedule(priv->repeat_timeout, + window_schedule_callback, + priv); + } else { + NSLOG(dukky, DEEPDEBUG, + "Removing completed callback %"PRIsizet, priv->handle); + /* Remove this from the ring */ + RING_REMOVE(priv->owner->schedule_ring, priv); + window_remove_callback_bits(priv->ctx, priv->handle); + free(priv); + } +} + +static size_t +window_alloc_new_callback(duk_context *ctx, + window_private_t *window, + bool repeating, + int timeout) +{ + size_t new_handle = next_handle++; + window_schedule_t *sched = calloc(1, sizeof *sched); + if (sched == NULL) { + return new_handle; + } + sched->owner = window; + sched->ctx = ctx; + sched->handle = new_handle; + sched->repeat_timeout = repeating ? timeout : 0; + sched->running = false; + + RING_INSERT(window->schedule_ring, sched); + + /* Next, the duktape stack looks like: func, timeout, ... + * In order to proceed, we want to put into the WINDOW_CALLBACKS + * keyed by the handle, an object containing the call to make and + * the array of arguments to call the function with + */ + duk_idx_t nargs = duk_get_top(ctx) - 2; + duk_push_global_object(ctx); + duk_get_prop_string(ctx, -1, WINDOW_CALLBACKS); + duk_push_int(ctx, (duk_int_t)new_handle); + duk_push_object(ctx); + /* stack is: func, timeout, ..., win, cbt, handle, cbo */ + + /* put the function into the cbo */ + duk_dup(ctx, 0); + duk_put_prop_string(ctx, -2, "func"); + + /* Now the arguments */ + duk_push_array(ctx); + for (duk_idx_t i = 0; i < nargs; ++i) { + duk_dup(ctx, 2 + i); /* Dup the arg */ + duk_put_prop_index(ctx, -2, i); /* arr[i] = arg[i] */ + } + duk_put_prop_string(ctx, -2, "args"); + /* stack is: func, timeout, ..., win, cbt, handle, cbo */ + duk_put_prop(ctx, -3); + /* stack is: func, timeout, ..., win, cbt */ + duk_pop_2(ctx); + /* And we're back to func, timeout, ... */ + + guit->misc->schedule(timeout, window_schedule_callback, sched); + NSLOG(dukky, DEEPDEBUG, "Scheduled callback %"PRIsizet" for %d ms from now", new_handle, timeout); + + return new_handle; +} + +static void +window_remove_callback_by_handle(duk_context *ctx, + window_private_t *window, + size_t handle) +{ + int res; + + RING_ITERATE_START(window_schedule_t, window->schedule_ring, sched) { + if (sched->handle == handle) { + if (sched->running) { + NSLOG(dukky, DEEPDEBUG, + "Cancelling in-train callback %"PRIsizet, + sched->handle); + sched->repeat_timeout = 0; + } else { + NSLOG(dukky, DEEPDEBUG, + "Cancelled callback %"PRIsizet, + sched->handle); + res = guit->misc->schedule(-1, + window_schedule_callback, + sched); + assert(res == NSERROR_OK); + RING_REMOVE(window->schedule_ring, sched); + window_remove_callback_bits(ctx, sched->handle); + free(sched); + } + RING_ITERATE_STOP(window->schedule_ring, sched); + } + } RING_ITERATE_END(window->schedule_ring, sched); +} + +/* This is the dodgy thread closedown method */ +static duk_ret_t dukky_window_closedown_thread(duk_context *ctx) +{ + window_private_t *priv = NULL; + + duk_push_global_object(ctx); + duk_get_prop_string(ctx, -1, dukky_magic_string_private); + priv = duk_get_pointer(ctx, -1); + duk_pop_2(ctx); + + if (priv == NULL) { + return 0; + } + + priv->closed_down = true; + + NSLOG(dukky, DEEPDEBUG, "Closing down thread"); + while (priv->schedule_ring != NULL) { + window_schedule_t *to_remove = NULL; + // Find a schedule item to remove + RING_ITERATE_START(window_schedule_t, priv->schedule_ring, sched) { + if (sched->running == false) { + // This one is not running, we can remove it + to_remove = sched; + RING_ITERATE_STOP(window->schedule_ring, sched); + } else if (sched->repeat_timeout != 0) { + // This one is running and has yet to be + // cancelled, so prevent it rescheduling itself + NSLOG(dukky, DEEPDEBUG, + "Cancelling in-train callback %"PRIsizet, + sched->handle); + sched->repeat_timeout = 0; + } + } RING_ITERATE_END(priv->schedule_ring, sched); + + if (to_remove == NULL) { + // We didn't find any non-running callbacks + // so let's log that and break out of the closedown + // loop so we can continue and hopefully close down + NSLOG(dukky, DEEPDEBUG, + "Leaving in-train callbacks to unwind"); + break; + } + + // Remove the handle we found, this will reduce the callback + // scheduler ring by one and perhaps leave it empty so we can + // finish the closedown. + window_remove_callback_by_handle(ctx, + priv, + to_remove->handle); + } + + return 0; +} + %}; }; init Window(struct browser_window *win, struct html_content *htmlc) %{ + /* It makes no sense if win or htmlc are NULL */ + assert(win != NULL); + assert(htmlc != NULL); /* element window */ priv->win = win; priv->htmlc = htmlc; - NSLOG(netsurf, INFO, "win=%p htmlc=%p", priv->win, priv->htmlc); + priv->schedule_ring = NULL; + priv->closed_down = false; + NSLOG(netsurf, DEEPDEBUG, "win=%p htmlc=%p", priv->win, priv->htmlc); + + NSLOG(netsurf, DEEPDEBUG, + "URL is %s", nsurl_access(browser_window_access_url(priv->win))); + duk_push_object(ctx); + duk_put_prop_string(ctx, 0, WINDOW_CALLBACKS); +%} - NSLOG(netsurf, INFO, - "URL is %s", nsurl_access(browser_window_get_url(priv->win))); +fini Window() +%{ + NSLOG(dukky, DEEPDEBUG, "Shutting down Window %p", priv->win); + /* Cheaply iterate the schedule ring, cancelling any pending callbacks */ + while (priv->schedule_ring != NULL) { + window_remove_callback_by_handle(ctx, priv, priv->schedule_ring->handle); + } %} prototype Window() @@ -36,25 +307,112 @@ prototype Window() #define EXPOSE(v) \ duk_get_global_string(ctx, #v); \ duk_put_prop_string(ctx, 0, #v) - /* steal undefined */ + /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects */ + /* ** Value properties */ + EXPOSE(Infinity); + EXPOSE(NaN); EXPOSE(undefined); + EXPOSE(null); + EXPOSE(globalThis); + + /* ** Function properties */ EXPOSE(eval); - EXPOSE(Object); - EXPOSE(parseInt); + /* EXPOSE(uneval); */ /* Not standard, maybe not available */ + EXPOSE(isFinite); + EXPOSE(isNaN); EXPOSE(parseFloat); - EXPOSE(Array); + EXPOSE(parseInt); + EXPOSE(decodeURI); + EXPOSE(decodeURIComponent); + EXPOSE(encodeURI); + EXPOSE(encodeURIComponent); + EXPOSE(escape); + EXPOSE(unescape); + + /* ** Fundamental Objects */ + EXPOSE(Object); + EXPOSE(Function); + EXPOSE(Boolean); + EXPOSE(Symbol); + EXPOSE(Error); + EXPOSE(EvalError); + EXPOSE(InternalError); + EXPOSE(RangeError); + EXPOSE(ReferenceError); + EXPOSE(SyntaxError); + EXPOSE(TypeError); + EXPOSE(URIError); + + /* ** Numbers and Dates */ + EXPOSE(Number); + EXPOSE(BigInt); + EXPOSE(Math); EXPOSE(Date); + + /* ** Text Processing */ + EXPOSE(String); EXPOSE(RegExp); - EXPOSE(Math); - EXPOSE(Function); + + /* ** Indexed Collections */ + EXPOSE(Array); + EXPOSE(Int8Array); + EXPOSE(Uint8Array); + EXPOSE(Uint8ClampedArray); + EXPOSE(Int16Array); + EXPOSE(Uint16Array); + EXPOSE(Int32Array); + EXPOSE(Uint32Array); + EXPOSE(Float32Array); + EXPOSE(Float64Array); + /* EXPOSE(BigInt64Array); */ /* Duktape lacks this - nonstandard API */ + /* EXPOSE(BigUint64Array); */ /* Duktape lacks this - nonstandard API */ + + /* ** Keyed Collections */ + /* EXPOSE(Map); */ /* Duktape lacks this - ES6 */ + /* EXPOSE(Set); */ /* Duktape lacks this - ES6 */ + /* EXPOSE(WeakMap); */ /* Duktape lacks this - ES6 */ + /* EXPOSE(WeakSet); */ /* Duktape lacks this - ES6 */ + + /* Structured Data */ + EXPOSE(ArrayBuffer); + /* EXPOSE(SharedArrayBuffer); */ /* Duktape lacks this - experimental API */ + /* EXPOSE(Atomics); */ /* Duktape lacks this - experimental API */ + EXPOSE(DataView); + EXPOSE(JSON); + + /* ** Control abstraction properties */ + /* EXPOSE(Promise); */ /* Probably ought to be one of ours? Also ES6 */ + /* EXPOSE(Generator); */ /* Duktape and async? ES6 */ + /* EXPOSE(GeneratorFunction); */ /* Duktape and async? ES6 */ + /* EXPOSE(AsyncFunction); */ /* Duktape lacks this - experimental API */ + + /* Reflection */ + EXPOSE(Reflect); EXPOSE(Proxy); - EXPOSE(String); + + /* ** Internationalisation */ + /* Duktape lacks Intl - Maybe polyfill it? */ + /* There is suggestion that cdn.polyfill.io exists for it */ + + /* ** WebAssembly */ + /* As yet, Duktape lacks WA */ #undef EXPOSE + /* Add s3kr1t method to close the JS thread (browsing context) */ + duk_dup(ctx, 0); + duk_push_string(ctx, MAGIC(closedownThread)); + duk_push_c_function(ctx, dukky_window_closedown_thread, DUK_VARARGS); + duk_def_prop(ctx, -3, + DUK_DEFPROP_HAVE_VALUE | + DUK_DEFPROP_HAVE_WRITABLE | + DUK_DEFPROP_HAVE_ENUMERABLE | + DUK_DEFPROP_ENUMERABLE | + DUK_DEFPROP_HAVE_CONFIGURABLE); + duk_pop(ctx); %} getter Window::document() %{ - JS_LOG("priv=%p", priv); + NSLOG(netsurf, DEBUG, "priv=%p", priv); dom_document *doc = priv->htmlc->document; dukky_push_node(ctx, (struct dom_node *)doc); return 1; @@ -83,9 +441,11 @@ getter Window::console() getter Window::location() %{ + /* obtain location object for this window (if it exists) */ duk_push_this(ctx); duk_get_prop_string(ctx, -1, MAGIC(Location)); if (duk_is_undefined(ctx, -1)) { + /* location object did not previously exist so create it */ duk_pop(ctx); duk_push_pointer(ctx, llcache_handle_get_url(priv->htmlc->base.llcache)); @@ -137,8 +497,227 @@ setter Window::name() method Window::alert() %{ - duk_size_t msg_len; - const char *msg = duk_safe_to_lstring(ctx, 0, &msg_len); - NSLOG(netsurf, INFO, "JS ALERT: %*s", (int)msg_len, msg); + duk_idx_t dukky_argc = duk_get_top(ctx); + if (dukky_argc == 0) { + NSLOG(netsurf, INFO, "JS ALERT"); + } else { + duk_size_t msg_len; + const char *msg; + + if (!duk_is_string(ctx, 0)) { + duk_to_string(ctx, 0); + } + msg = duk_safe_to_lstring(ctx, 0, &msg_len); + NSLOG(netsurf, INFO, "JS ALERT: %*s", (int)msg_len, msg); + } + return 0; +%} + +method Window::setTimeout() +%{ + duk_idx_t argc = duk_get_top(ctx); + duk_int_t timeout = 10; + + if (priv->closed_down == true) { + return 0; /* coerced to undefined */ + } + + if (argc >= 2) { + timeout = duk_get_int(ctx, 1); + } + /* func, [timeout, args...] */ + if (timeout < 10) { timeout = 10; } + size_t handle = window_alloc_new_callback(ctx, priv, false, (int)timeout); + + duk_push_int(ctx, (duk_int_t)handle); + return 1; +%} + +method Window::setInterval() +%{ + duk_idx_t argc = duk_get_top(ctx); + duk_int_t timeout = 10; + + if (priv->closed_down == true) { + return 0; /* coerced to undefined */ + } + + if (argc >= 2) { + timeout = duk_get_int(ctx, 1); + } + /* func, [timeout, args...] */ + if (timeout < 10) { timeout = 10; } + size_t handle = window_alloc_new_callback(ctx, priv, true, (int)timeout); + + duk_push_int(ctx, (duk_int_t)handle); + return 1; +%} + +method Window::clearTimeout() +%{ + duk_int_t handle = duk_get_int(ctx, 0); + window_remove_callback_by_handle(ctx, priv, (size_t) handle); + + return 0; +%} + +method Window::clearInterval() +%{ + duk_int_t handle = duk_get_int(ctx, 0); + window_remove_callback_by_handle(ctx, priv, (size_t) handle); + return 0; %} + +getter Window::onabort(); +setter Window::onabort(); +getter Window::onafterprint(); +setter Window::onafterprint(); +getter Window::onautocompleteerror(); +setter Window::onautocompleteerror(); +getter Window::onautocomplete(); +setter Window::onautocomplete(); +getter Window::onbeforeprint(); +setter Window::onbeforeprint(); +getter Window::onbeforeunload(); +setter Window::onbeforeunload(); +getter Window::onblur(); +setter Window::onblur(); +getter Window::oncancel(); +setter Window::oncancel(); +getter Window::oncanplaythrough(); +setter Window::oncanplaythrough(); +getter Window::oncanplay(); +setter Window::oncanplay(); +getter Window::onchange(); +setter Window::onchange(); +getter Window::onclick(); +setter Window::onclick(); +getter Window::onclose(); +setter Window::onclose(); +getter Window::oncontextmenu(); +setter Window::oncontextmenu(); +getter Window::oncuechange(); +setter Window::oncuechange(); +getter Window::ondblclick(); +setter Window::ondblclick(); +getter Window::ondragend(); +setter Window::ondragend(); +getter Window::ondragenter(); +setter Window::ondragenter(); +getter Window::ondragexit(); +setter Window::ondragexit(); +getter Window::ondragleave(); +setter Window::ondragleave(); +getter Window::ondragover(); +setter Window::ondragover(); +getter Window::ondragstart(); +setter Window::ondragstart(); +getter Window::ondrag(); +setter Window::ondrag(); +getter Window::ondrop(); +setter Window::ondrop(); +getter Window::ondurationchange(); +setter Window::ondurationchange(); +getter Window::onemptied(); +setter Window::onemptied(); +getter Window::onended(); +setter Window::onended(); +getter Window::onerror(); +setter Window::onerror(); +getter Window::onfocus(); +setter Window::onfocus(); +getter Window::onhashchange(); +setter Window::onhashchange(); +getter Window::oninput(); +setter Window::oninput(); +getter Window::oninvalid(); +setter Window::oninvalid(); +getter Window::onkeydown(); +setter Window::onkeydown(); +getter Window::onkeypress(); +setter Window::onkeypress(); +getter Window::onkeyup(); +setter Window::onkeyup(); +getter Window::onlanguagechange(); +setter Window::onlanguagechange(); +getter Window::onloadeddata(); +setter Window::onloadeddata(); +getter Window::onloadedmetadata(); +setter Window::onloadedmetadata(); +getter Window::onloadstart(); +setter Window::onloadstart(); +getter Window::onload(); +setter Window::onload(); +getter Window::onmessage(); +setter Window::onmessage(); +getter Window::onmousedown(); +setter Window::onmousedown(); +getter Window::onmouseenter(); +setter Window::onmouseenter(); +getter Window::onmouseleave(); +setter Window::onmouseleave(); +getter Window::onmousemove(); +setter Window::onmousemove(); +getter Window::onmouseout(); +setter Window::onmouseout(); +getter Window::onmouseover(); +setter Window::onmouseover(); +getter Window::onmouseup(); +setter Window::onmouseup(); +getter Window::onoffline(); +setter Window::onoffline(); +getter Window::ononline(); +setter Window::ononline(); +getter Window::onpagehide(); +setter Window::onpagehide(); +getter Window::onpageshow(); +setter Window::onpageshow(); +getter Window::onpause(); +setter Window::onpause(); +getter Window::onplaying(); +setter Window::onplaying(); +getter Window::onplay(); +setter Window::onplay(); +getter Window::onpopstate(); +setter Window::onpopstate(); +getter Window::onprogress(); +setter Window::onprogress(); +getter Window::onratechange(); +setter Window::onratechange(); +getter Window::onreset(); +setter Window::onreset(); +getter Window::onresize(); +setter Window::onresize(); +getter Window::onscroll(); +setter Window::onscroll(); +getter Window::onseeked(); +setter Window::onseeked(); +getter Window::onseeking(); +setter Window::onseeking(); +getter Window::onselect(); +setter Window::onselect(); +getter Window::onshow(); +setter Window::onshow(); +getter Window::onsort(); +setter Window::onsort(); +getter Window::onstalled(); +setter Window::onstalled(); +getter Window::onstorage(); +setter Window::onstorage(); +getter Window::onsubmit(); +setter Window::onsubmit(); +getter Window::onsuspend(); +setter Window::onsuspend(); +getter Window::ontimeupdate(); +setter Window::ontimeupdate(); +getter Window::ontoggle(); +setter Window::ontoggle(); +getter Window::onunload(); +setter Window::onunload(); +getter Window::onvolumechange(); +setter Window::onvolumechange(); +getter Window::onwaiting(); +setter Window::onwaiting(); +getter Window::onwheel(); +setter Window::onwheel(); |