From 68a7a2641847d120e5c9d3a17729ddb824645d02 Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Mon, 22 Jul 2024 22:01:40 +0200 Subject: [PATCH] [quickjs] Copy of collection.c as collection2.c They differ in finalizer. collection2 is for getElementsByClassName --- src/ecmascript/ecmascript-c.c | 23 ++ src/ecmascript/ecmascript-c.h | 21 ++ src/ecmascript/quickjs/Makefile | 2 +- src/ecmascript/quickjs/collection.h | 1 + src/ecmascript/quickjs/collection2.c | 361 +++++++++++++++++++++++++++ src/ecmascript/quickjs/document.c | 4 +- src/ecmascript/quickjs/meson.build | 3 +- 7 files changed, 411 insertions(+), 4 deletions(-) create mode 100644 src/ecmascript/quickjs/collection2.c diff --git a/src/ecmascript/ecmascript-c.c b/src/ecmascript/ecmascript-c.c index 1bbea650e..00d30bd22 100644 --- a/src/ecmascript/ecmascript-c.c +++ b/src/ecmascript/ecmascript-c.c @@ -672,6 +672,28 @@ prepare_strings(const char *text) return list; } +void +free_el_dom_collection(void *ctx) +{ + if (!ctx) { + return; + } + LIST_OF(struct class_string) *list = (LIST_OF(struct class_string) *)ctx; + struct class_string *st; + + foreach (st, *list) { + lwc_string *ls = (lwc_string *)st->name; + + if (ls) { + lwc_string_unref(ls); + } + } + free_list(*list); + mem_free(list); +} + + +#if 0 typedef bool (*dom_callback_is_in_collection)(struct dom_node *node, void *ctx); /** @@ -690,6 +712,7 @@ struct el_dom_html_collection { dom_node *root; /**< The root node of this collection */ uint32_t refcnt; /**< Reference counting */ }; +#endif /** * Intialiase a dom_html_collection diff --git a/src/ecmascript/ecmascript-c.h b/src/ecmascript/ecmascript-c.h index 5088cb843..eac2ddbe0 100644 --- a/src/ecmascript/ecmascript-c.h +++ b/src/ecmascript/ecmascript-c.h @@ -35,6 +35,25 @@ struct class_string { void *name; }; +typedef bool (*dom_callback_is_in_collection)(struct dom_node *node, void *ctx); + +/** + * The html_collection structure + */ +struct el_dom_html_collection { + dom_callback_is_in_collection ic; + /**< The function pointer used to test + * whether some node is an element of + * this collection + */ + void *ctx; /**< Context for the callback */ + struct dom_html_document *doc; /**< The document created this + * collection + */ + dom_node *root; /**< The root node of this collection */ + uint32_t refcnt; /**< Reference counting */ +}; + int ecmascript_get_interpreter_count(void); void ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter); void toggle_ecmascript(struct session *ses); @@ -60,6 +79,8 @@ void *get_elements_by_class_name(dom_html_document *doc, dom_node *node, const c void camel_to_html(struct string *result, const char *camel); void ecmascript_walk_tree(struct string *buf, void *nod, bool start, bool toSortAttrs); +void free_el_dom_collection(void *ctx); + extern struct module ecmascript_module; #ifdef __cplusplus diff --git a/src/ecmascript/quickjs/Makefile b/src/ecmascript/quickjs/Makefile index b284cc902..d53cc1494 100644 --- a/src/ecmascript/quickjs/Makefile +++ b/src/ecmascript/quickjs/Makefile @@ -1,7 +1,7 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config -OBJS = attr.o attributes.o collection.o console.o css.o customevent.o dataset.o document.o domparser.o domrect.o element.o event.o form.o forms.o heartbeat.o history.o implementation.o input.o \ +OBJS = attr.o attributes.o collection.o collection2.o console.o css.o customevent.o dataset.o document.o domparser.o domrect.o element.o event.o form.o forms.o heartbeat.o history.o implementation.o input.o \ keyboard.o localstorage.o location.o mapa.o message.o navigator.o nodelist.o nodelist2.o screen.o style.o tokenlist.o unibar.o url.o urlsearchparams.o window.o xhr.o include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/quickjs/collection.h b/src/ecmascript/quickjs/collection.h index 453964299..0c7048361 100644 --- a/src/ecmascript/quickjs/collection.h +++ b/src/ecmascript/quickjs/collection.h @@ -8,6 +8,7 @@ extern "C" { #endif JSValue getCollection(JSContext *ctx, void *node); +JSValue getCollection2(JSContext *ctx, void *node); #ifdef __cplusplus } diff --git a/src/ecmascript/quickjs/collection2.c b/src/ecmascript/quickjs/collection2.c new file mode 100644 index 000000000..f72d6d47c --- /dev/null +++ b/src/ecmascript/quickjs/collection2.c @@ -0,0 +1,361 @@ +/* The QuickJS html collection2 object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifdef CONFIG_LIBDOM +#include +#include +#endif + +#include "elinks.h" + +#include "document/libdom/corestrings.h" +#include "ecmascript/ecmascript-c.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/quickjs/mapa.h" +#include "ecmascript/quickjs.h" +#include "ecmascript/quickjs/collection.h" +#include "ecmascript/quickjs/element.h" + +#define countof(x) (sizeof(x) / sizeof((x)[0])) + +void *map_collections2; +void *map_rev_collections2; + +JSClassID js_htmlCollection2_class_id; + +static void * +js_htmlCollection2_GetOpaque(JSValueConst this_val) +{ + REF_JS(this_val); + + return JS_GetOpaque(this_val, js_htmlCollection2_class_id); + +// return attr_find_in_map_rev(map_rev_collections, this_val); +} + +static void +js_htmlCollection2_SetOpaque(JSValueConst this_val, void *node) +{ + REF_JS(this_val); + + JS_SetOpaque(this_val, node); +#if 0 + if (!node) { + attr_erase_from_map_rev(map_rev_collections, this_val); + } else { + attr_save_in_map_rev(map_rev_collections, this_val, node); + } +#endif +} + +static +void js_htmlColection2_finalizer(JSRuntime *rt, JSValue val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(val); + struct el_dom_html_collection *ns = (struct el_dom_html_collection *)(js_htmlCollection2_GetOpaque(val)); + + if (ns) { + //attr_erase_from_map_str(map_collections, ns); + if (ns->refcnt > 0) { + free_el_dom_collection(ns->ctx); + ns->ctx = NULL; + dom_html_collection_unref(ns); + } + } +} + +static JSValue +js_htmlCollection2_get_property_length(JSContext *ctx, JSValueConst this_val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct el_dom_html_collection *ns = (struct el_dom_html_collection *)(js_htmlCollection2_GetOpaque(this_val)); + uint32_t size; + + if (!ns) { + return JS_NewInt32(ctx, 0); + } + dom_html_collection_ref(ns); + + if (dom_html_collection_get_length(ns, &size) != DOM_NO_ERR) { + dom_html_collection_unref(ns); + return JS_NewInt32(ctx, 0); + } + dom_html_collection_unref(ns); + + return JS_NewInt32(ctx, size); +} + +static JSValue +js_htmlCollection2_item2(JSContext *ctx, JSValueConst this_val, int idx) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct el_dom_html_collection *ns = (struct el_dom_html_collection *)(js_htmlCollection2_GetOpaque(this_val)); + dom_node *node; + dom_exception err; + JSValue ret; + + if (!ns) { + return JS_UNDEFINED; + } + dom_html_collection_ref(ns); + err = dom_html_collection_item(ns, idx, &node); + + if (err != DOM_NO_ERR) { + dom_html_collection_unref(ns); + return JS_UNDEFINED; + } + ret = getElement(ctx, node); + dom_node_unref(node); + dom_html_collection_unref(ns); + + return ret; +} + +static JSValue +js_htmlCollection2_item(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + + if (argc != 1) { + return JS_UNDEFINED; + } + + int index; + JS_ToInt32(ctx, &index, argv[0]); + + return js_htmlCollection2_item2(ctx, this_val, index); +} + +static JSValue +js_htmlCollection2_namedItem2(JSContext *ctx, JSValueConst this_val, const char *str) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct el_dom_html_collection *ns = (struct el_dom_html_collection *)(js_htmlCollection2_GetOpaque(this_val)); + dom_exception err; + dom_string *name; + uint32_t size, i; + + if (!ns) { + return JS_UNDEFINED; + } + dom_html_collection_ref(ns); + + if (dom_html_collection_get_length(ns, &size) != DOM_NO_ERR) { + dom_html_collection_unref(ns); + return JS_UNDEFINED; + } + err = dom_string_create((const uint8_t*)str, strlen(str), &name); + + if (err != DOM_NO_ERR) { + dom_html_collection_unref(ns); + return JS_UNDEFINED; + } + + for (i = 0; i < size; i++) { + dom_node *element = NULL; + dom_string *val = NULL; + + err = dom_html_collection_item(ns, i, &element); + + if (err != DOM_NO_ERR || !element) { + continue; + } + err = dom_element_get_attribute(element, corestring_dom_id, &val); + + if (err == DOM_NO_ERR && val) { + if (dom_string_caseless_isequal(name, val)) { + JSValue ret = getElement(ctx, element); + dom_string_unref(val); + dom_string_unref(name); + dom_node_unref(element); + dom_html_collection_unref(ns); + + return ret; + } + dom_string_unref(val); + } + err = dom_element_get_attribute(element, corestring_dom_name, &val); + + if (err == DOM_NO_ERR && val) { + if (dom_string_caseless_isequal(name, val)) { + JSValue ret = getElement(ctx, element); + dom_string_unref(val); + dom_string_unref(name); + dom_node_unref(element); + dom_html_collection_unref(ns); + + return ret; + } + dom_string_unref(val); + } + dom_node_unref(element); + } + dom_string_unref(name); + dom_html_collection_unref(ns); + + return JS_UNDEFINED; +} + +static JSValue +js_htmlCollection2_namedItem(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + + if (argc != 1) { + return JS_UNDEFINED; + } + + const char *str; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + + if (!str) { + return JS_EXCEPTION; + } + + JSValue ret = js_htmlCollection2_namedItem2(ctx, this_val, str); + JS_FreeCString(ctx, str); + + RETURN_JS(ret); +} + +static void +js_htmlCollection2_set_items(JSContext *ctx, JSValue this_val, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + + int counter = 0; + uint32_t size, i; + struct el_dom_html_collection *ns = (struct el_dom_html_collection *)(js_htmlCollection2_GetOpaque(this_val)); + dom_exception err; + + if (!ns) { + return; + } + dom_html_collection_ref(ns); + + if (dom_html_collection_get_length(ns, &size) != DOM_NO_ERR) { + dom_html_collection_unref(ns); + return; + } + + for (i = 0; i < size; i++) { + dom_node *element = NULL; + dom_string *name = NULL; + err = dom_html_collection_item(ns, i, &element); + + if (err != DOM_NO_ERR || !element) { + continue; + } + JSValue obj = getElement(ctx, element); + + REF_JS(obj); + JS_SetPropertyUint32(ctx, this_val, counter, JS_DupValue(ctx, obj)); + err = dom_element_get_attribute(element, corestring_dom_id, &name); + + if (err != DOM_NO_ERR || !name) { + err = dom_element_get_attribute(element, corestring_dom_name, &name); + } + + if (err == DOM_NO_ERR && name) { + if (!dom_string_caseless_lwc_isequal(name, corestring_lwc_item) && !dom_string_caseless_lwc_isequal(name, corestring_lwc_nameditem)) { + JS_DefinePropertyValueStr(ctx, this_val, dom_string_data(name), JS_DupValue(ctx, obj), 0); + } + } + JS_FreeValue(ctx, obj); + counter++; + if (name) { + dom_string_unref(name); + } + dom_node_unref(element); + } + dom_html_collection_unref(ns); +} + +static JSValue +js_htmlCollection2_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + + return JS_NewString(ctx, "[htmlCollection2 object]"); +} + +static const JSCFunctionListEntry js_htmlCollection2_proto_funcs[] = { + JS_CGETSET_DEF("length", js_htmlCollection2_get_property_length, NULL), + JS_CFUNC_DEF("item", 1, js_htmlCollection2_item), + JS_CFUNC_DEF("namedItem", 1, js_htmlCollection2_namedItem), + JS_CFUNC_DEF("toString", 0, js_htmlCollection2_toString) +}; + +static JSClassDef js_htmlCollection2_class = { + "htmlCollection2", + .finalizer = js_htmlColection2_finalizer +}; + +JSValue +getCollection2(JSContext *ctx, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + static int initialized; +// JSValue second; + + if (!initialized) { + /* collection class */ + JS_NewClassID(&js_htmlCollection2_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_htmlCollection2_class_id, &js_htmlCollection2_class); + initialized = 1; + } +// second = attr_find_in_map(map_collections, node); + +// if (!JS_IsNull(second)) { +// JSValue r = JS_DupValue(ctx, second); +// +// RETURN_JS(r); +// } + JSValue proto = JS_NewObjectClass(ctx, js_htmlCollection2_class_id); + REF_JS(proto); + + JS_SetPropertyFunctionList(ctx, proto, js_htmlCollection2_proto_funcs, countof(js_htmlCollection2_proto_funcs)); + JS_SetClassProto(ctx, js_htmlCollection2_class_id, proto); + + js_htmlCollection2_SetOpaque(proto, node); +// js_htmlCollection2_set_items(ctx, proto, node); +// attr_save_in_map(map_collections, node, proto); + JSValue rr = JS_DupValue(ctx, proto); + + RETURN_JS(rr); +} diff --git a/src/ecmascript/quickjs/document.c b/src/ecmascript/quickjs/document.c index 9dddad1b7..9982c85f3 100644 --- a/src/ecmascript/quickjs/document.c +++ b/src/ecmascript/quickjs/document.c @@ -1465,7 +1465,7 @@ js_document_getElementsByClassName(JSContext *ctx, JSValueConst this_val, int ar return JS_NULL; } const char *classes = JS_ToCString(ctx, argv[0]); - dom_html_collection *col = get_elements_by_class_name(doc, (dom_node *)doc, classes); + struct el_dom_html_collection *col = get_elements_by_class_name(doc, (dom_node *)doc, classes); if (classes) { JS_FreeCString(ctx, classes); @@ -1473,7 +1473,7 @@ js_document_getElementsByClassName(JSContext *ctx, JSValueConst this_val, int ar if (!col) { return JS_NULL; } - JSValue ret = getCollection(ctx, col); + JSValue ret = getCollection2(ctx, col); RETURN_JS(ret); } diff --git a/src/ecmascript/quickjs/meson.build b/src/ecmascript/quickjs/meson.build index 28ccc98fc..f1024e085 100644 --- a/src/ecmascript/quickjs/meson.build +++ b/src/ecmascript/quickjs/meson.build @@ -1,3 +1,4 @@ -srcs += files('attr.c', 'attributes.c', 'collection.c', 'console.c', 'css.c', 'customevent.c', 'dataset.c', 'document.c', 'domparser.c', 'domrect.c', 'element.c', 'event.c', 'form.c', 'forms.c', 'heartbeat.c', 'history.c', +srcs += files('attr.c', 'attributes.c', 'collection.c', 'collection2.c', 'console.c', 'css.c', 'customevent.c', 'dataset.c', 'document.c', 'domparser.c', 'domrect.c', +'element.c', 'event.c', 'form.c', 'forms.c', 'heartbeat.c', 'history.c', 'implementation.c', 'input.c', 'keyboard.c', 'localstorage.c', 'location.c', 'mapa.c', 'message.c', 'navigator.c', 'nodelist.c', 'nodelist2.c', 'screen.c', 'style.c', 'tokenlist.c', 'unibar.c', 'url.c', 'urlsearchparams.c', 'window.c', 'xhr.c')