From 3e30d82fb463a2ecca7d1d49b47f2ede508a2012 Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Tue, 11 May 2021 19:02:54 +0200 Subject: [PATCH] [js] jsGetElementsByName --- src/ecmascript/spidermonkey/document.c | 63 ++++++- src/ecmascript/spidermonkey/element.c | 243 ++++++++++++++++++++++++- src/ecmascript/spidermonkey/element.h | 1 + test/ecmascript/getElementsByName.html | 22 +++ 4 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 test/ecmascript/getElementsByName.html diff --git a/src/ecmascript/spidermonkey/document.c b/src/ecmascript/spidermonkey/document.c index c637308c..c67ae09c 100644 --- a/src/ecmascript/spidermonkey/document.c +++ b/src/ecmascript/spidermonkey/document.c @@ -463,12 +463,14 @@ static bool document_write(JSContext *ctx, unsigned int argc, JS::Value *rval); static bool document_writeln(JSContext *ctx, unsigned int argc, JS::Value *rval); static bool document_replace(JSContext *ctx, unsigned int argc, JS::Value *rval); static bool document_getElementById(JSContext *ctx, unsigned int argc, JS::Value *rval); +static bool document_getElementByName(JSContext *ctx, unsigned int argc, JS::Value *rval); const spidermonkeyFunctionSpec document_funcs[] = { { "write", document_write, 1 }, { "writeln", document_writeln, 1 }, { "replace", document_replace, 1 }, { "getElementById", document_getElementById, 1 }, + { "getElementsByName", document_getElementByName, 1 }, { NULL } }; @@ -709,7 +711,6 @@ document_parse(struct document *document) return (void *)root; } - static bool document_getElementById(JSContext *ctx, unsigned int argc, JS::Value *vp) { @@ -766,3 +767,63 @@ document_getElementById(JSContext *ctx, unsigned int argc, JS::Value *vp) return true; } + +static bool +document_getElementByName(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ + JS::CallArgs args = CallArgsFromVp(argc, vp); + + if (argc != 1) { + args.rval().setBoolean(false); + return true; + } + + 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; + } + + xmlpp::Element* root = (xmlpp::Element *)document->dom; + + struct string idstr; + + init_string(&idstr); + jshandle_value_to_char_string(&idstr, ctx, &args[0]); + std::string id = idstr.source; + + std::string xpath = "//*[@id=\""; + xpath += id; + xpath += "\"]|//*[@name=\""; + xpath += id; + xpath += "\"]"; + + done_string(&idstr); + + xmlpp::Node::NodeSet *elements = new xmlpp::Node::NodeSet; + + *elements = root->find(xpath); + + if (elements->size() == 0) { + args.rval().setNull(); + return true; + } + + JSObject *elem = getCollection(ctx, elements); + + 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 index 8a2e52ff..6a0c879a 100644 --- a/src/ecmascript/spidermonkey/element.c +++ b/src/ecmascript/spidermonkey/element.c @@ -584,7 +584,6 @@ element_get_property_title(JSContext *ctx, unsigned int argc, JS::Value *vp) return true; } - static int was_el = 0; static void @@ -1102,3 +1101,245 @@ getElement(JSContext *ctx, void *node) return el; } + +static bool htmlCollection_item(JSContext *ctx, unsigned int argc, JS::Value *rval); +static bool htmlCollection_namedItem(JSContext *ctx, unsigned int argc, JS::Value *rval); +static bool htmlCollection_get_property(JSContext *ctx, JS::HandleObject hobj, JS::HandleId hid, JS::MutableHandleValue hvp); +static bool htmlCollection_item2(JSContext *ctx, JS::HandleObject hobj, int index, JS::MutableHandleValue hvp); +static bool htmlCollection_namedItem2(JSContext *ctx, JS::HandleObject hobj, char *str, JS::MutableHandleValue hvp); + +JSClassOps htmlCollection_ops = { + JS_PropertyStub, nullptr, + htmlCollection_get_property, JS_StrictPropertyStub, + nullptr, nullptr, nullptr, nullptr +}; + +JSClass htmlCollection_class = { + "htmlCollection", + JSCLASS_HAS_PRIVATE, + &htmlCollection_ops +}; + +static const spidermonkeyFunctionSpec htmlCollection_funcs[] = { + { "item", htmlCollection_item, 1 }, + { "namedItem", htmlCollection_namedItem, 1 }, + { NULL } +}; + +static bool htmlCollection_get_property_length(JSContext *ctx, unsigned int argc, JS::Value *vp); + +static JSPropertySpec htmlCollection_props[] = { + JS_PSG("length", htmlCollection_get_property_length, JSPROP_ENUMERATE), + JS_PS_END +}; + +static bool +htmlCollection_get_property_length(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, &htmlCollection_class, NULL)) + return false; + + vs = interpreter->vs; + if (!vs) { + return false; + } + + xmlpp::Node::NodeSet *ns = JS_GetPrivate(hobj); + + if (!ns) { + args.rval().setInt32(0); + return true; + } + + args.rval().setInt32(ns->size()); + + return true; +} + +static bool +htmlCollection_item(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ + JS::Value val; + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + JS::RootedObject hobj(ctx, &args.thisv().toObject()); + JS::RootedValue rval(ctx, val); + + int index = args[0].toInt32(); + bool ret = htmlCollection_item2(ctx, hobj, index, &rval); + args.rval().set(rval); + + return ret; +} + +static bool +htmlCollection_namedItem(JSContext *ctx, unsigned int argc, JS::Value *vp) +{ + JS::Value val; + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + JS::RootedObject hobj(ctx, &args.thisv().toObject()); + JS::RootedValue rval(ctx, val); + + char *str = JS_EncodeString(ctx, args[0].toString()); + bool ret = htmlCollection_namedItem2(ctx, hobj, str, &rval); + args.rval().set(rval); + + return ret; +} + +static bool +htmlCollection_item2(JSContext *ctx, JS::HandleObject hobj, int index, JS::MutableHandleValue hvp) +{ + JSCompartment *comp = js::GetContextCompartment(ctx); + + if (!comp) { + return false; + } + + struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(comp); + + if (!JS_InstanceOf(ctx, hobj, &htmlCollection_class, NULL)) return false; + + hvp.setUndefined(); + + xmlpp::Node::NodeSet *ns = JS_GetPrivate(hobj); + + if (!ns) { + return true; + } + + xmlpp::Element *element; + + try { + element = ns->at(index); + } catch (std::out_of_range e) { return true;} + + if (!element) { + return true; + } + + JSObject *obj = getElement(ctx, element); + hvp.setObject(*obj); + + return true; +} + +static bool +htmlCollection_namedItem2(JSContext *ctx, JS::HandleObject hobj, char *str, JS::MutableHandleValue hvp) +{ + JSCompartment *comp = js::GetContextCompartment(ctx); + + if (!comp) { + return false; + } + + struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(comp); + + if (!JS_InstanceOf(ctx, hobj, &htmlCollection_class, NULL)) + return false; + + xmlpp::Node::NodeSet *ns = JS_GetPrivate(hobj); + + hvp.setUndefined(); + + if (!ns) { + return true; + } + + std::string name = str; + + auto it = ns->begin(); + auto end = ns->end(); + + for (; it != end; ++it) { + const auto element = dynamic_cast(*it); + + if (!element) { + continue; + } + + if (name == element->get_attribute_value("id") + || name == element->get_attribute_value("name")) { + JSObject *obj = getElement(ctx, element); + hvp.setObject(*obj); + return true; + } + } + + return true; +} + +static bool +htmlCollection_get_property(JSContext *ctx, JS::HandleObject hobj, JS::HandleId hid, JS::MutableHandleValue hvp) +{ + jsid id = hid.get(); + struct view_state *vs; + JS::Value idval; + + 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, &htmlCollection_class, NULL)) { + return false; + } + + if (JSID_IS_INT(id)) { + JS::RootedValue r_idval(ctx, idval); + JS_IdToValue(ctx, id, &r_idval); + int index = r_idval.toInt32(); + return htmlCollection_item2(ctx, hobj, index, hvp); + } + +#if 0 + if (JSID_IS_STRING(id)) { + JS::RootedValue r_idval(ctx, idval); + JS_IdToValue(ctx, id, &r_idval); + char *string = JS_EncodeString(ctx, r_idval.toString()); + + return htmlCollection_namedItem2(ctx, hobj, string, hvp); + } +#endif + + return JS_PropertyStub(ctx, hobj, hid, hvp); +} + +JSObject * +getCollection(JSContext *ctx, void *node) +{ + JSObject *el = JS_NewObject(ctx, &htmlCollection_class); + + if (!el) { + return NULL; + } + + JS::RootedObject r_el(ctx, el); + + JS_DefineProperties(ctx, r_el, (JSPropertySpec *) htmlCollection_props); + spidermonkey_DefineFunctions(ctx, el, htmlCollection_funcs); + + JS_SetPrivate(el, node); + + return el; +} diff --git a/src/ecmascript/spidermonkey/element.h b/src/ecmascript/spidermonkey/element.h index de5ef22e..9c565c68 100644 --- a/src/ecmascript/spidermonkey/element.h +++ b/src/ecmascript/spidermonkey/element.h @@ -10,5 +10,6 @@ extern JSClass element_class; extern JSPropertySpec element_props[]; JSObject *getElement(JSContext *ctx, void *node); +JSObject *getCollection(JSContext *ctx, void *node); #endif diff --git a/test/ecmascript/getElementsByName.html b/test/ecmascript/getElementsByName.html new file mode 100644 index 00000000..fbaa86c5 --- /dev/null +++ b/test/ecmascript/getElementsByName.html @@ -0,0 +1,22 @@ + + + + +First Name:
+First Name: + +

Click the button to get the tag name of the first element in the document that has a name attribute with the value "fname".

+ + + +

+ + + + +