From 42a5152cc478944610b6abdecf1f136af10d90ab Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Fri, 14 Jun 2024 21:24:14 +0200 Subject: [PATCH] [quickjs] URLSearchParams --- src/ecmascript/quickjs.c | 2 + src/ecmascript/quickjs/Makefile | 2 +- src/ecmascript/quickjs/meson.build | 2 +- src/ecmascript/quickjs/urlsearchparams.c | 497 ++++++++++++++++++++ src/ecmascript/quickjs/urlsearchparams.h | 16 + test/ecmascript/assert/URLSearchParams.html | 8 +- 6 files changed, 522 insertions(+), 5 deletions(-) create mode 100644 src/ecmascript/quickjs/urlsearchparams.c create mode 100644 src/ecmascript/quickjs/urlsearchparams.h diff --git a/src/ecmascript/quickjs.c b/src/ecmascript/quickjs.c index 42aa3252a..5c0f8bb0d 100644 --- a/src/ecmascript/quickjs.c +++ b/src/ecmascript/quickjs.c @@ -41,6 +41,7 @@ #include "ecmascript/quickjs/screen.h" #include "ecmascript/quickjs/unibar.h" #include "ecmascript/quickjs/url.h" +#include "ecmascript/quickjs/urlsearchparams.h" #include "ecmascript/quickjs/window.h" #include "ecmascript/quickjs/xhr.h" #include "intl/libintl.h" @@ -187,6 +188,7 @@ quickjs_get_interpreter(struct ecmascript_interpreter *interpreter) js_messageEvent_init(ctx); js_customEvent_init(ctx); js_url_init(ctx); + js_urlSearchParams_init(ctx); interpreter->document_obj = getDocument(ctx, document->dom); diff --git a/src/ecmascript/quickjs/Makefile b/src/ecmascript/quickjs/Makefile index 8d7652936..6729fe893 100644 --- a/src/ecmascript/quickjs/Makefile +++ b/src/ecmascript/quickjs/Makefile @@ -2,6 +2,6 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config OBJS = attr.o attributes.o collection.o console.o css.o customevent.o document.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 screen.o style.o unibar.o url.o window.o xhr.o + keyboard.o localstorage.o location.o mapa.o message.o navigator.o nodelist.o screen.o style.o unibar.o url.o urlsearchparams.o window.o xhr.o include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/quickjs/meson.build b/src/ecmascript/quickjs/meson.build index dca2bd353..3dc12df9f 100644 --- a/src/ecmascript/quickjs/meson.build +++ b/src/ecmascript/quickjs/meson.build @@ -1,3 +1,3 @@ srcs += files('attr.c', 'attributes.c', 'collection.c', 'console.c', 'css.c', 'customevent.c', 'document.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', 'screen.c', 'style.c', 'unibar.c', 'url.c', 'window.c', 'xhr.c') +'mapa.c', 'message.c', 'navigator.c', 'nodelist.c', 'screen.c', 'style.c', 'unibar.c', 'url.c', 'urlsearchparams.c', 'window.c', 'xhr.c') diff --git a/src/ecmascript/quickjs/urlsearchparams.c b/src/ecmascript/quickjs/urlsearchparams.c new file mode 100644 index 000000000..6a81c91cf --- /dev/null +++ b/src/ecmascript/quickjs/urlsearchparams.c @@ -0,0 +1,497 @@ +/* The QuickJS URLSearchParams 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/urlsearchparams.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/qs_parse/qs_parse.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" + +#define countof(x) (sizeof(x) / sizeof((x)[0])) + +static JSClassID js_urlSearchParams_class_id; + +struct eljs_urlSearchParams { + JSValue map; +}; + +static +void js_urlSearchParams_finalizer(JSRuntime *rt, JSValue val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(val); + + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)JS_GetOpaque(val, js_urlSearchParams_class_id); + + if (u) { + JS_FreeValueRT(rt, u->map); + mem_free(u); + } +} + +static void +js_urlSearchParams_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(val); + + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)JS_GetOpaque(val, js_urlSearchParams_class_id); + + if (u) { + JS_MarkValue(rt, u->map, mark_func); + } +} + +static void +parse_text(JSContext *ctx, JSValue map, char *str) +{ + if (!str || !*str) { + return; + } + + char *kvpairs[1024]; + int i = qs_parse(str, kvpairs, 1024); + int j; + + for (j = 0; j < i; j++) { + char *key = kvpairs[j]; + char *value = strchr(key, '='); + if (value) { + *value++ = '\0'; + } + JSValue argv[2]; + + argv[0] = JS_NewString(ctx, key); + argv[1] = JS_NewString(ctx, value); + JSValue set = JS_GetPropertyStr(ctx, map, "set"); + JSValue ret = JS_Call(ctx, set, map, 2, argv); + JS_FreeValue(ctx, set); + JS_FreeValue(ctx, argv[0]); + JS_FreeValue(ctx, argv[1]); + JS_FreeValue(ctx, map); + } +} + +static JSValue +js_urlSearchParams_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(new_target); + + JSValue obj = JS_NewObjectClass(ctx, js_urlSearchParams_class_id); + REF_JS(obj); + + if (JS_IsException(obj)) { + return obj; + } + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)mem_calloc(1, sizeof(*u)); + + if (!u) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + + if (argc > 0) { + const char *str; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + + if (!str) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + + char *urlstring = memacpy(str, len); + JS_FreeCString(ctx, str); + + if (!urlstring) { + return JS_EXCEPTION; + } + u->map = JS_Eval(ctx, "new Map();", strlen("new Map();"), "", JS_EVAL_TYPE_GLOBAL); + parse_text(ctx, u->map, urlstring); + mem_free(urlstring); + } + JS_SetOpaque(obj, u); + + return obj; +} + +static void +JS_NewGlobalCConstructor2(JSContext *ctx, JSValue func_obj, const char *name, JSValueConst proto) +{ + REF_JS(func_obj); + REF_JS(proto); + + JSValue global_object = JS_GetGlobalObject(ctx); + + JS_DefinePropertyValueStr(ctx, global_object, name, + JS_DupValue(ctx, func_obj), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_SetConstructor(ctx, func_obj, proto); + JS_FreeValue(ctx, func_obj); + JS_FreeValue(ctx, global_object); +} + +static JSValueConst +JS_NewGlobalCConstructor(JSContext *ctx, const char *name, JSCFunction *func, int length, JSValueConst proto) +{ + JSValue func_obj; + func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0); + REF_JS(func_obj); + REF_JS(proto); + + JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); + + return func_obj; +} + + +static JSValue +js_urlSearchParams_get_property_size(JSContext *ctx, JSValueConst this_val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + JSValue ret = JS_GetPropertyStr(ctx, u->map, "size"); + + RETURN_JS(ret); +} + + +static JSValue +js_urlSearchParams_delete(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + JSValue del = JS_GetPropertyStr(ctx, u->map, "delete"); + JS_Call(ctx, del, u->map, argc, argv); + JS_FreeValue(ctx, del); + + return JS_UNDEFINED; +} + +static JSValue +js_urlSearchParams_entries(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + JSValue entries = JS_GetPropertyStr(ctx, u->map, "entries"); + + RETURN_JS(entries); +} + +static JSValue +js_urlSearchParams_forEach(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + + if (argc < 1) { + return JS_UNDEFINED; + } + JSValue forEach = JS_GetPropertyStr(ctx, u->map, "forEach"); + JS_Call(ctx, forEach, u->map, argc, argv); + JS_FreeValue(ctx, forEach); + + return JS_UNDEFINED; +} + +static JSValue +js_urlSearchParams_get(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + + if (argc == 1) { + JSValue get = JS_GetPropertyStr(ctx, u->map, "get"); + JSValue ret = JS_Call(ctx, get, u->map, argc, argv); + JS_FreeValue(ctx, get); + + RETURN_JS(ret); + } + + return JS_UNDEFINED; +} + +static JSValue +js_urlSearchParams_has(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + + if (argc == 1) { + JSValue has = JS_GetPropertyStr(ctx, u->map, "has"); + JSValue ret = JS_Call(ctx, has, u->map, argc, argv); + JS_FreeValue(ctx, has); + + RETURN_JS(ret); + } + + if (argc > 1) { + JSValue get = JS_GetPropertyStr(ctx, u->map, "get"); + JSValue ret = JS_Call(ctx, get, u->map, argc, argv); + JS_FreeValue(ctx, get); + + const char *ret1 = JS_ToCString(ctx, ret); + const char *val = JS_ToCString(ctx, argv[1]); + + JS_FreeValue(ctx, ret); + + if (ret1 && val) { + bool r = !strcmp(ret1, val); + + JS_FreeCString(ctx, ret1); + JS_FreeCString(ctx, val); + + return JS_NewBool(ctx, r); + } + } + + return JS_FALSE; +} + +static JSValue +js_urlSearchParams_keys(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + JSValue keys = JS_GetPropertyStr(ctx, u->map, "keys"); + + RETURN_JS(keys); +} + +static JSValue +js_urlSearchParams_set(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + + if (argc < 1) { + return JS_UNDEFINED; + } + JSValue set = JS_GetPropertyStr(ctx, u->map, "set"); + JS_Call(ctx, set, u->map, argc, argv); + JS_FreeValue(ctx, set); + + return JS_UNDEFINED; +} + +static struct string result; +static char *prepend; + +static JSValue +map_foreach_callback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ + const char *val = JS_ToCString(ctx, argv[0]); + const char *key = JS_ToCString(ctx, argv[1]); + + add_to_string(&result, prepend); + + if (key) { + add_to_string(&result, key); + add_char_to_string(&result, '='); + JS_FreeCString(ctx, key); + } + + if (val) { + add_to_string(&result, val); + JS_FreeCString(ctx, val); + } + prepend = "&"; + + return JS_UNDEFINED; +} + +static JSValue +js_urlSearchParams_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); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + +// return JS_NewString(ctx, "aaa"); + + if (!init_string(&result)) { + return JS_EXCEPTION; + } + prepend = ""; + JSValue fun = JS_NewCFunction(ctx, map_foreach_callback, "f", 3); + JSValue forEach = JS_GetPropertyStr(ctx, u->map, "forEach"); + JS_Call(ctx, forEach, u->map, 2, &fun); + JS_FreeValue(ctx, forEach); + JS_FreeValue(ctx, fun); + + JSValue ret = JS_NewStringLen(ctx, result.source, result.length); + done_string(&result); + + RETURN_JS(ret); +} + +static JSValue +js_urlSearchParams_values(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + REF_JS(this_val); + struct eljs_urlSearchParams *u = (struct eljs_urlSearchParams *)(JS_GetOpaque(this_val, js_urlSearchParams_class_id)); + + if (!u) { + return JS_NULL; + } + JSValue values = JS_GetPropertyStr(ctx, u->map, "values"); + + RETURN_JS(values); +} + +static JSClassDef js_urlSearchParams_class = { + "URLSearchParams", + .finalizer = js_urlSearchParams_finalizer, + .gc_mark = js_urlSearchParams_mark +}; + +static const JSCFunctionListEntry js_urlSearchParams_proto_funcs[] = { + JS_CGETSET_DEF("size", js_urlSearchParams_get_property_size, NULL), + JS_CFUNC_DEF("delete", 1, js_urlSearchParams_delete ), + JS_CFUNC_DEF("entries", 0, js_urlSearchParams_entries ), + JS_CFUNC_DEF("forEach", 2, js_urlSearchParams_forEach ), + JS_CFUNC_DEF("get", 1, js_urlSearchParams_get ), + JS_CFUNC_DEF("has", 2, js_urlSearchParams_has ), + JS_CFUNC_DEF("keys", 0, js_urlSearchParams_keys ), + JS_CFUNC_DEF("set", 2, js_urlSearchParams_set ), + JS_CFUNC_DEF("toString", 0, js_urlSearchParams_toString ), + JS_CFUNC_DEF("values", 0, js_urlSearchParams_values ), +}; + +int +js_urlSearchParams_init(JSContext *ctx) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JSValue proto, obj; + + /* urlSearchParams class */ + JS_NewClassID(&js_urlSearchParams_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_urlSearchParams_class_id, &js_urlSearchParams_class); + proto = JS_NewObject(ctx); + REF_JS(proto); + + JS_SetPropertyFunctionList(ctx, proto, js_urlSearchParams_proto_funcs, countof(js_urlSearchParams_proto_funcs)); + JS_SetClassProto(ctx, js_urlSearchParams_class_id, proto); + + /* url object */ + obj = JS_NewGlobalCConstructor(ctx, "URLSearchParams", js_urlSearchParams_constructor, 1, proto); + REF_JS(obj); + + return 0; +} diff --git a/src/ecmascript/quickjs/urlsearchparams.h b/src/ecmascript/quickjs/urlsearchparams.h new file mode 100644 index 000000000..1be14ddc9 --- /dev/null +++ b/src/ecmascript/quickjs/urlsearchparams.h @@ -0,0 +1,16 @@ +#ifndef EL__ECMASCRIPT_QUICKJS_URLSEARCHPARAMS_H +#define EL__ECMASCRIPT_QUICKJS_URLSEARCHPARAMS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int js_urlSearchParams_init(JSContext *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/ecmascript/assert/URLSearchParams.html b/test/ecmascript/assert/URLSearchParams.html index 3b12258f4..023cda881 100644 --- a/test/ecmascript/assert/URLSearchParams.html +++ b/test/ecmascript/assert/URLSearchParams.html @@ -1,10 +1,12 @@