From 89dde0e0b6e1d53e78056c11a8712e75e5558c7d Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Mon, 24 Jun 2024 16:50:08 +0200 Subject: [PATCH] [spidermonkey] own implementation of nodeList for querySelectorAll --- src/ecmascript/ecmascript-c.c | 19 +- src/ecmascript/ecmascript-c.h | 9 +- src/ecmascript/spidermonkey/Makefile | 2 +- src/ecmascript/spidermonkey/document.cpp | 21 +- src/ecmascript/spidermonkey/element.cpp | 16 +- src/ecmascript/spidermonkey/meson.build | 2 +- src/ecmascript/spidermonkey/nodelist2.cpp | 204 +++++++++++++++++++ src/ecmascript/spidermonkey/nodelist2.h | 8 + test/ecmascript/assert/nodelist.forEach.html | 2 +- 9 files changed, 249 insertions(+), 34 deletions(-) create mode 100644 src/ecmascript/spidermonkey/nodelist2.cpp create mode 100644 src/ecmascript/spidermonkey/nodelist2.h diff --git a/src/ecmascript/ecmascript-c.c b/src/ecmascript/ecmascript-c.c index 825329a9..1b1d83e0 100644 --- a/src/ecmascript/ecmascript-c.c +++ b/src/ecmascript/ecmascript-c.c @@ -559,7 +559,7 @@ walk_tree_query(dom_node *node, const char *selector, int depth) } void -walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int depth) +walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int depth, LIST_OF(struct selector_node) *result_list) { dom_exception exc; dom_node *child; @@ -574,17 +574,11 @@ walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int } if (res = el_match_selector(selector, node)) { - dom_node *clone = NULL; - exc = dom_node_clone_node(res, false, &clone); + struct selector_node *sn = (struct selector_node *)mem_calloc(1, sizeof(*sn)); - if (exc != DOM_NO_ERR || !clone) { - } else { - dom_node *result = NULL; - exc = dom_node_append_child(root, clone, &result); - } - - if (exc != DOM_NO_ERR) { - return; + if (sn) { + sn->node = res; + add_to_list_end(*result_list, sn); } } /* Get the node's first child */ @@ -601,7 +595,7 @@ walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int dom_node *next_child; /* Visit node's descendents */ - walk_tree_query_append(root, child, selector, depth); + walk_tree_query_append(root, child, selector, depth, result_list); /* Go to next sibling */ exc = dom_node_get_next_sibling(child, &next_child); @@ -615,4 +609,3 @@ walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int } while (child != NULL); /* No more children */ } } - diff --git a/src/ecmascript/ecmascript-c.h b/src/ecmascript/ecmascript-c.h index 346149f3..352cfbd4 100644 --- a/src/ecmascript/ecmascript-c.h +++ b/src/ecmascript/ecmascript-c.h @@ -5,6 +5,7 @@ #include "ecmascript/libdom/dom.h" #include "main/module.h" #include "terminal/kbd.h" +#include "util/lists.h" #ifdef __cplusplus extern "C" { @@ -22,6 +23,12 @@ struct term_event; struct uri; struct view_state; +struct selector_node { + LIST_HEAD_EL(struct selector_node); + + void *node; +}; + int ecmascript_get_interpreter_count(void); void ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter); void toggle_ecmascript(struct session *ses); @@ -42,7 +49,7 @@ void ecmascript_detach_form_state(struct form_state *fs); void ecmascript_moved_form_state(struct form_state *fs); void *walk_tree_query(dom_node *node, const char *selector, int depth); -void walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int depth); +void walk_tree_query_append(dom_node *root, dom_node *node, const char *selector, int depth, LIST_OF(struct selector_node) *result_list); extern struct module ecmascript_module; diff --git a/src/ecmascript/spidermonkey/Makefile b/src/ecmascript/spidermonkey/Makefile index bf98c5ed..5cdd2882 100644 --- a/src/ecmascript/spidermonkey/Makefile +++ b/src/ecmascript/spidermonkey/Makefile @@ -3,6 +3,6 @@ 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 \ - keyboard.obj localstorage.obj location.obj message.obj navigator.obj nodelist.obj screen.obj style.obj unibar.obj url.obj urlsearchparams.obj window.obj xhr.obj + keyboard.obj localstorage.obj location.obj message.obj navigator.obj nodelist.obj nodelist2.obj screen.obj style.obj unibar.obj url.obj urlsearchparams.obj window.obj xhr.obj include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/spidermonkey/document.cpp b/src/ecmascript/spidermonkey/document.cpp index 15dc312b..a8bf4911 100644 --- a/src/ecmascript/spidermonkey/document.cpp +++ b/src/ecmascript/spidermonkey/document.cpp @@ -41,6 +41,7 @@ #include "ecmascript/spidermonkey/event.h" #include "ecmascript/spidermonkey/heartbeat.h" #include "ecmascript/spidermonkey/nodelist.h" +#include "ecmascript/spidermonkey/nodelist2.h" #include "ecmascript/spidermonkey/util.h" #include "ecmascript/spidermonkey/window.h" #include "intl/libintl.h" @@ -2181,19 +2182,21 @@ document_querySelectorAll(JSContext *ctx, unsigned int argc, JS::Value *vp) args.rval().setNull(); return true; } - walk_tree_query_append((dom_node *)element, doc_root, selector, 0); - dom_node_unref(doc_root); - mem_free(selector); + LIST_OF(struct selector_node) *result_list = (LIST_OF(struct selector_node) *)mem_calloc(1, sizeof(*result_list)); - dom_nodelist *nodes = NULL; - exc = dom_node_get_child_nodes(element, &nodes); - dom_node_unref(element); - - if (exc != DOM_NO_ERR || !nodes) { + if (!result_list) { + dom_node_unref(doc_root); + mem_free(selector); args.rval().setNull(); return true; } - JSObject *obj = getNodeList(ctx, nodes); + init_list(*result_list); + + walk_tree_query_append((dom_node *)element, doc_root, selector, 0, result_list); + dom_node_unref(doc_root); + mem_free(selector); + + JSObject *obj = getNodeList2(ctx, result_list); args.rval().setObject(*obj); return true; diff --git a/src/ecmascript/spidermonkey/element.cpp b/src/ecmascript/spidermonkey/element.cpp index 7a80f5b3..d693abd3 100644 --- a/src/ecmascript/spidermonkey/element.cpp +++ b/src/ecmascript/spidermonkey/element.cpp @@ -39,6 +39,7 @@ #include "ecmascript/spidermonkey/heartbeat.h" #include "ecmascript/spidermonkey/keyboard.h" #include "ecmascript/spidermonkey/nodelist.h" +#include "ecmascript/spidermonkey/nodelist2.h" #include "ecmascript/spidermonkey/style.h" #include "ecmascript/spidermonkey/window.h" #include "intl/libintl.h" @@ -5058,18 +5059,17 @@ element_querySelectorAll(JSContext *ctx, unsigned int argc, JS::Value *vp) args.rval().setNull(); return true; } - walk_tree_query_append((dom_node *)element, el, selector, 0); - mem_free(selector); + LIST_OF(struct selector_node) *result_list = (LIST_OF(struct selector_node) *)mem_calloc(1, sizeof(*result_list)); - dom_nodelist *nodes = NULL; - exc = dom_node_get_child_nodes(element, &nodes); - dom_node_unref(element); - - if (exc != DOM_NO_ERR || !nodes) { + if (!result_list) { + mem_free(selector); args.rval().setNull(); return true; } - JSObject *obj = getNodeList(ctx, nodes); + init_list(*result_list); + walk_tree_query_append((dom_node *)element, el, selector, 0, result_list); + mem_free(selector); + JSObject *obj = getNodeList2(ctx, result_list); args.rval().setObject(*obj); return true; diff --git a/src/ecmascript/spidermonkey/meson.build b/src/ecmascript/spidermonkey/meson.build index 37e4f445..e77d2daf 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', 'form.cpp', 'forms.cpp', 'heartbeat.cpp', 'history.cpp', 'implementation.cpp', 'input.cpp', 'keyboard.cpp', -'localstorage.cpp', 'location.cpp', 'message.cpp', 'navigator.cpp', 'nodelist.cpp', 'screen.cpp', 'style.cpp', 'unibar.cpp', 'url.cpp', +'localstorage.cpp', 'location.cpp', 'message.cpp', 'navigator.cpp', 'nodelist.cpp', 'nodelist2.cpp', 'screen.cpp', 'style.cpp', 'unibar.cpp', 'url.cpp', 'urlsearchparams.cpp', 'window.cpp', 'xhr.cpp') diff --git a/src/ecmascript/spidermonkey/nodelist2.cpp b/src/ecmascript/spidermonkey/nodelist2.cpp new file mode 100644 index 00000000..9c82329f --- /dev/null +++ b/src/ecmascript/spidermonkey/nodelist2.cpp @@ -0,0 +1,204 @@ +/* The SpiderMonkey nodeList2 object 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 "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/ecmascript-c.h" +#include "ecmascript/spidermonkey/element.h" +#include "ecmascript/spidermonkey/nodelist2.h" +#include "ecmascript/spidermonkey/window.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/lists.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 bool nodeList2_item(JSContext *ctx, unsigned int argc, JS::Value *rval); +static bool nodeList2_item2(JSContext *ctx, JS::HandleObject hobj, int index, JS::MutableHandleValue hvp); + +static void nodeList2_finalize(JS::GCContext *op, JSObject *obj) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + LIST_OF(struct selector_node) *sni = JS::GetMaybePtrFromReservedSlot(obj, 0); + + if (sni) { + free_list(*sni); + } +} + +JSClassOps nodeList2_ops = { + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + nodeList2_finalize, // finalize + nullptr, // call + nullptr, // construct + JS_GlobalObjectTraceHook +}; + +JSClass nodeList2_class = { + "nodeList2", + JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + &nodeList2_ops +}; + +static const spidermonkeyFunctionSpec nodeList2_funcs[] = { + { "item", nodeList2_item, 1 }, + { NULL } +}; + +static bool nodeList2_get_property_length(JSContext *ctx, unsigned int argc, JS::Value *vp); + +static JSPropertySpec nodeList2_props[] = { + JS_PSG("length", nodeList2_get_property_length, JSPROP_ENUMERATE), + JS_PS_END +}; + +static bool +nodeList2_get_property_length(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()); + + struct view_state *vs; + JS::Realm *comp = js::GetContextRealm(ctx); + + if (!comp) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return false; + } + + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS::GetRealmPrivate(comp); + + /* This can be called if @obj if not itself an instance of the + * appropriate class but has one in its prototype chain. Fail + * such calls. */ + if (!JS_InstanceOf(ctx, hobj, &nodeList2_class, NULL)) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return false; + } + LIST_OF(struct selector_node) *sni = JS::GetMaybePtrFromReservedSlot(hobj, 0); + args.rval().setInt32(list_size(sni)); + + return true; +} + +static bool +nodeList2_item(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JS::Value val; + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + JS::RootedObject hobj(ctx, &args.thisv().toObject()); + JS::RootedValue rval(ctx, val); + LIST_OF(struct selector_node) *sni = JS::GetMaybePtrFromReservedSlot(hobj, 0); + int index = args[0].toInt32(); + int counter = 0; + struct selector_node *sn = NULL; + + foreach (sn, *sni) { + if (counter == index) { + break; + } + counter++; + } + + if (!sn || !sn->node) { + args.rval().setNull(); + return true; + } + + JSObject *res = getElement(ctx, sn->node); + args.rval().setObject(*res); + return true; +} + +JSObject * +getNodeList2(JSContext *ctx, void *res) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + LIST_OF(struct selector_node) *sni = (LIST_OF(struct selector_node) *)res; + JSObject *el = JS_NewObject(ctx, &nodeList2_class); + + if (!el) { + return NULL; + } + JS::RootedObject r_el(ctx, el); + + JS_DefineProperties(ctx, r_el, (JSPropertySpec *) nodeList2_props); + spidermonkey_DefineFunctions(ctx, el, nodeList2_funcs); + + struct selector_node *sn = NULL; + int i = 0; + + foreach (sn, *sni) { + JSObject *obj = getElement(ctx, sn->node); + + if (obj) { + JS::RootedObject v(ctx, obj); + JS::RootedValue ro(ctx, JS::ObjectOrNullValue(v)); + JS_SetElement(ctx, r_el, i, ro); + } + i++; + } + JS::SetReservedSlot(el, 0, JS::PrivateValue(res)); + + return el; +} diff --git a/src/ecmascript/spidermonkey/nodelist2.h b/src/ecmascript/spidermonkey/nodelist2.h new file mode 100644 index 00000000..bc4548a5 --- /dev/null +++ b/src/ecmascript/spidermonkey/nodelist2.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_SPIDERMONKEY_NODELIST2_H +#define EL__ECMASCRIPT_SPIDERMONKEY_NODELIST2_H + +#include "ecmascript/spidermonkey/util.h" + +JSObject *getNodeList2(JSContext *ctx, void *result_list); + +#endif diff --git a/test/ecmascript/assert/nodelist.forEach.html b/test/ecmascript/assert/nodelist.forEach.html index 0d4595e0..a9727555 100644 --- a/test/ecmascript/assert/nodelist.forEach.html +++ b/test/ecmascript/assert/nodelist.forEach.html @@ -9,7 +9,7 @@ console.error('nodelist.forEach.html'); var birds = document.querySelectorAll('li'); -console.assert(birds.length === 3, birds.length); +console.assert(birds.length === 3, 'birds.length'); var counter = 0; birds.forEach(function(b) {