static JSValue qjs_xml_node_make(JSContext *cx, qjs_xml_doc_t *doc,
xmlNode *node);
+static int qjs_xml_node_has_method(JSContext *cx, JSValueConst obj,
+ JSAtom prop, njs_str_t *name);
static int qjs_xml_node_get_own_property(JSContext *cx,
JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop);
static int qjs_xml_node_get_own_property_names(JSContext *cx,
}
+static int
+qjs_xml_node_has_method(JSContext *cx, JSValueConst obj, JSAtom prop,
+ njs_str_t *name)
+{
+ int rc;
+ size_t i, length;
+ JSValue proto;
+ const JSCFunctionListEntry *entry;
+
+ for (i = 0; i < njs_nitems(qjs_xml_node_proto); i++) {
+ entry = &qjs_xml_node_proto[i];
+
+ if (entry->def_type != JS_DEF_CFUNC) {
+ continue;
+ }
+
+ length = njs_strlen(entry->name);
+
+ if (name->length == length
+ && njs_strncmp(name->start, entry->name, length) == 0)
+ {
+ proto = JS_GetPrototype(cx, obj);
+ if (JS_IsException(proto)) {
+ return -1;
+ }
+
+ rc = JS_IsObject(proto) ? JS_GetOwnProperty(cx, NULL, proto, prop)
+ : 0;
+ JS_FreeValue(cx, proto);
+
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+
static int
qjs_xml_node_get_own_property(JSContext *cx, JSPropertyDescriptor *pdesc,
JSValueConst obj, JSAtom prop)
{
+ int rc;
u_char *text;
JSValue value;
xmlNode *node;
nm = name;
+ rc = qjs_xml_node_has_method(cx, obj, prop, &name);
+ if (rc != 0) {
+ JS_FreeCString(cx, (char *) name.start);
+ return rc < 0 ? -1 : 0;
+ }
+
tag:
value = qjs_xml_node_tag_handler(cx, current, &nm);
}
}
+ if (delete) {
+ rc = qjs_xml_node_has_method(cx, obj, atom, &name);
+ if (rc != 0) {
+ JS_FreeCString(cx, (char *) name.start);
+ return rc < 0 ? -1 : 1;
+ }
+ }
+
rc = qjs_xml_node_tag_modify(cx, obj, &name, delete);
JS_FreeCString(cx, (char *) name.start);
{ doc: `<r><!--c--><?p?>text<c/></r>`,
get: (doc) => doc.r.getChildNS(null, 'c').$name,
expected: 'c' },
+ { doc: `<r ID="ok"><addChild/><getAttributeNS/><getChildNS/>`
+ + `<removeChildren/><setAttribute/><removeAttribute/>`
+ + `<removeAllAttributes/><setText/><removeText/>`
+ + `<Issuer>trusted</Issuer></r>`,
+ get: (doc) => {
+ const node = doc.r;
+ const methods = ['addChild', 'getAttributeNS', 'getChildNS',
+ 'removeChildren', 'setAttribute',
+ 'removeAttribute', 'removeAllAttributes',
+ 'setText', 'removeText'];
+
+ return methods.every(name => typeof node[name] == 'function'
+ && node[`$tag$${name}`].$name == name)
+ && node.getAttributeNS(null, 'ID') == 'ok'
+ && node.getChildNS(null, 'Issuer').$text == 'trusted';
+ },
+ expected: true },
+ { doc: `<r><getChildNS/></r>`,
+ get: (doc) => {
+ delete doc.r.$tag$getChildNS;
+ return typeof doc.r.getChildNS == 'function'
+ && doc.r.$tag$getChildNS === undefined;
+ },
+ expected: true },
+ { doc: `<r><setAttribute/></r>`,
+ skip: () => !has_quickjs(),
+ get: (doc) => {
+ delete doc.r.setAttribute;
+ return typeof doc.r.setAttribute == 'function'
+ && doc.r.$tag$setAttribute.$name == 'setAttribute';
+ },
+ expected: true },
+ { doc: `<r><getChildNS/></r>`,
+ skip: () => !has_quickjs(),
+ get: (doc) => {
+ const proto = Object.getPrototypeOf(doc.r);
+ const method = proto.getChildNS;
+
+ delete proto.getChildNS;
+
+ try {
+ return doc.r.getChildNS.$name == 'getChildNS';
+
+ } finally {
+ proto.getChildNS = method;
+ }
+ },
+ expected: true },
{ doc: `<!DOCTYPE r [<!ENTITY e "complete">]>`
+ `<r xmlns:x="urn:other" x:ID="pre&e;post"/>`,
get: (doc) => doc.r.getAttributeNS('urn:other', 'ID'),
/**
* node.$tag$xxx - the node's first child tag named "xxx".
+ * Use this form when the tag name matches an XMLNode method.
* Assigning a value throws TypeError at runtime. Deleting the property
* removes matching child tags.
*/