diff --git a/src/ecmascript/quickjs/Makefile b/src/ecmascript/quickjs/Makefile index febf2f6d..215ea443 100644 --- a/src/ecmascript/quickjs/Makefile +++ b/src/ecmascript/quickjs/Makefile @@ -2,7 +2,7 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config OBJS = attr.obj attributes.obj collection.obj console.obj document.obj element.obj form.obj \ - forms.obj heartbeat.obj history.obj implementation.obj input.obj location.obj \ + forms.obj heartbeat.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 include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/quickjs/element.cpp b/src/ecmascript/quickjs/element.cpp index 4a130d60..bd6c9093 100644 --- a/src/ecmascript/quickjs/element.cpp +++ b/src/ecmascript/quickjs/element.cpp @@ -26,6 +26,8 @@ #include "ecmascript/quickjs/attributes.h" #include "ecmascript/quickjs/collection.h" #include "ecmascript/quickjs/element.h" +#include "ecmascript/quickjs/heartbeat.h" +#include "ecmascript/quickjs/keyboard.h" #include "ecmascript/quickjs/nodelist.h" #include "ecmascript/quickjs/window.h" #include "intl/libintl.h" @@ -63,13 +65,37 @@ static JSClassID js_element_class_id; +struct listener { + LIST_HEAD(struct listener); + char *typ; + JSValue fun; +}; + +struct js_element_private { + LIST_OF(struct listener) listeners; + struct ecmascript_interpreter *interpreter; + JSValue thisval; + void *node; +}; + +static void * +js_getopaque(JSValueConst obj, JSClassID class_id) +{ + struct js_element_private *res = (struct js_element_private *)JS_GetOpaque(obj, class_id); + + if (!res) { + return NULL; + } + return res->node; +} + static JSValue js_element_get_property_attributes(JSContext *ctx, JSValueConst this_val) { #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -92,7 +118,7 @@ js_element_get_property_children(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -134,7 +160,7 @@ js_element_get_property_childElementCount(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -151,7 +177,7 @@ js_element_get_property_childNodes(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -178,7 +204,7 @@ js_element_get_property_className(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -195,7 +221,7 @@ js_element_get_property_dir(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -216,7 +242,7 @@ js_element_get_property_firstChild(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -237,7 +263,7 @@ js_element_get_property_firstElementChild(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -267,7 +293,7 @@ js_element_get_property_id(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -284,7 +310,7 @@ js_element_get_property_lang(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -301,7 +327,7 @@ js_element_get_property_lastChild(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -321,7 +347,7 @@ js_element_get_property_lastElementChild(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -352,7 +378,7 @@ js_element_get_property_nextElementSibling(JSContext *ctx, JSValueConst this_val #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -381,7 +407,7 @@ js_element_get_property_nodeName(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Node *node = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Node *node = static_cast(js_getopaque(this_val, js_element_class_id)); xmlpp::ustring v; @@ -415,7 +441,7 @@ js_element_get_property_nodeType(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Node *node = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Node *node = static_cast(js_getopaque(this_val, js_element_class_id)); if (!node) { return JS_NULL; @@ -441,7 +467,7 @@ js_element_get_property_nodeValue(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Node *node = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Node *node = static_cast(js_getopaque(this_val, js_element_class_id)); if (!node) { return JS_NULL; @@ -487,7 +513,7 @@ js_element_get_property_nextSibling(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -520,7 +546,7 @@ js_element_get_property_parentElement(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -541,7 +567,7 @@ js_element_get_property_parentNode(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -561,7 +587,7 @@ js_element_get_property_previousElementSibling(JSContext *ctx, JSValueConst this #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -590,7 +616,7 @@ js_element_get_property_previousSibling(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -610,7 +636,7 @@ js_element_get_property_tagName(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -628,7 +654,7 @@ js_element_get_property_title(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -728,7 +754,7 @@ js_element_get_property_innerHtml(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -750,7 +776,7 @@ js_element_get_property_outerHtml(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -772,7 +798,7 @@ js_element_get_property_textContent(JSContext *ctx, JSValueConst this_val) #ifdef ECMASCRIPT_DEBUG fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -796,7 +822,7 @@ js_element_set_property_className(JSContext *ctx, JSValueConst this_val, JSValue #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); assert(interpreter); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -823,7 +849,7 @@ js_element_set_property_dir(JSContext *ctx, JSValueConst this_val, JSValue val) #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); assert(interpreter); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -853,7 +879,7 @@ js_element_set_property_id(JSContext *ctx, JSValueConst this_val, JSValue val) #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); assert(interpreter); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -879,7 +905,7 @@ js_element_set_property_innerHtml(JSContext *ctx, JSValueConst this_val, JSValue fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -926,7 +952,7 @@ js_element_set_property_innerText(JSContext *ctx, JSValueConst this_val, JSValue fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -958,7 +984,7 @@ js_element_set_property_lang(JSContext *ctx, JSValueConst this_val, JSValue val) fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -984,7 +1010,7 @@ js_element_set_property_title(JSContext *ctx, JSValueConst this_val, JSValue val fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -1037,6 +1063,107 @@ check_contains(xmlpp::Node *node, xmlpp::Node *searched, bool *result_set, bool } } +static JSValue +js_element_addEventListener(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct js_element_private *el_private = (struct js_element_private *)(JS_GetOpaque(this_val, js_element_class_id)); + + if (!el_private) { + return JS_NULL; + } + + if (argc < 2) { + return JS_UNDEFINED; + } + const char *str; + size_t len; + str = JS_ToCStringLen(ctx, &len, argv[0]); + + if (!str) { + return JS_EXCEPTION; + } + char *method = stracpy(str); + JS_FreeCString(ctx, str); + + if (!method) { + return JS_EXCEPTION; + } + + JSValue fun = argv[1]; + struct listener *l; + + foreach(l, el_private->listeners) { + if (strcmp(l->typ, method)) { + continue; + } + if (JS_VALUE_GET_PTR(l->fun) == JS_VALUE_GET_PTR(fun)) { + mem_free(method); + return JS_UNDEFINED; + } + } + struct listener *n = (struct listener *)mem_calloc(1, sizeof(*n)); + + if (n) { + n->typ = method; + n->fun = JS_DupValue(ctx, argv[1]); + add_to_list_end(el_private->listeners, n); + } + return JS_UNDEFINED; +} + +static JSValue +js_element_removeEventListener(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct js_element_private *el_private = (struct js_element_private *)(JS_GetOpaque(this_val, js_element_class_id)); + + if (!el_private) { + return JS_NULL; + } + + if (argc < 2) { + return JS_UNDEFINED; + } + const char *str; + size_t len; + str = JS_ToCStringLen(ctx, &len, argv[0]); + + if (!str) { + return JS_EXCEPTION; + } + char *method = stracpy(str); + JS_FreeCString(ctx, str); + + if (!method) { + return JS_EXCEPTION; + } + JSValue fun = argv[1]; + struct listener *l; + + foreach(l, el_private->listeners) { + if (strcmp(l->typ, method)) { + continue; + } + if (JS_VALUE_GET_PTR(l->fun) == JS_VALUE_GET_PTR(fun)) { + del_from_list(l); + mem_free_set(&l->typ, NULL); + mem_free(l); + mem_free(method); + return JS_UNDEFINED; + } + } + mem_free(method); + return JS_UNDEFINED; +} + + static JSValue js_element_appendChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1044,7 +1171,7 @@ js_element_appendChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueC fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); #endif struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (argc != 1) { return JS_NULL; @@ -1053,7 +1180,7 @@ js_element_appendChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueC if (!el) { return JS_NULL; } - xmlpp::Node *el2 = static_cast(JS_GetOpaque(argv[0], js_element_class_id)); + xmlpp::Node *el2 = static_cast(js_getopaque(argv[0], js_element_class_id)); el->import_node(el2); interpreter->changed = true; @@ -1073,7 +1200,7 @@ js_element_cloneNode(JSContext *ctx, JSValueConst this_val, int argc, JSValueCon return JS_NULL; } struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -1129,7 +1256,7 @@ js_element_closest(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst if (argc != 1) { return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_NULL; @@ -1184,12 +1311,12 @@ js_element_contains(JSContext *ctx, JSValueConst this_val, int argc, JSValueCons #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; } - xmlpp::Element *el2 = static_cast(JS_GetOpaque(argv[0], js_element_class_id)); + xmlpp::Element *el2 = static_cast(js_getopaque(argv[0], js_element_class_id)); if (!el2) { return JS_FALSE; @@ -1215,7 +1342,7 @@ js_element_getAttribute(JSContext *ctx, JSValueConst this_val, int argc, JSValue #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1251,7 +1378,7 @@ js_element_getAttributeNode(JSContext *ctx, JSValueConst this_val, int argc, JSV #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -1281,7 +1408,7 @@ js_element_hasAttribute(JSContext *ctx, JSValueConst this_val, int argc, JSValue #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1311,7 +1438,7 @@ js_element_hasAttributes(JSContext *ctx, JSValueConst this_val, int argc, JSValu #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1333,7 +1460,7 @@ js_element_hasChildNodes(JSContext *ctx, JSValueConst this_val, int argc, JSValu #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1356,7 +1483,7 @@ js_element_insertBefore(JSContext *ctx, JSValueConst this_val, int argc, JSValue return JS_UNDEFINED; } struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -1365,13 +1492,13 @@ js_element_insertBefore(JSContext *ctx, JSValueConst this_val, int argc, JSValue JSValue next_sibling1 = argv[1]; JSValue child1 = argv[0]; - xmlpp::Node *next_sibling = static_cast(JS_GetOpaque(next_sibling1, js_element_class_id)); + xmlpp::Node *next_sibling = static_cast(js_getopaque(next_sibling1, js_element_class_id)); if (!next_sibling) { return JS_NULL; } - xmlpp::Node *child = static_cast(JS_GetOpaque(child1, js_element_class_id)); + xmlpp::Node *child = static_cast(js_getopaque(child1, js_element_class_id)); auto node = xmlAddPrevSibling(next_sibling->cobj(), child->cobj()); auto res = el_add_child_element_common(child->cobj(), node); @@ -1392,14 +1519,14 @@ js_element_isEqualNode(JSContext *ctx, JSValueConst this_val, int argc, JSValueC #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; } JSValue node = argv[0]; - xmlpp::Element *el2 = static_cast(JS_GetOpaque(node, js_element_class_id)); + xmlpp::Element *el2 = static_cast(js_getopaque(node, js_element_class_id)); struct string first; struct string second; @@ -1435,13 +1562,13 @@ js_element_isSameNode(JSContext *ctx, JSValueConst this_val, int argc, JSValueCo #endif return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; } JSValue node = argv[0]; - xmlpp::Element *el2 = static_cast(JS_GetOpaque(node, js_element_class_id)); + xmlpp::Element *el2 = static_cast(js_getopaque(node, js_element_class_id)); return JS_NewBool(ctx, (el == el2)); } @@ -1455,7 +1582,7 @@ js_element_matches(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst if (argc != 1) { return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1498,7 +1625,7 @@ js_element_querySelector(JSContext *ctx, JSValueConst this_val, int argc, JSValu if (argc != 1) { return JS_UNDEFINED; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1542,7 +1669,7 @@ js_element_querySelectorAll(JSContext *ctx, JSValueConst this_val, int argc, JSV if (argc != 1) { return JS_FALSE; } - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_FALSE; @@ -1592,7 +1719,7 @@ js_element_remove(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst return JS_UNDEFINED; } struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -1617,7 +1744,7 @@ js_element_removeChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueC return JS_UNDEFINED; } struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el || !JS_IsObject(argv[0])) { return JS_NULL; @@ -1626,7 +1753,7 @@ js_element_removeChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueC auto children = el->get_children(); auto it = children.begin(); auto end = children.end(); - xmlpp::Element *el2 = static_cast(JS_GetOpaque(node, js_element_class_id)); + xmlpp::Element *el2 = static_cast(js_getopaque(node, js_element_class_id)); for (;it != end; ++it) { if (*it == el2) { @@ -1653,7 +1780,7 @@ js_element_setAttribute(JSContext *ctx, JSValueConst this_val, int argc, JSValue return JS_UNDEFINED; } struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); - xmlpp::Element *el = static_cast(JS_GetOpaque(this_val, js_element_class_id)); + xmlpp::Element *el = static_cast(js_getopaque(this_val, js_element_class_id)); if (!el) { return JS_UNDEFINED; @@ -1721,6 +1848,7 @@ static const JSCFunctionListEntry js_element_proto_funcs[] = { JS_CGETSET_DEF("tagName", js_element_get_property_tagName, nullptr), JS_CGETSET_DEF("textContent", js_element_get_property_textContent, nullptr), JS_CGETSET_DEF("title", js_element_get_property_title, js_element_set_property_title), + JS_CFUNC_DEF("addEventListener", 3, js_element_addEventListener), JS_CFUNC_DEF("appendChild", 1, js_element_appendChild), JS_CFUNC_DEF("cloneNode", 1, js_element_cloneNode), JS_CFUNC_DEF("closest", 1, js_element_closest), @@ -1738,6 +1866,7 @@ static const JSCFunctionListEntry js_element_proto_funcs[] = { JS_CFUNC_DEF("querySelectorAll",1, js_element_querySelectorAll), JS_CFUNC_DEF("remove", 0, js_element_remove), JS_CFUNC_DEF("removeChild",1, js_element_removeChild), + JS_CFUNC_DEF("removeEventListener", 3, js_element_removeEventListener), JS_CFUNC_DEF("setAttribute",2, js_element_setAttribute), JS_CFUNC_DEF("toString", 0, js_element_toString) @@ -1748,9 +1877,19 @@ static std::map map_elements; static void js_element_finalizer(JSRuntime *rt, JSValue val) { - void *node = JS_GetOpaque(val, js_element_class_id); + struct js_element_private *el_private = (struct js_element_private *)JS_GetOpaque(val, js_element_class_id); - map_elements.erase(node); + if (el_private) { + struct listener *l; + + foreach(l, el_private->listeners) { + mem_free_set(&l->typ, NULL); + } + free_list(el_private->listeners); + + map_elements.erase(el_private->node); + mem_free(el_private); + } } static JSClassDef js_element_class = { @@ -1808,6 +1947,7 @@ js_element_init(JSContext *ctx) return 0; } +static std::map map_privates; JSValue getElement(JSContext *ctx, void *node) @@ -1831,14 +1971,67 @@ getElement(JSContext *ctx, void *node) RETURN_JS(r); } + struct js_element_private *el_private = mem_calloc(1, sizeof(*el_private)); + + if (!el_private) { + return JS_NULL; + } + init_list(el_private->listeners); + el_private->node = node; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + el_private->interpreter = interpreter; + JSValue element_obj = JS_NewObjectClass(ctx, js_element_class_id); JS_SetPropertyFunctionList(ctx, element_obj, js_element_proto_funcs, countof(js_element_proto_funcs)); JS_SetClassProto(ctx, js_element_class_id, element_obj); - JS_SetOpaque(element_obj, node); + JS_SetOpaque(element_obj, el_private); map_elements[node] = element_obj; + map_privates[node] = el_private; JSValue rr = JS_DupValue(ctx, element_obj); + el_private->thisval = rr; RETURN_JS(rr); } + + +void +check_element_event(void *elem, const char *event_name, struct term_event *ev) +{ + JSObject *obj; + auto el = map_privates.find(elem); + + if (el == map_privates.end()) { + return; + } + struct js_element_private *el_private = el->second; + struct ecmascript_interpreter *interpreter = el_private->interpreter; + JSContext *ctx = (JSContext *)interpreter->backend_data; + interpreter->heartbeat = add_heartbeat(interpreter); + + struct listener *l; + + foreach(l, el_private->listeners) { + if (strcmp(l->typ, event_name)) { + continue; + } + if (ev && ev->ev == EVENT_KBD && (!strcmp(event_name, "keydown") || !strcmp(event_name, "keyup"))) { + JSValue func = JS_DupValue(ctx, l->fun); + JSValue arg = get_keyboardEvent(ctx, ev); + JSValue ret = JS_Call(ctx, func, el_private->thisval, 1, (JSValueConst *) &arg); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, arg); + } else { + JSValue func = JS_DupValue(ctx, l->fun); + JSValue arg = JS_UNDEFINED; + JSValue ret = JS_Call(ctx, func, el_private->thisval, 1, (JSValueConst *) &arg); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, arg); + } + } + done_heartbeat(interpreter->heartbeat); + check_for_rerender(interpreter, event_name); +} diff --git a/src/ecmascript/quickjs/element.h b/src/ecmascript/quickjs/element.h index 5ad3cd59..4f17b229 100644 --- a/src/ecmascript/quickjs/element.h +++ b/src/ecmascript/quickjs/element.h @@ -3,9 +3,12 @@ #include +struct term_event; + JSValue getElement(JSContext *ctx, void *node); int js_element_init(JSContext *ctx); void walk_tree(struct string *buf, void *nod, bool start = true, bool toSortAttrs = false); +void check_element_event(void *elem, const char *event_name, struct term_event *ev); #endif diff --git a/src/ecmascript/quickjs/keyboard.cpp b/src/ecmascript/quickjs/keyboard.cpp new file mode 100644 index 00000000..dbf85820 --- /dev/null +++ b/src/ecmascript/quickjs/keyboard.cpp @@ -0,0 +1,185 @@ +/* The QuickJS KeyboardEvent 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/quickjs.h" +#include "ecmascript/quickjs/heartbeat.h" +#include "ecmascript/quickjs/keyboard.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 + +#define countof(x) (sizeof(x) / sizeof((x)[0])) + +static JSClassID js_keyboardEvent_class_id; + +static JSValue js_keyboardEvent_get_property_key(JSContext *ctx, JSValueConst this_val); +static JSValue js_keyboardEvent_get_property_keyCode(JSContext *ctx, JSValueConst this_val); + +static unicode_val_T keyCode; + +struct keyboard { + unicode_val_T keyCode; +}; + +static +void js_keyboardEvent_finalizer(JSRuntime *rt, JSValue val) +{ + struct keyboard *keyb = JS_GetOpaque(val, js_keyboardEvent_class_id); + + if (keyb) { + mem_free(keyb); + } +} + +static JSClassDef js_keyboardEvent_class = { + "KeyboardEvent", + js_keyboardEvent_finalizer +}; + +static JSValue +js_keyboardEvent_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + JSValue obj = JS_UNDEFINED; + JSValue proto; + + struct keyboard *keyb = (struct keyboard *)mem_calloc(1, sizeof(*keyb)); + + if (!keyb) { + return JS_EXCEPTION; + } + + /* using new_target to get the prototype is necessary when the + class is extended. */ + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + + if (JS_IsException(proto)) { + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, proto, js_keyboardEvent_class_id); + JS_FreeValue(ctx, proto); + + if (JS_IsException(obj)) { + goto fail; + } + keyb->keyCode = keyCode; + JS_SetOpaque(obj, keyb); + + RETURN_JS(obj); + +fail: + JS_FreeValue(ctx, obj); + mem_free(keyb); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_keyboardEvent_proto_funcs[] = { + JS_CGETSET_DEF("key", js_keyboardEvent_get_property_key, nullptr), + JS_CGETSET_DEF("keyCode", js_keyboardEvent_get_property_keyCode, nullptr) +}; + +static JSValue +js_keyboardEvent_get_property_key(JSContext *ctx, JSValueConst this_val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct keyboard *keyb = static_cast(JS_GetOpaque(this_val, js_keyboardEvent_class_id)); + + if (!keyb) { + return JS_NULL; + } + char text[8] = {0}; + + *text = keyb->keyCode; + JSValue r = JS_NewString(ctx, text); + + RETURN_JS(r); +} + +static JSValue +js_keyboardEvent_get_property_keyCode(JSContext *ctx, JSValueConst this_val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct keyboard *keyb = static_cast(JS_GetOpaque(this_val, js_keyboardEvent_class_id)); + + if (!keyb) { + return JS_NULL; + } + return JS_NewUint32(ctx, keyb->keyCode); +} + +JSValue +get_keyboardEvent(JSContext *ctx, struct term_event *ev) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + static int initialized; + /* create the element class */ + if (!initialized) { + JS_NewClassID(&js_keyboardEvent_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_keyboardEvent_class_id, &js_keyboardEvent_class); + initialized = 1; + } + struct keyboard *keyb = (struct keyboard *)mem_calloc(1, sizeof(*keyb)); + + if (!keyb) { + return JS_NULL; + } + keyCode = keyb->keyCode = get_kbd_key(ev); + JSValue keyb_obj = JS_NewObjectClass(ctx, js_keyboardEvent_class_id); + JS_SetPropertyFunctionList(ctx, keyb_obj, js_keyboardEvent_proto_funcs, countof(js_keyboardEvent_proto_funcs)); + JS_SetClassProto(ctx, js_keyboardEvent_class_id, keyb_obj); + JS_SetOpaque(keyb_obj, keyb); + + JSValue rr = JS_DupValue(ctx, keyb_obj); + RETURN_JS(rr); +} diff --git a/src/ecmascript/quickjs/keyboard.h b/src/ecmascript/quickjs/keyboard.h new file mode 100644 index 00000000..bdaaaf13 --- /dev/null +++ b/src/ecmascript/quickjs/keyboard.h @@ -0,0 +1,10 @@ +#ifndef EL__ECMASCRIPT_QUICKJS_KEYBOARD_H +#define EL__ECMASCRIPT_QUICKJS_KEYBOARD_H + +#include + +struct term_event; + +JSValue get_keyboardEvent(JSContext *ctx, struct term_event *ev); + +#endif diff --git a/src/ecmascript/quickjs/meson.build b/src/ecmascript/quickjs/meson.build index 227d6f74..d7352bd6 100644 --- a/src/ecmascript/quickjs/meson.build +++ b/src/ecmascript/quickjs/meson.build @@ -1,2 +1,2 @@ srcs += files('attr.cpp', 'attributes.cpp', 'collection.cpp', 'console.cpp', 'document.cpp', 'element.cpp', 'form.cpp', 'forms.cpp', 'heartbeat.cpp', 'history.cpp', 'implementation.cpp', -'input.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', 'navigator.cpp', 'nodelist.cpp', 'screen.cpp', 'unibar.cpp', 'window.cpp', 'xhr.cpp') diff --git a/src/viewer/text/link.cpp b/src/viewer/text/link.cpp index 4e6aa493..ab265930 100644 --- a/src/viewer/text/link.cpp +++ b/src/viewer/text/link.cpp @@ -23,6 +23,13 @@ #ifdef CONFIG_ECMASCRIPT_SMJS #include "ecmascript/spidermonkey/element.h" +#endif + +#ifdef CONFIG_QUICKJS +#include "ecmascript/quickjs/element.h" +#endif + +#ifdef CONFIG_ECMASCRIPT #include #include #endif @@ -68,7 +75,7 @@ current_link_evhook(struct document_view *doc_view, enum script_event_hook_type if (!link) return -1; if (!doc_view->vs->ecmascript) return -1; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) std::map *mapa = (std::map *)doc_view->document->element_map; if (mapa) { diff --git a/src/viewer/text/view.cpp b/src/viewer/text/view.cpp index b372a70a..d0098caf 100644 --- a/src/viewer/text/view.cpp +++ b/src/viewer/text/view.cpp @@ -27,11 +27,20 @@ #include "document/options.h" #include "document/renderer.h" #include "document/view.h" + #ifdef CONFIG_ECMASCRIPT_SMJS #include "ecmascript/spidermonkey/element.h" +#endif + +#ifdef CONFIG_QUICKJS +#include "ecmascript/quickjs/element.h" +#endif + +#ifdef CONFIG_ECMASCRIPT #include #include #endif + #include "intl/charsets.h" #include "intl/libintl.h" #include "main/event.h" @@ -1289,7 +1298,7 @@ try_form_action(struct session *ses, struct document_view *doc_view, if (!link_is_textinput(link)) return FRAME_EVENT_IGNORED; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) if (ses->insert_mode == INSERT_MODE_ON) { std::map *mapa = (std::map *)doc_view->document->element_map;