diff --git a/src/ecmascript/mujs/Makefile b/src/ecmascript/mujs/Makefile index c86b49a3..80530f4f 100644 --- a/src/ecmascript/mujs/Makefile +++ b/src/ecmascript/mujs/Makefile @@ -4,6 +4,6 @@ INCLUDES += $(MUJS_CFLAGS) OBJS = attr.obj attributes.obj collection.obj console.obj document.obj element.obj form.obj \ forms.obj history.obj implementation.obj input.obj keyboard.obj location.obj \ - localstorage.obj navigator.obj nodelist.obj screen.obj unibar.obj window.obj xhr.obj + localstorage.obj message.obj navigator.obj nodelist.obj screen.obj unibar.obj window.obj xhr.obj include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/mujs/meson.build b/src/ecmascript/mujs/meson.build index e8a56bf7..b3c57c3b 100644 --- a/src/ecmascript/mujs/meson.build +++ b/src/ecmascript/mujs/meson.build @@ -1,2 +1,2 @@ srcs += files('attr.cpp', 'attributes.cpp', 'collection.cpp', 'console.cpp', 'document.cpp', 'element.cpp', 'form.cpp', 'forms.cpp', 'history.cpp', 'implementation.cpp', -'input.cpp', 'keyboard.cpp', 'localstorage.cpp', 'location.cpp', 'navigator.cpp', 'nodelist.cpp', 'screen.cpp', 'unibar.cpp', 'window.cpp', 'xhr.cpp') +'input.cpp', 'keyboard.cpp', 'localstorage.cpp', 'location.cpp', 'message.cpp', 'navigator.cpp', 'nodelist.cpp', 'screen.cpp', 'unibar.cpp', 'window.cpp', 'xhr.cpp') diff --git a/src/ecmascript/mujs/message.cpp b/src/ecmascript/mujs/message.cpp new file mode 100644 index 00000000..fc175377 --- /dev/null +++ b/src/ecmascript/mujs/message.cpp @@ -0,0 +1,177 @@ +/* The MuJS MessageEvent object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/message.h" +#include "ecmascript/timer.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "main/timer.h" +#include "network/connection.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/download.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include +#include +#include +#include + +static void mjs_messageEvent_get_property_data(js_State *J); +static void mjs_messageEvent_get_property_lastEventId(js_State *J); +static void mjs_messageEvent_get_property_origin(js_State *J); +static void mjs_messageEvent_get_property_source(js_State *J); + +struct message_event { + char *data; + char *lastEventId; + char *origin; + char *source; +}; + +static +void mjs_messageEvent_finalizer(js_State *J, void *val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct message_event *event = (struct message_event *)val; + + if (event) { + mem_free_if(event->data); + mem_free_if(event->lastEventId); + mem_free_if(event->origin); + mem_free_if(event->source); + mem_free(event); + } +} + +static int lastEventId; + +void +mjs_push_messageEvent(js_State *J, char *data, char *origin, char *source) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct message_event *event = (struct message_event *)mem_calloc(1, sizeof(*event)); + + if (!event) { + js_pushnull(J); + return; + } + event->data = null_or_stracpy(data); + event->origin = null_or_stracpy(origin); + event->source = null_or_stracpy(source); + + char id[32]; + + snprintf(id, "%d", 31, ++lastEventId); + event->lastEventId = stracpy(id); + + js_newobject(J); + { + js_newuserdata(J, "event", event, mjs_messageEvent_finalizer); + + addproperty(J, "data", mjs_messageEvent_get_property_data, NULL); + addproperty(J, "lastEventId", mjs_messageEvent_get_property_lastEventId, NULL); + addproperty(J, "origin", mjs_messageEvent_get_property_origin, NULL); + addproperty(J, "source", mjs_messageEvent_get_property_source, NULL); + } +} + +static void +mjs_messageEvent_get_property_data(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct message_event *event = (struct message_event *)js_touserdata(J, 0, "event"); + + if (!event || !event->data) { + js_pushnull(J); + return; + } + js_pushstring(J, event->data); +} + +static void +mjs_messageEvent_get_property_lastEventId(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct message_event *event = (struct message_event *)js_touserdata(J, 0, "event"); + + if (!event || !event->lastEventId) { + js_pushnull(J); + return; + } + js_pushstring(J, event->lastEventId); +} + +static void +mjs_messageEvent_get_property_origin(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct message_event *event = (struct message_event *)js_touserdata(J, 0, "event"); + + if (!event || !event->origin) { + js_pushnull(J); + return; + } + js_pushstring(J, event->origin); +} + +static void +mjs_messageEvent_get_property_source(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct message_event *event = (struct message_event *)js_touserdata(J, 0, "event"); + + if (!event || !event->source) { + js_pushnull(J); + return; + } + js_pushstring(J, event->source); +} diff --git a/src/ecmascript/mujs/message.h b/src/ecmascript/mujs/message.h new file mode 100644 index 00000000..fa83f6b2 --- /dev/null +++ b/src/ecmascript/mujs/message.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_MUJS_MESSAGE_H +#define EL__ECMASCRIPT_MUJS_MESSAGE_H + +#include + +void mjs_push_messageEvent(js_State *J, char *data, char *origin, char *source); + +#endif diff --git a/src/ecmascript/mujs/window.cpp b/src/ecmascript/mujs/window.cpp index 21b478e0..5f36a5fa 100644 --- a/src/ecmascript/mujs/window.cpp +++ b/src/ecmascript/mujs/window.cpp @@ -1,4 +1,4 @@ -/* The Quickjs window object implementation. */ +/* The MuJS window object implementation. */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -21,6 +21,7 @@ #include "document/view.h" #include "ecmascript/ecmascript.h" #include "ecmascript/mujs.h" +#include "ecmascript/mujs/message.h" #include "ecmascript/mujs/window.h" #include "ecmascript/timer.h" #include "intl/libintl.h" @@ -44,6 +45,43 @@ #include "viewer/text/link.h" #include "viewer/text/vs.h" +struct listener { + LIST_HEAD(struct listener); + char *typ; + char *fun; +}; + +struct el_window { + struct ecmascript_interpreter *interpreter; + char *thisval; + LIST_OF(struct listener) listeners; + char *onmessage; +}; + +struct el_message { + char *messageObject; + struct el_window *elwin; +}; + +static +void mjs_window_finalizer(js_State *J, void *val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct el_window *elwin = (struct el_window *)val; + + if (elwin) { + struct listener *l; + + foreach(l, elwin->listeners) { + mem_free_set(&l->typ, NULL); + } + free_list(elwin->listeners); + mem_free(elwin); + } +} + static void mjs_window_get_property_closed(js_State *J) { @@ -387,14 +425,211 @@ mjs_window_toString(js_State *J) js_pushstring(J, "[window object]"); } +static void +mjs_window_addEventListener(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct el_window *elwin = (struct el_window *)js_touserdata(J, 0, "window"); + + if (!elwin) { + elwin = (struct el_window *)mem_calloc(1, sizeof(*elwin)); + + if (!elwin) { + js_pushnull(J); + return; + } + init_list(elwin->listeners); + elwin->interpreter = interpreter; + elwin->thisval = js_ref(J); + js_newuserdata(J, "window", elwin, mjs_window_finalizer); + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + char *method = stracpy(str); + + if (!method) { + js_pushnull(J); + return; + } + js_copy(J, 2); + const char *fun = js_ref(J); + + struct listener *l; + + foreach(l, elwin->listeners) { + if (strcmp(l->typ, method)) { + continue; + } + if (!strcmp(l->fun, fun)) { + mem_free(method); + js_pushundefined(J); + return; + } + } + struct listener *n = (struct listener *)mem_calloc(1, sizeof(*n)); + + if (n) { + n->typ = method; + n->fun = fun; + add_to_list_end(elwin->listeners, n); + } + js_pushundefined(J); +} + +static void +mjs_window_removeEventListener(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct el_window *elwin = (struct el_window *)js_touserdata(J, 0, "window"); + + if (!elwin) { + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + char *method = stracpy(str); + + if (!method) { + js_pushnull(J); + return; + } + js_copy(J, 2); + const char *fun = js_ref(J); + + struct listener *l; + + foreach(l, elwin->listeners) { + if (strcmp(l->typ, method)) { + continue; + } + + if (!strcmp(l->fun, fun)) { + del_from_list(l); + mem_free_set(&l->typ, NULL); + if (l->fun) js_unref(J, l->fun); + mem_free(l); + mem_free(method); + js_pushundefined(J); + return; + } + } + mem_free(method); + js_pushundefined(J); +} + +static void +onmessage_run(void *data) +{ + struct el_message *mess = (struct el_message *)data; + + if (mess) { + struct el_window *elwin = mess->elwin; + + if (!elwin) { + mem_free(mess); + return; + } + + struct ecmascript_interpreter *interpreter = elwin->interpreter; + js_State *J = (js_State *)interpreter->backend_data; + + struct listener *l; + +// TODO parameers for js_pcall + foreach(l, elwin->listeners) { + if (strcmp(l->typ, "message")) { + continue; + } + js_getregistry(J, l->fun); /* retrieve the js function from the registry */ + js_getregistry(J, elwin->thisval); + js_pcall(J, 0); + js_pop(J, 1); + } + + if (elwin->onmessage) { + js_getregistry(J, elwin->onmessage); /* retrieve the js function from the registry */ + js_getregistry(J, mess->messageObject); + js_pcall(J, 0); + js_pop(J, 1); + } + check_for_rerender(interpreter, "window_message"); + } +} + +static void +mjs_window_postMessage(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct el_window *elwin = (struct el_window *)js_touserdata(J, 0, "window"); + + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + char *data = stracpy(str); + + const char *str2 = js_tostring(J, 2); + + if (!str2) { + mem_free_if(data); + js_pushnull(J); + return; + } + char *targetOrigin = stracpy(str2); + char *source = stracpy("TODO"); + + mjs_push_messageEvent(J, data, targetOrigin, source); + + mem_free_if(data); + mem_free_if(targetOrigin); + mem_free_if(source); + + js_pop(J, 1); + char *val = js_ref(J); + + struct el_message *mess = (struct el_message *)mem_calloc(1, sizeof(*mess)); + if (!mess) { + js_pushnull(J); + return; + } + mess->messageObject = val; + mess->elwin = elwin; + register_bottom_half(onmessage_run, mess); + + js_pushundefined(J); +} + int mjs_window_init(js_State *J) { js_newobject(J); { + addmethod(J, "addEventListener", mjs_window_addEventListener, 3); addmethod(J, "alert", mjs_window_alert, 1); addmethod(J, "clearTimeout", mjs_window_clearTimeout, 1); addmethod(J, "open", mjs_window_open, 3); + addmethod(J, "postMessage", mjs_window_postMessage, 3); + addmethod(J, "removeEventListener", mjs_window_removeEventListener, 3); addmethod(J, "setTimeout", mjs_window_setTimeout, 2); addmethod(J, "toString", mjs_window_toString, 0);