From fe5d5bcf68a89684a75b0cd00cab1160dc764e0e Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Fri, 28 Jun 2024 16:42:11 +0200 Subject: [PATCH] [spidermonkey] element.dataset (slow) --- src/ecmascript/spidermonkey/Makefile | 2 +- src/ecmascript/spidermonkey/dataset.cpp | 261 ++++++++++++++++++++ src/ecmascript/spidermonkey/dataset.h | 8 + src/ecmascript/spidermonkey/element.cpp | 26 +- src/ecmascript/spidermonkey/meson.build | 2 +- test/ecmascript/assert/element.dataset.html | 4 + test/ecmascript/assert/meson.build | 1 + 7 files changed, 300 insertions(+), 4 deletions(-) create mode 100644 src/ecmascript/spidermonkey/dataset.cpp create mode 100644 src/ecmascript/spidermonkey/dataset.h diff --git a/src/ecmascript/spidermonkey/Makefile b/src/ecmascript/spidermonkey/Makefile index b7e89c2f8..e8a15c2ca 100644 --- a/src/ecmascript/spidermonkey/Makefile +++ b/src/ecmascript/spidermonkey/Makefile @@ -2,7 +2,7 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config INCLUDES += $(SPIDERMONKEY_CFLAGS) -OBJS = attr.obj attributes.obj collection.obj console.obj css.obj customevent.obj document.obj domrect.obj element.obj event.obj form.obj forms.obj heartbeat.obj history.obj implementation.obj input.obj \ +OBJS = attr.obj attributes.obj collection.obj console.obj css.obj customevent.obj dataset.obj document.obj domrect.obj element.obj event.obj form.obj forms.obj heartbeat.obj history.obj implementation.obj input.obj \ keyboard.obj localstorage.obj location.obj message.obj navigator.obj nodelist.obj nodelist2.obj screen.obj style.obj tokenlist.obj unibar.obj url.obj urlsearchparams.obj window.obj xhr.obj include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/spidermonkey/dataset.cpp b/src/ecmascript/spidermonkey/dataset.cpp new file mode 100644 index 000000000..8a6e72df6 --- /dev/null +++ b/src/ecmascript/spidermonkey/dataset.cpp @@ -0,0 +1,261 @@ +/* The SpiderMonkey datasetobject implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "ecmascript/libdom/dom.h" + +#include "ecmascript/spidermonkey/util.h" +#include +#include + +#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/libdom/corestrings.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/spidermonkey/dataset.h" +#include "ecmascript/spidermonkey/element.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.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 + +static void dataset_finalize(JS::GCContext *op, JSObject *obj) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + dom_node *element = JS::GetMaybePtrFromReservedSlot(obj, 0); + + if (element) { + dom_node_unref(element); + } +} + +static bool +dataset_obj_getProperty(JSContext* ctx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id, JS::MutableHandleValue vp) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + vp.setUndefined(); + + if (!id.isString()) { + return true; + } + char *property = jsid_to_string(ctx, id); + + if (!property) { + return true; + } + dom_node *el = JS::GetMaybePtrFromReservedSlot(obj, 0); + struct string data; + + if (!el ||!init_string(&data)) { + mem_free(property); + return true; + } + add_to_string(&data, "data-"); + add_to_string(&data, property); + mem_free(property); + + dom_string *attr_name = NULL; + dom_exception exc = dom_string_create(data.source, data.length, &attr_name); + done_string(&data); + + if (exc != DOM_NO_ERR || !attr_name) { + return true; + } + dom_string *attr_value = NULL; + exc = dom_element_get_attribute(el, attr_name, &attr_value); + dom_string_unref(attr_name); + + if (exc != DOM_NO_ERR || !attr_value) { + return true; + } + vp.setString(JS_NewStringCopyZ(ctx, dom_string_data(attr_value))); + dom_string_unref(attr_value); + + return true; +} + +static bool +dataset_obj_setProperty(JSContext* ctx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v, JS::HandleValue receiver, JS::ObjectOpResult& result) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + if (!id.isString()) { + return true; + } + char *property = jsid_to_string(ctx, id); + + if (!property) { + return true; + } + char *value = jsval_to_string(ctx, v); + + if (!value) { + mem_free(property); + return true; + } + dom_node *el = JS::GetMaybePtrFromReservedSlot(obj, 0); + struct string data; + + if (!el ||!init_string(&data)) { + mem_free(property); + mem_free(value); + return true; + } + add_to_string(&data, "data-"); + add_to_string(&data, property); + mem_free(property); + + dom_string *attr_name = NULL; + dom_exception exc = dom_string_create(data.source, data.length, &attr_name); + done_string(&data); + + if (exc != DOM_NO_ERR || !attr_name) { + return true; + } + dom_string *attr_value = NULL; + exc = dom_string_create(value, strlen(value), &attr_value); + mem_free(value); + + if (exc != DOM_NO_ERR || !attr_value) { + dom_string_unref(attr_name); + return true; + } + exc = dom_element_set_attribute(el, attr_name, attr_value); + dom_string_unref(attr_name); + dom_string_unref(attr_value); + + return true; +} + +static bool +dataset_obj_deleteProperty(JSContext* ctx, JS::HandleObject obj, JS::HandleId id, JS::ObjectOpResult& result) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + if (!id.isString()) { + return true; + } + char *property = jsid_to_string(ctx, id); + + if (!property) { + return true; + } + dom_node *el = JS::GetMaybePtrFromReservedSlot(obj, 0); + struct string data; + + if (!el ||!init_string(&data)) { + mem_free(property); + return true; + } + add_to_string(&data, "data-"); + add_to_string(&data, property); + mem_free(property); + + dom_string *attr_name = NULL; + dom_exception exc = dom_string_create(data.source, data.length, &attr_name); + done_string(&data); + + if (exc != DOM_NO_ERR || !attr_name) { + return true; + } + dom_string *attr_value = NULL; + exc = dom_element_remove_attribute(el, attr_name); + dom_string_unref(attr_name); + + return true; +} + +JSClassOps dataset_ops = { + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + dataset_finalize, // finalize + nullptr, // call + nullptr, // construct + JS_GlobalObjectTraceHook +}; + +js::ObjectOps dataset_obj_ops = { +// .hasProperty = dataset_obj_hasProperty, + .getProperty = dataset_obj_getProperty, + .setProperty = dataset_obj_setProperty, + .deleteProperty = dataset_obj_deleteProperty +}; + +JSClass dataset_class = { + "dataset", + JSCLASS_HAS_RESERVED_SLOTS(1), + &dataset_ops, + .oOps = &dataset_obj_ops +}; + +static const spidermonkeyFunctionSpec dataset_funcs[] = { + { NULL } +}; + +static JSPropertySpec dataset_props[] = { + JS_PS_END +}; + +JSObject * +getDataset(JSContext *ctx, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JSObject *ds = JS_NewObject(ctx, &dataset_class); + + if (!ds) { + return NULL; + } + JS::RootedObject r_el(ctx, ds); +// JS_DefineProperties(ctx, r_el, (JSPropertySpec *)dataset_props); +// spidermonkey_DefineFunctions(ctx, el, dataset_funcs); + dom_node_ref(node); + JS::SetReservedSlot(ds, 0, JS::PrivateValue(node)); + + return ds; +} diff --git a/src/ecmascript/spidermonkey/dataset.h b/src/ecmascript/spidermonkey/dataset.h new file mode 100644 index 000000000..3bd4b2044 --- /dev/null +++ b/src/ecmascript/spidermonkey/dataset.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_SPIDERMONKEY_DATASET_H +#define EL__ECMASCRIPT_SPIDERMONKEY_DATASET_H + +#include "ecmascript/spidermonkey/util.h" + +JSObject *getDataset(JSContext *ctx, void *element); + +#endif diff --git a/src/ecmascript/spidermonkey/element.cpp b/src/ecmascript/spidermonkey/element.cpp index 341562a21..d76352d17 100644 --- a/src/ecmascript/spidermonkey/element.cpp +++ b/src/ecmascript/spidermonkey/element.cpp @@ -33,6 +33,7 @@ #include "ecmascript/spidermonkey/attr.h" #include "ecmascript/spidermonkey/attributes.h" #include "ecmascript/spidermonkey/collection.h" +#include "ecmascript/spidermonkey/dataset.h" #include "ecmascript/spidermonkey/domrect.h" #include "ecmascript/spidermonkey/event.h" #include "ecmascript/spidermonkey/element.h" @@ -77,6 +78,7 @@ static bool element_get_property_childNodes(JSContext *ctx, unsigned int argc, J static bool element_get_property_classList(JSContext *ctx, unsigned int argc, JS::Value *vp); static bool element_get_property_className(JSContext *ctx, unsigned int argc, JS::Value *vp); static bool element_set_property_className(JSContext *ctx, unsigned int argc, JS::Value *vp); +static bool element_get_property_dataset(JSContext *ctx, unsigned int argc, JS::Value *vp); //static bool element_get_property_clientHeight(JSContext *ctx, unsigned int argc, JS::Value *vp); //static bool element_get_property_clientLeft(JSContext *ctx, unsigned int argc, JS::Value *vp); //static bool element_get_property_clientTop(JSContext *ctx, unsigned int argc, JS::Value *vp); @@ -173,6 +175,7 @@ JSPropertySpec element_props[] = { // JS_PSG("clientLeft", element_get_property_clientLeft, JSPROP_ENUMERATE), // JS_PSG("clientTop", element_get_property_clientTop, JSPROP_ENUMERATE), // JS_PSG("clientWidth", element_get_property_clientWidth, JSPROP_ENUMERATE), + JS_PSG("dataset", element_get_property_dataset, JSPROP_ENUMERATE), JS_PSGS("dir", element_get_property_dir, element_set_property_dir, JSPROP_ENUMERATE), JS_PSG("firstChild", element_get_property_firstChild, JSPROP_ENUMERATE), JS_PSG("firstElementChild", element_get_property_firstElementChild, JSPROP_ENUMERATE), @@ -667,6 +670,27 @@ element_get_property_className(JSContext *ctx, unsigned int argc, JS::Value *vp) return true; } +static bool +element_get_property_dataset(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JS::CallArgs args = CallArgsFromVp(argc, vp); + JS::RootedObject hobj(ctx, &args.thisv().toObject()); + dom_node *el = (dom_node *)JS::GetMaybePtrFromReservedSlot(hobj, 0); + + if (!el) { + args.rval().setNull(); + return true; + } + JSObject *obj = getDataset(ctx, el); + args.rval().setObject(*obj); + + return true; +} + + #if 0 // it does not work yet static bool element_get_property_clientHeight(JSContext *ctx, unsigned int argc, JS::Value *vp) @@ -4460,7 +4484,6 @@ element_getAttribute(JSContext *ctx, unsigned int argc, JS::Value *rval) return true; } len = strlen(str); - exc = dom_string_create((const uint8_t *)str, len, &attr_name); mem_free(str); @@ -4468,7 +4491,6 @@ element_getAttribute(JSContext *ctx, unsigned int argc, JS::Value *rval) args.rval().setNull(); return true; } - exc = dom_element_get_attribute(el, attr_name, &attr_value); dom_string_unref(attr_name); diff --git a/src/ecmascript/spidermonkey/meson.build b/src/ecmascript/spidermonkey/meson.build index 77b3da7bb..1af30783e 100644 --- a/src/ecmascript/spidermonkey/meson.build +++ b/src/ecmascript/spidermonkey/meson.build @@ -1,4 +1,4 @@ -srcs += files('attr.cpp', 'attributes.cpp', 'collection.cpp', 'console.cpp', 'css.cpp', 'customevent.cpp', 'document.cpp', 'domrect.cpp', 'element.cpp', 'event.cpp', +srcs += files('attr.cpp', 'attributes.cpp', 'collection.cpp', 'console.cpp', 'css.cpp', 'customevent.cpp', 'dataset.cpp', 'document.cpp', 'domrect.cpp', 'element.cpp', 'event.cpp', 'form.cpp', 'forms.cpp', 'heartbeat.cpp', 'history.cpp', 'implementation.cpp', 'input.cpp', 'keyboard.cpp', 'localstorage.cpp', 'location.cpp', 'message.cpp', 'navigator.cpp', 'nodelist.cpp', 'nodelist2.cpp', 'screen.cpp', 'style.cpp', 'tokenlist.cpp', 'unibar.cpp', 'url.cpp', 'urlsearchparams.cpp', 'window.cpp', 'xhr.cpp') diff --git a/test/ecmascript/assert/element.dataset.html b/test/ecmascript/assert/element.dataset.html index 61f3ed937..9f85dc944 100644 --- a/test/ecmascript/assert/element.dataset.html +++ b/test/ecmascript/assert/element.dataset.html @@ -14,6 +14,10 @@ function myFunction() { console.assert(div.dataset.b === 'abc', 'abc'); div.dataset.b = '123'; console.assert(div.dataset.b === '123', '123'); + console.assert(div.dataset.abc === undefined, 'undefined'); + + delete(div.dataset.b); + console.assert(div.dataset.b === undefined, 'undefined because deleted'); } console.error('element.dataset.html'); diff --git a/test/ecmascript/assert/meson.build b/test/ecmascript/assert/meson.build index eb7f5f371..1520cb387 100644 --- a/test/ecmascript/assert/meson.build +++ b/test/ecmascript/assert/meson.build @@ -33,6 +33,7 @@ took = [ 'element.cloneNode.html', 'element.closest.html', 'element.contains.html', +'element.dataset.html', 'element.dir.html', 'element.eventListener.html', 'element.firstChild.html',