From 63d892838c1c091062f4e822b2fda3456b0f058c Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Sun, 2 May 2021 17:27:35 +0200 Subject: [PATCH] [getElementById] small success. see test/ecmascript/element.html Now memory leaks, etc. are not taken into account. For the beginning I will implement read properties. --- src/document/document.h | 1 + src/ecmascript/spidermonkey/Makefile | 2 +- src/ecmascript/spidermonkey/document.c | 75 ++++++++++- src/ecmascript/spidermonkey/element.c | 159 ++++++++++++++++++++++++ src/ecmascript/spidermonkey/element.h | 14 +++ src/ecmascript/spidermonkey/meson.build | 2 +- test/ecmascript/element.html | 15 +++ 7 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 src/ecmascript/spidermonkey/element.c create mode 100644 src/ecmascript/spidermonkey/element.h create mode 100644 test/ecmascript/element.html diff --git a/src/document/document.h b/src/document/document.h index f15dd633a..8535ef311 100644 --- a/src/document/document.h +++ b/src/document/document.h @@ -211,6 +211,7 @@ struct document { /** used by setTimeout */ timer_id_T timeout; int ecmascript_counter; + void *dom; #endif #ifdef CONFIG_CSS /** @todo FIXME: We should externally maybe using cache_entry store the diff --git a/src/ecmascript/spidermonkey/Makefile b/src/ecmascript/spidermonkey/Makefile index b5b95e1e7..0c9996f42 100644 --- a/src/ecmascript/spidermonkey/Makefile +++ b/src/ecmascript/spidermonkey/Makefile @@ -2,6 +2,6 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config INCLUDES += $(SPIDERMONKEY_CFLAGS) -OBJS = console.o document.o form.o heartbeat.o location.o localstorage.o localstorage-db.o navigator.o unibar.o window.o +OBJS = console.o document.o element.c form.o heartbeat.o location.o localstorage.o localstorage-db.o navigator.o unibar.o window.o include $(top_srcdir)/Makefile.lib diff --git a/src/ecmascript/spidermonkey/document.c b/src/ecmascript/spidermonkey/document.c index c2dce55f5..153ae4938 100644 --- a/src/ecmascript/spidermonkey/document.c +++ b/src/ecmascript/spidermonkey/document.c @@ -27,6 +27,7 @@ #include "ecmascript/spidermonkey/form.h" #include "ecmascript/spidermonkey/location.h" #include "ecmascript/spidermonkey/document.h" +#include "ecmascript/spidermonkey/element.h" #include "ecmascript/spidermonkey/window.h" #include "intl/gettext/libintl.h" #include "main/select.h" @@ -48,6 +49,10 @@ #include "viewer/text/link.h" #include "viewer/text/vs.h" +#include +using namespace htmlcxx; + +#include static bool document_get_property(JSContext *ctx, JS::HandleObject hobj, JS::HandleId hid, JS::MutableHandleValue hvp); @@ -678,6 +683,30 @@ document_replace(JSContext *ctx, unsigned int argc, JS::Value *vp) return(true); } +static void * +document_parse(struct document *document) +{ + struct cache_entry *cached = document->cached; + struct fragment *f = get_cache_fragment(cached); + + if (!f || !f->length) { + return NULL; + } + + struct string str; + init_string(&str); + + add_bytes_to_string(&str, f->data, f->length); + + HTML::ParserDom parser; + tree *dom = new tree; + *dom = parser.parseTree(str.source); + done_string(&str); + + return (void *)dom; +} + + static bool document_getElementById(JSContext *ctx, unsigned int argc, JS::Value *vp) { @@ -687,7 +716,51 @@ document_getElementById(JSContext *ctx, unsigned int argc, JS::Value *vp) args.rval().setBoolean(false); return true; } - args.rval().setNull(); + + JSCompartment *comp = js::GetContextCompartment(ctx); + struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(comp); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + args.rval().setNull(); + return true; + } + + tree *dom = document->dom; + tree::iterator it = dom->begin(); + tree::iterator end = dom->end(); + + struct string idstr; + + init_string(&idstr); + jshandle_value_to_char_string(&idstr, ctx, &args[0]); + std::string id = idstr.source; + + JSObject *elem = nullptr; + + for (; it != end; ++it) { + if (it->isTag()) { + it->parseAttributes(); + if (it->attribute("id").first && it->attribute("id").second == id) { + tree *node = new tree; + *node = *it; + elem = getElement(ctx, node); + break; + } + } + } + + done_string(&idstr); + if (elem) { + args.rval().setObject(*elem); + } else { + args.rval().setNull(); + } return true; } diff --git a/src/ecmascript/spidermonkey/element.c b/src/ecmascript/spidermonkey/element.c new file mode 100644 index 000000000..af5721db0 --- /dev/null +++ b/src/ecmascript/spidermonkey/element.c @@ -0,0 +1,159 @@ +/* The SpiderMonkey html element objects implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.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/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/spidermonkey/element.h" +#include "ecmascript/spidermonkey/window.h" +#include "intl/gettext/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" + +using namespace htmlcxx; + +static bool element_get_property_id(JSContext *ctx, unsigned int argc, JS::Value *vp); +static bool element_set_property_id(JSContext *ctx, unsigned int argc, JS::Value *vp); + +JSClassOps element_ops = { + JS_PropertyStub, nullptr, + JS_PropertyStub, JS_StrictPropertyStub, + nullptr, nullptr, nullptr, nullptr +}; + +JSClass element_class = { + "element", + JSCLASS_HAS_PRIVATE, + &element_ops +}; + +JSPropertySpec element_props[] = { + JS_PSGS("id", element_get_property_id, element_set_property_id, JSPROP_ENUMERATE), + JS_PS_END +}; + + +static bool +element_get_property_id(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ + JS::CallArgs args = CallArgsFromVp(argc, vp); + JS::RootedObject hobj(ctx, &args.thisv().toObject()); + + struct view_state *vs; + JSCompartment *comp = js::GetContextCompartment(ctx); + + if (!comp) { + return false; + } + + struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(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, &element_class, NULL)) + return false; + + vs = interpreter->vs; + if (!vs) { + return false; + } + + tree *el = JS_GetPrivate(hobj); + + if (!el) { + args.rval().setNull(); + return true; + } + + tree::iterator it = el->begin(); + it->parseAttributes(); + std::string v = it->attribute("id").second; + + args.rval().setString(JS_NewStringCopyZ(ctx, v.c_str())); + + return true; +} + +static bool +element_set_property_id(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ + JS::CallArgs args = CallArgsFromVp(argc, vp); + JS::RootedObject hobj(ctx, &args.thisv().toObject()); + + JSCompartment *comp = js::GetContextCompartment(ctx); + + if (!comp) { + return false; + } + + struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(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, &element_class, NULL)) + return false; + + struct view_state *vs = interpreter->vs; + if (!vs) { + return true; + } + + return true; +} + +JSObject * +getElement(JSContext *ctx, void *node) +{ + JSObject *el = JS_NewObject(ctx, &element_class); + + if (!el) { + return NULL; + } + + JS::RootedObject r_el(ctx, el); + + JS_DefineProperties(ctx, r_el, (JSPropertySpec *) element_props); +// spidermonkey_DefineFunctions(ctx, el, element_funcs); + + JS_SetPrivate(el, node); + + return el; +} diff --git a/src/ecmascript/spidermonkey/element.h b/src/ecmascript/spidermonkey/element.h new file mode 100644 index 000000000..de5ef22e6 --- /dev/null +++ b/src/ecmascript/spidermonkey/element.h @@ -0,0 +1,14 @@ + +#ifndef EL__ECMASCRIPT_SPIDERMONKEY_ELEMENT_H +#define EL__ECMASCRIPT_SPIDERMONKEY_ELEMENT_H + +#include "ecmascript/spidermonkey/util.h" +#include +using namespace htmlcxx; + +extern JSClass element_class; +extern JSPropertySpec element_props[]; + +JSObject *getElement(JSContext *ctx, void *node); + +#endif diff --git a/src/ecmascript/spidermonkey/meson.build b/src/ecmascript/spidermonkey/meson.build index 015060b88..2a218759f 100644 --- a/src/ecmascript/spidermonkey/meson.build +++ b/src/ecmascript/spidermonkey/meson.build @@ -1,3 +1,3 @@ #INCLUDES += $(SPIDERMONKEY_CFLAGS) -srcs += files('console.c', 'document.c', 'form.c', 'heartbeat.c', 'location.c', 'localstorage.c', 'localstorage-db.c', 'navigator.c', 'unibar.c', 'window.c') +srcs += files('console.c', 'document.c', 'element.c', 'form.c', 'heartbeat.c', 'location.c', 'localstorage.c', 'localstorage-db.c', 'navigator.c', 'unibar.c', 'window.c') diff --git a/test/ecmascript/element.html b/test/ecmascript/element.html new file mode 100644 index 000000000..74c83efd5 --- /dev/null +++ b/test/ecmascript/element.html @@ -0,0 +1,15 @@ + + +BBB +bbb +AAA +BB + + + +