summaryrefslogtreecommitdiff
path: root/content/handlers/javascript/duktape/Window.bnd
diff options
context:
space:
mode:
Diffstat (limited to 'content/handlers/javascript/duktape/Window.bnd')
-rw-r--r--content/handlers/javascript/duktape/Window.bnd611
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..f7d143f6c 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(sizeof *sched, 1);
+ 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();