diff options
| author | Bob Jamison <ishmalius@gmail.com> | 2008-03-08 21:34:57 +0000 |
|---|---|---|
| committer | ishmal <ishmal@users.sourceforge.net> | 2008-03-08 21:34:57 +0000 |
| commit | e5587d6e0be240ac09ea23535d346a62d2a59d2b (patch) | |
| tree | 21e311c33fb02affaea5db580918888482fd7232 /src/dom/js/jsxml.c | |
| parent | Oops. added ucd.c instead of ucd.cpp. (diff) | |
| download | inkscape-e5587d6e0be240ac09ea23535d346a62d2a59d2b.tar.gz inkscape-e5587d6e0be240ac09ea23535d346a62d2a59d2b.zip | |
Begin change from Spidermonkey binding to JVM
(bzr r4997)
Diffstat (limited to 'src/dom/js/jsxml.c')
| -rw-r--r-- | src/dom/js/jsxml.c | 8295 |
1 files changed, 0 insertions, 8295 deletions
diff --git a/src/dom/js/jsxml.c b/src/dom/js/jsxml.c deleted file mode 100644 index e7cde2edf..000000000 --- a/src/dom/js/jsxml.c +++ /dev/null @@ -1,8295 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey E4X code, released August, 2004. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsstddef.h" -#include "jsconfig.h" - -#if JS_HAS_XML_SUPPORT - -#include <math.h> -#include <stdlib.h> -#include <string.h> -#include "jstypes.h" -#include "jsbit.h" -#include "jsprf.h" -#include "jsutil.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsxml.h" - -#ifdef DEBUG -#include <string.h> /* for #ifdef DEBUG memset calls */ -#endif - -/* - * NOTES - * - in the js shell, you must use the -x command line option, or call - * options('xml') before compiling anything that uses XML literals - * - * TODO - * - XXXbe patrol - * - Fuse objects and their JSXML* private data into single GC-things - * - fix function::foo vs. x.(foo == 42) collision using proper namespacing - * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants - * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! - * - JS_TypeOfValue sure could use a cleaner interface to "types" - */ - -#ifdef DEBUG_brendan -#define METERING 1 -#endif - -#ifdef METERING -static struct { - jsrefcount qname; - jsrefcount qnameobj; - jsrefcount liveqname; - jsrefcount liveqnameobj; - jsrefcount namespace; - jsrefcount namespaceobj; - jsrefcount livenamespace; - jsrefcount livenamespaceobj; - jsrefcount xml; - jsrefcount xmlobj; - jsrefcount livexml; - jsrefcount livexmlobj; -} xml_stats; - -#define METER(x) JS_ATOMIC_INCREMENT(&(x)) -#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) -#else -#define METER(x) /* nothing */ -#define UNMETER(x) /* nothing */ -#endif - -/* - * Random utilities and global functions. - */ -const char js_AnyName_str[] = "AnyName"; -const char js_AttributeName_str[] = "AttributeName"; -const char js_isXMLName_str[] = "isXMLName"; -const char js_XMLList_str[] = "XMLList"; -const char js_localName_str[] = "localName"; -const char js_xml_parent_str[] = "parent"; -const char js_prefix_str[] = "prefix"; -const char js_toXMLString_str[] = "toXMLString"; -const char js_uri_str[] = "uri"; - -const char js_amp_entity_str[] = "&"; -const char js_gt_entity_str[] = ">"; -const char js_lt_entity_str[] = "<"; -const char js_quot_entity_str[] = """; - -#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) -#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') - -static JSBool -xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); - return JS_TRUE; -} - -/* - * Namespace class and library functions. - */ -enum namespace_tinyid { - NAMESPACE_PREFIX = -1, - NAMESPACE_URI = -2 -}; - -static JSBool -namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXMLNamespace *ns; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - ns = (JSXMLNamespace *) - JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); - if (!ns) - return JS_TRUE; - - switch (JSVAL_TO_INT(id)) { - case NAMESPACE_PREFIX: - *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; - break; - case NAMESPACE_URI: - *vp = STRING_TO_JSVAL(ns->uri); - break; - } - return JS_TRUE; -} - -static void -namespace_finalize(JSContext *cx, JSObject *obj) -{ - JSXMLNamespace *ns; - JSRuntime *rt; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - if (!ns) - return; - JS_ASSERT(ns->object == obj); - ns->object = NULL; - UNMETER(xml_stats.livenamespaceobj); - - rt = cx->runtime; - if (rt->functionNamespaceObject == obj) - rt->functionNamespaceObject = NULL; -} - -static void -namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len, - void *arg) -{ - uint32 i; - JSXMLNamespace *ns; - - for (i = 0; i < len; i++) { - ns = vec[i]; - { -#ifdef GC_MARK_DEBUG - char buf[100]; - - JS_snprintf(buf, sizeof buf, "%s=%s", - ns->prefix ? JS_GetStringBytes(ns->prefix) : "", - JS_GetStringBytes(ns->uri)); -#else - const char *buf = NULL; -#endif - JS_MarkGCThing(cx, ns, buf, arg); - } - } -} - -static uint32 -namespace_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - JS_MarkGCThing(cx, ns, js_private_str, arg); - return 0; -} - -static JSBool -namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXMLNamespace *ns, *ns2; - JSObject *obj2; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - JS_ASSERT(JSVAL_IS_OBJECT(v)); - obj2 = JSVAL_TO_OBJECT(v); - if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { - *bp = JS_FALSE; - } else { - ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); - *bp = !js_CompareStrings(ns->uri, ns2->uri); - } - return JS_TRUE; -} - -JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { - { "Namespace", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED, - JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, namespace_mark, NULL }, - namespace_equality, - NULL, NULL, - JSCLASS_NO_RESERVED_MEMBERS -}; - -#define NAMESPACE_ATTRS \ - (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) - -static JSPropertySpec namespace_props[] = { - {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, - {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, - {0,0,0,0,0} -}; - -static JSBool -namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) - JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); - if (!ns) - return JS_FALSE; - - *rval = STRING_TO_JSVAL(ns->uri); - return JS_TRUE; -} - -static JSFunctionSpec namespace_methods[] = { - {js_toString_str, namespace_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSXMLNamespace * -js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) - js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); - if (!ns) - return NULL; - ns->object = NULL; - ns->prefix = prefix; - ns->uri = uri; - ns->declared = declared; - METER(xml_stats.namespace); - METER(xml_stats.livenamespace); - return ns; -} - -void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns, void *arg) -{ - JS_MarkGCThing(cx, ns->object, js_object_str, arg); - JS_MarkGCThing(cx, ns->prefix, js_prefix_str, arg); - JS_MarkGCThing(cx, ns->uri, js_uri_str, arg); -} - -void -js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) -{ - UNMETER(xml_stats.livenamespace); -} - -JSObject * -js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared) -{ - JSXMLNamespace *ns; - - ns = js_NewXMLNamespace(cx, prefix, uri, declared); - if (!ns) - return NULL; - return js_GetXMLNamespaceObject(cx, ns); -} - -JSObject * -js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) -{ - JSObject *obj; - - obj = ns->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == ns); - return obj; - } - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, ns)) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } - ns->object = obj; - METER(xml_stats.namespaceobj); - METER(xml_stats.livenamespaceobj); - return obj; -} - -/* - * QName class and library functions. - */ -enum qname_tinyid { - QNAME_URI = -1, - QNAME_LOCALNAME = -2 -}; - -static JSBool -qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXMLQName *qn; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - qn = (JSXMLQName *) - JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); - if (!qn) - return JS_TRUE; - - switch (JSVAL_TO_INT(id)) { - case QNAME_URI: - *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; - break; - case QNAME_LOCALNAME: - *vp = STRING_TO_JSVAL(qn->localName); - break; - } - return JS_TRUE; -} - -static void -qname_finalize(JSContext *cx, JSObject *obj) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - if (!qn) - return; - JS_ASSERT(qn->object == obj); - qn->object = NULL; - UNMETER(xml_stats.liveqnameobj); -} - -static void -anyname_finalize(JSContext* cx, JSObject* obj) -{ - JSRuntime *rt; - - /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ - rt = cx->runtime; - if (rt->anynameObject == obj) - rt->anynameObject = NULL; - - qname_finalize(cx, obj); -} - -static uint32 -qname_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - JS_MarkGCThing(cx, qn, js_private_str, arg); - return 0; -} - -static JSBool -qname_identity(JSXMLQName *qna, JSXMLQName *qnb) -{ - if (!qna->uri ^ !qnb->uri) - return JS_FALSE; - if (qna->uri && js_CompareStrings(qna->uri, qnb->uri)) - return JS_FALSE; - return !js_CompareStrings(qna->localName, qnb->localName); -} - -static JSBool -qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXMLQName *qn, *qn2; - JSObject *obj2; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - JS_ASSERT(JSVAL_IS_OBJECT(v)); - obj2 = JSVAL_TO_OBJECT(v); - if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { - *bp = JS_FALSE; - } else { - qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); - *bp = qname_identity(qn, qn2); - } - return JS_TRUE; -} - -JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { - { "QName", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED, - JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL }, - qname_equality, - NULL, NULL, - JSCLASS_NO_RESERVED_MEMBERS -}; - -/* - * Classes for the ECMA-357-internal types AttributeName and AnyName, which - * are like QName, except that they have no property getters. They share the - * qname_toString method, and therefore are exposed as constructable objects - * in this implementation. - */ -JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { - js_AttributeName_str, JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL -}; - -JS_FRIEND_DATA(JSClass) js_AnyNameClass = { - js_AnyName_str, JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL -}; - -#define QNAME_ATTRS \ - (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) - -static JSPropertySpec qname_props[] = { - {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, - {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, - {0,0,0,0,0} -}; - -static JSBool -qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSClass *clasp; - JSXMLQName *qn; - JSString *str, *qualstr; - size_t length; - jschar *chars; - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - } else { - qn = (JSXMLQName *) - JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); - if (!qn) - return JS_FALSE; - } - - if (!qn->uri) { - /* No uri means wildcard qualifier. */ - str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); - } else if (IS_EMPTY(qn->uri)) { - /* Empty string for uri means localName is in no namespace. */ - str = cx->runtime->emptyString; - } else { - qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); - str = js_ConcatStrings(cx, qn->uri, qualstr); - if (!str) - return JS_FALSE; - } - str = js_ConcatStrings(cx, str, qn->localName); - if (!str) - return JS_FALSE; - - if (str && clasp == &js_AttributeNameClass) { - length = JSSTRING_LENGTH(str); - chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - *chars = '@'; - js_strncpy(chars + 1, JSSTRING_CHARS(str), length); - chars[++length] = 0; - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - } - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSFunctionSpec qname_methods[] = { - {js_toString_str, qname_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSXMLQName * -js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); - if (!qn) - return NULL; - qn->object = NULL; - qn->uri = uri; - qn->prefix = prefix; - qn->localName = localName; - METER(xml_stats.qname); - METER(xml_stats.liveqname); - return qn; -} - -void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn, void *arg) -{ - JS_MarkGCThing(cx, qn->object, js_object_str, arg); - JS_MarkGCThing(cx, qn->uri, js_uri_str, arg); - JS_MarkGCThing(cx, qn->prefix, js_prefix_str, arg); - JS_MarkGCThing(cx, qn->localName, js_localName_str, arg); -} - -void -js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) -{ - UNMETER(xml_stats.liveqname); -} - -JSObject * -js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName) -{ - JSXMLQName *qn; - - qn = js_NewXMLQName(cx, uri, prefix, localName); - if (!qn) - return NULL; - return js_GetXMLQNameObject(cx, qn); -} - -JSObject * -js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) -{ - JSObject *obj; - - obj = qn->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == qn); - return obj; - } - obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - return obj; -} - -JSObject * -js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) -{ - JSObject *obj; - - obj = qn->object; - if (obj) { - if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) - return obj; - qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); - if (!qn) - return NULL; - } - - obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } - - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - return obj; -} - -JSObject * -js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) -{ - jsval argv[2]; - - /* - * ECMA-357 11.1.2, - * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ - * production, step 2. - */ - if (!JSVAL_IS_PRIMITIVE(nsval) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { - nsval = JSVAL_NULL; - } - - argv[0] = nsval; - argv[1] = lnval; - return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); -} - -static JSBool -IsXMLName(const jschar *cp, size_t n) -{ - JSBool rv; - jschar c; - - rv = JS_FALSE; - if (n != 0 && JS_ISXMLNSSTART(*cp)) { - while (--n != 0) { - c = *++cp; - if (!JS_ISXMLNS(c)) - return rv; - } - rv = JS_TRUE; - } - return rv; -} - -JSBool -js_IsXMLName(JSContext *cx, jsval v) -{ - JSClass *clasp; - JSXMLQName *qn; - JSString *name; - JSErrorReporter older; - - /* - * Inline specialization of the QName constructor called with v passed as - * the only argument, to compute the localName for the constructed qname, - * without actually allocating the object or computing its uri and prefix. - * See ECMA-357 13.1.2.1 step 1 and 13.3.2. - */ - if (!JSVAL_IS_PRIMITIVE(v) && - (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), - clasp == &js_QNameClass.base || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass)) { - qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - name = qn->localName; - } else { - older = JS_SetErrorReporter(cx, NULL); - name = js_ValueToString(cx, v); - JS_SetErrorReporter(cx, older); - if (!name) { - JS_ClearPendingException(cx); - return JS_FALSE; - } - } - - return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); -} - -static JSBool -Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval urival, prefixval; - JSObject *uriobj; - JSBool isNamespace, isQName; - JSClass *clasp; - JSString *empty, *prefix; - JSXMLNamespace *ns, *ns2; - JSXMLQName *qn; - - urival = argv[argc > 1]; - isNamespace = isQName = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(urival)) { - uriobj = JSVAL_TO_OBJECT(urival); - clasp = OBJ_GET_CLASS(cx, uriobj); - isNamespace = (clasp == &js_NamespaceClass.base); - isQName = (clasp == &js_QNameClass.base); - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else uriobj = NULL; -#endif - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* Namespace called as function. */ - if (argc == 1 && isNamespace) { - /* Namespace called with one Namespace argument is identity. */ - *rval = urival; - return JS_TRUE; - } - - /* Create and return a new QName object exactly as if constructed. */ - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - METER(xml_stats.namespaceobj); - METER(xml_stats.livenamespaceobj); - - /* - * Create and connect private data to rooted obj early, so we don't have - * to worry about rooting string newborns hanging off of the private data - * further below. - */ - empty = cx->runtime->emptyString; - ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); - if (!ns) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, ns)) - return JS_FALSE; - ns->object = obj; - - if (argc == 1) { - if (isNamespace) { - ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); - ns->uri = ns2->uri; - ns->prefix = ns2->prefix; - } else if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { - ns->uri = qn->uri; - ns->prefix = qn->prefix; - } else { - ns->uri = js_ValueToString(cx, urival); - if (!ns->uri) - return JS_FALSE; - - /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ - if (!IS_EMPTY(ns->uri)) - ns->prefix = NULL; - } - } else if (argc == 2) { - if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { - ns->uri = qn->uri; - } else { - ns->uri = js_ValueToString(cx, urival); - if (!ns->uri) - return JS_FALSE; - } - - prefixval = argv[0]; - if (IS_EMPTY(ns->uri)) { - if (!JSVAL_IS_VOID(prefixval)) { - prefix = js_ValueToString(cx, prefixval); - if (!prefix) - return JS_FALSE; - if (!IS_EMPTY(prefix)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAMESPACE, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(prefix))); - return JS_FALSE; - } - } - } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { - /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ - ns->prefix = NULL; - } else { - prefix = js_ValueToString(cx, prefixval); - if (!prefix) - return JS_FALSE; - ns->prefix = prefix; - } - } - - return JS_TRUE; -} - -static JSBool -QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval nameval, nsval; - JSBool isQName, isNamespace; - JSXMLQName *qn; - JSString *uri, *prefix, *name; - JSObject *nsobj; - JSClass *clasp; - JSXMLNamespace *ns; - - nameval = argv[argc > 1]; - isQName = - !JSVAL_IS_PRIMITIVE(nameval) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* QName called as function. */ - if (argc == 1 && isQName) { - /* QName called with one QName argument is identity. */ - *rval = nameval; - return JS_TRUE; - } - - /* - * Create and return a new QName object exactly as if constructed. - * Use the constructor's clasp so we can be shared by AttributeName - * (see below after this function). - */ - obj = js_NewObject(cx, - argv - ? JS_ValueToFunction(cx, argv[-2])->clasp - : &js_QNameClass.base, - NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - if (isQName) { - /* If namespace is not specified and name is a QName, clone it. */ - qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); - if (argc == 1) { - uri = qn->uri; - prefix = qn->prefix; - name = qn->localName; - goto out; - } - - /* Namespace and qname were passed -- use the qname's localName. */ - nameval = STRING_TO_JSVAL(qn->localName); - } - - if (argc == 0) { - name = cx->runtime->emptyString; - } else { - name = js_ValueToString(cx, nameval); - if (!name) - return JS_FALSE; - - /* Use argv[1] as a local root for name, even if it was not passed. */ - argv[1] = STRING_TO_JSVAL(name); - } - - nsval = argv[0]; - if (argc == 1 || JSVAL_IS_VOID(nsval)) { - if (IS_STAR(name)) { - nsval = JSVAL_NULL; - } else { - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return JS_FALSE; - } - } - - if (JSVAL_IS_NULL(nsval)) { - /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ - uri = prefix = NULL; - } else { - /* - * Inline specialization of the Namespace constructor called with - * nsval passed as the only argument, to compute the uri and prefix - * for the constructed namespace, without actually allocating the - * object or computing other members. See ECMA-357 13.3.2 6(a) and - * 13.2.2. - */ - isNamespace = isQName = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(nsval)) { - nsobj = JSVAL_TO_OBJECT(nsval); - clasp = OBJ_GET_CLASS(cx, nsobj); - isNamespace = (clasp == &js_NamespaceClass.base); - isQName = (clasp == &js_QNameClass.base); - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else nsobj = NULL; -#endif - - if (isNamespace) { - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - uri = ns->uri; - prefix = ns->prefix; - } else if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { - uri = qn->uri; - prefix = qn->prefix; - } else { - uri = js_ValueToString(cx, nsval); - if (!uri) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(uri); /* local root */ - - /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ - prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; - } - } - -out: - qn = js_NewXMLQName(cx, uri, prefix, name); - if (!qn) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, qn)) - return JS_FALSE; - qn->object = obj; - return JS_TRUE; -} - -static JSBool -AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* - * Since js_AttributeNameClass was initialized, obj will have that as its - * class, not js_QNameClass. - */ - return QName(cx, obj, argc, argv, rval); -} - -/* - * XMLArray library functions. - */ -static JSBool -namespace_identity(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsa->prefix && nsb->prefix) { - if (js_CompareStrings(nsa->prefix, nsb->prefix)) - return JS_FALSE; - } else { - if (nsa->prefix || nsb->prefix) - return JS_FALSE; - } - return !js_CompareStrings(nsa->uri, nsb->uri); -} - -static JSBool -attr_identity(const void *a, const void *b) -{ - const JSXML *xmla = (const JSXML *) a; - const JSXML *xmlb = (const JSXML *) b; - - return qname_identity(xmla->name, xmlb->name); -} - -static void -XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) -{ - JSXMLArrayCursor *next; - - cursor->array = array; - cursor->index = 0; - next = cursor->next = array->cursors; - if (next) - next->prevp = &cursor->next; - cursor->prevp = &array->cursors; - array->cursors = cursor; - cursor->root = NULL; -} - -static void -XMLArrayCursorFinish(JSXMLArrayCursor *cursor) -{ - JSXMLArrayCursor *next; - - if (!cursor->array) - return; - next = cursor->next; - if (next) - next->prevp = cursor->prevp; - *cursor->prevp = next; - cursor->array = NULL; -} - -static void * -XMLArrayCursorNext(JSXMLArrayCursor *cursor) -{ - JSXMLArray *array; - - array = cursor->array; - if (!array || cursor->index >= array->length) - return NULL; - return cursor->root = array->vector[cursor->index++]; -} - -static void * -XMLArrayCursorItem(JSXMLArrayCursor *cursor) -{ - JSXMLArray *array; - - array = cursor->array; - if (!array || cursor->index >= array->length) - return NULL; - return cursor->root = array->vector[cursor->index]; -} - -static void -XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) -{ - while (cursor) { - GC_MARK(cx, cursor->root, "cursor->root", NULL); - cursor = cursor->next; - } -} - -/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ -static JSBool -XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) -{ - void **vector; - - if (capacity == 0) { - /* We could let realloc(p, 0) free this, but purify gets confused. */ - if (array->vector) - free(array->vector); - vector = NULL; - } else { - if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || - !(vector = (void **) - realloc(array->vector, capacity * sizeof(void *)))) { - if (cx) - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - array->capacity = JSXML_PRESET_CAPACITY | capacity; - array->vector = vector; - return JS_TRUE; -} - -static void -XMLArrayTrim(JSXMLArray *array) -{ - if (array->capacity & JSXML_PRESET_CAPACITY) - return; - if (array->length < array->capacity) - XMLArraySetCapacity(NULL, array, array->length); -} - -static JSBool -XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) -{ - array->length = array->capacity = 0; - array->vector = NULL; - array->cursors = NULL; - return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); -} - -static void -XMLArrayFinish(JSContext *cx, JSXMLArray *array) -{ - JSXMLArrayCursor *cursor; - - JS_free(cx, array->vector); - - while ((cursor = array->cursors) != NULL) - XMLArrayCursorFinish(cursor); - -#ifdef DEBUG - memset(array, 0xd5, sizeof *array); -#endif -} - -#define XML_NOT_FOUND ((uint32) -1) - -static uint32 -XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) -{ - void **vector; - uint32 i, n; - - /* The identity op must not reallocate array->vector. */ - vector = array->vector; - if (identity) { - for (i = 0, n = array->length; i < n; i++) { - if (identity(vector[i], elt)) - return i; - } - } else { - for (i = 0, n = array->length; i < n; i++) { - if (vector[i] == elt) - return i; - } - } - return XML_NOT_FOUND; -} - -/* - * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after - * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold - * should be greater than increment. - */ -#define LINEAR_THRESHOLD 256 -#define LINEAR_INCREMENT 32 - -static JSBool -XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) -{ - uint32 capacity, i; - int log2; - void **vector; - - if (index >= array->length) { - if (index >= JSXML_CAPACITY(array)) { - /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ - capacity = index + 1; - if (index >= LINEAR_THRESHOLD) { - capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); - } else { - JS_CEILING_LOG2(log2, capacity); - capacity = JS_BIT(log2); - } - if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || - !(vector = (void **) - realloc(array->vector, capacity * sizeof(void *)))) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - array->capacity = capacity; - array->vector = vector; - for (i = array->length; i < index; i++) - vector[i] = NULL; - } - array->length = index + 1; - } - - array->vector[index] = elt; - return JS_TRUE; -} - -static JSBool -XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) -{ - uint32 j; - JSXMLArrayCursor *cursor; - - j = array->length; - JS_ASSERT(i <= j); - if (!XMLArraySetCapacity(cx, array, j + n)) - return JS_FALSE; - - array->length = j + n; - JS_ASSERT(n != (uint32)-1); - while (j != i) { - --j; - array->vector[j + n] = array->vector[j]; - } - - for (cursor = array->cursors; cursor; cursor = cursor->next) { - if (cursor->index > i) - cursor->index += n; - } - return JS_TRUE; -} - -static void * -XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) -{ - uint32 length; - void **vector, *elt; - JSXMLArrayCursor *cursor; - - length = array->length; - if (index >= length) - return NULL; - - vector = array->vector; - elt = vector[index]; - if (compress) { - while (++index < length) - vector[index-1] = vector[index]; - array->length = length - 1; - array->capacity = JSXML_CAPACITY(array); - } else { - vector[index] = NULL; - } - - for (cursor = array->cursors; cursor; cursor = cursor->next) { - if (cursor->index > index) - --cursor->index; - } - return elt; -} - -static void -XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) -{ - void **vector; - - JS_ASSERT(!array->cursors); - if (length >= array->length) - return; - - if (length == 0) { - if (array->vector) - free(array->vector); - vector = NULL; - } else { - vector = realloc(array->vector, length * sizeof(void *)); - if (!vector) - return; - } - - if (array->length > length) - array->length = length; - array->capacity = length; - array->vector = vector; -} - -#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) -#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ - XML_NOT_FOUND) -#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ - ? (t *) (a)->vector[i] \ - : NULL) -#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ - if ((a)->length <= (i)) \ - (a)->length = (i) + 1; \ - ((a)->vector[i] = (void *)(e)); \ - JS_END_MACRO -#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) -#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) -#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) -#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) -#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) - -/* - * Define XML setting property strings and constants early, so everyone can - * use the same names and their magic numbers (tinyids, flags). - */ -static const char js_ignoreComments_str[] = "ignoreComments"; -static const char js_ignoreProcessingInstructions_str[] - = "ignoreProcessingInstructions"; -static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; -static const char js_prettyPrinting_str[] = "prettyPrinting"; -static const char js_prettyIndent_str[] = "prettyIndent"; - -/* - * NB: These XML static property tinyids must - * (a) not collide with the generic negative tinyids at the top of jsfun.c; - * (b) index their corresponding xml_static_props array elements. - * Don't change 'em! - */ -enum xml_static_tinyid { - XML_IGNORE_COMMENTS, - XML_IGNORE_PROCESSING_INSTRUCTIONS, - XML_IGNORE_WHITESPACE, - XML_PRETTY_PRINTING, - XML_PRETTY_INDENT -}; - -static JSBool -xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -static JSBool -xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool b; - uint8 flag; - - JS_ASSERT(JSVAL_IS_INT(id)); - if (!js_ValueToBoolean(cx, *vp, &b)) - return JS_FALSE; - - flag = JS_BIT(JSVAL_TO_INT(id)); - if (b) - cx->xmlSettingFlags |= flag; - else - cx->xmlSettingFlags &= ~flag; - return JS_TRUE; -} - -static JSPropertySpec xml_static_props[] = { - {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_ignoreProcessingInstructions_str, - XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, - xml_setting_getter, NULL}, - {0,0,0,0,0} -}; - -/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ -#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) -#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ - JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) -#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) -#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) -#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) - -/* - * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. - * This flag means a couple of things: - * - * - The top JSXML created for a parse tree must have an object owning it. - * - * - That the default namespace normally inherited from the temporary - * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source - * string must, in the case of a precompiled XML object tree, inherit via - * ad-hoc code in ParseNodeToXML. - * - * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. - */ -#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) - -/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ -#define IS_XML(str) \ - (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) - -#define IS_XMLNS(str) \ - (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) - -#define IS_XML_CHARS(chars) \ - (JS_TOLOWER((chars)[0]) == 'x' && \ - JS_TOLOWER((chars)[1]) == 'm' && \ - JS_TOLOWER((chars)[2]) == 'l') - -#define HAS_NS_AFTER_XML(chars) \ - (JS_TOLOWER((chars)[3]) == 'n' && \ - JS_TOLOWER((chars)[4]) == 's') - -#define IS_XMLNS_CHARS(chars) \ - (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) - -#define STARTS_WITH_XML(chars,length) \ - (length >= 3 && IS_XML_CHARS(chars)) - -static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; -static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; - -static JSXMLQName * -ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, - JSBool isAttributeName) -{ - JSString *str, *uri, *prefix, *localName; - size_t length, offset; - const jschar *start, *limit, *colon; - uint32 n; - JSXMLNamespace *ns; - - JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = ATOM_TO_STRING(pn->pn_atom); - length = JSSTRING_LENGTH(str); - start = JSSTRING_CHARS(str); - JS_ASSERT(length != 0 && *start != '@'); - JS_ASSERT(length != 1 || *start != '*'); - - uri = cx->runtime->emptyString; - limit = start + length; - colon = js_strchr_limit(start, ':', limit); - if (colon) { - offset = PTRDIFF(colon, start, jschar); - prefix = js_NewDependentString(cx, str, 0, offset, 0); - if (!prefix) - return NULL; - - if (STARTS_WITH_XML(start, offset)) { - if (offset == 3) { - uri = JS_InternString(cx, xml_namespace_str); - if (!uri) - return NULL; - } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { - uri = JS_InternString(cx, xmlns_namespace_str); - if (!uri) - return NULL; - } else { - uri = NULL; - } - } else { - uri = NULL; - n = inScopeNSes->length; - while (n != 0) { - --n; - ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (ns->prefix && !js_CompareStrings(ns->prefix, prefix)) { - uri = ns->uri; - break; - } - } - } - - if (!uri) { - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_XML_NAMESPACE, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(prefix))); - return NULL; - } - - localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); - if (!localName) - return NULL; - } else { - if (isAttributeName) { - /* - * An unprefixed attribute is not in any namespace, so set prefix - * as well as uri to the empty string. - */ - prefix = uri; - } else { - /* - * Loop from back to front looking for the closest declared default - * namespace. - */ - n = inScopeNSes->length; - while (n != 0) { - --n; - ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (!ns->prefix || IS_EMPTY(ns->prefix)) { - uri = ns->uri; - break; - } - } - prefix = NULL; - } - localName = str; - } - - return js_NewXMLQName(cx, uri, prefix, localName); -} - -static JSString * -ChompXMLWhitespace(JSContext *cx, JSString *str) -{ - size_t length, newlength, offset; - const jschar *cp, *start, *end; - jschar c; - - length = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (!JS_ISXMLSPACE(c)) - break; - } - while (end > cp) { - c = end[-1]; - if (!JS_ISXMLSPACE(c)) - break; - --end; - } - newlength = PTRDIFF(end, cp, jschar); - if (newlength == length) - return str; - offset = PTRDIFF(cp, start, jschar); - return js_NewDependentString(cx, str, offset, newlength, 0); -} - -static JSXML * -ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, - uintN flags) -{ - JSXML *xml, *kid, *attr, *attrj; - JSString *str; - uint32 length, n, i, j; - JSParseNode *pn2, *pn3, *head, **pnp; - JSXMLNamespace *ns; - JSXMLQName *qn, *attrjqn; - JSXMLClass xml_class; - -#define PN2X_SKIP_CHILD ((JSXML *) 1) - - /* - * Cases return early to avoid common code that gets an outermost xml's - * object, which protects GC-things owned by xml and its descendants from - * garbage collection. - */ - xml = NULL; - if (!JS_EnterLocalRootScope(cx)) - return NULL; - switch (pn->pn_type) { - case TOK_XMLELEM: - length = inScopeNSes->length; - pn2 = pn->pn_head; - xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (!xml) - goto fail; - if (js_PushLocalRoot(cx, cx->localRootStack, (jsval)xml) < 0) - goto fail; - - flags &= ~XSF_PRECOMPILED_ROOT; - n = pn->pn_count; - JS_ASSERT(n >= 2); - n -= 2; - if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) - goto fail; - - i = 0; - while ((pn2 = pn2->pn_next) != NULL) { - if (!pn2->pn_next) { - /* Don't append the end tag! */ - JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); - break; - } - - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && pn2->pn_type == TOK_XMLSPACE) { - --n; - continue; - } - - kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (kid == PN2X_SKIP_CHILD) { - --n; - continue; - } - - if (!kid) - goto fail; - - /* Store kid in xml right away, to protect it from GC. */ - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); - kid->parent = xml; - ++i; - - /* XXX where is this documented in an XML spec, or in E4X? */ - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid->xml_value); - if (!str) - goto fail; - kid->xml_value = str; - } - } - - JS_ASSERT(i == n); - if (n < pn->pn_count - 2) - XMLArrayTrim(&xml->xml_kids); - XMLARRAY_TRUNCATE(cx, inScopeNSes, length); - break; - - case TOK_XMLLIST: - xml = js_NewXML(cx, JSXML_CLASS_LIST); - if (!xml) - goto fail; - - n = pn->pn_count; - if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) - goto fail; - - i = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Always ignore insignificant whitespace in lists -- we shouldn't - * condition this on an XML.ignoreWhitespace setting when the list - * constructor is XMLList (note XML/XMLList unification hazard). - */ - if (pn2->pn_type == TOK_XMLSPACE) { - --n; - continue; - } - - kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (kid == PN2X_SKIP_CHILD) { - --n; - continue; - } - - if (!kid) - goto fail; - - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); - ++i; - } - - if (n < pn->pn_count) - XMLArrayTrim(&xml->xml_kids); - break; - - case TOK_XMLSTAGO: - case TOK_XMLPTAGC: - length = inScopeNSes->length; - pn2 = pn->pn_head; - JS_ASSERT(pn2->pn_type == TOK_XMLNAME); - if (pn2->pn_arity == PN_LIST) - goto syntax; - - xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); - if (!xml) - goto fail; - - /* First pass: check syntax and process namespace declarations. */ - JS_ASSERT(pn->pn_count >= 1); - n = pn->pn_count - 1; - pnp = &pn2->pn_next; - head = *pnp; - while ((pn2 = *pnp) != NULL) { - size_t length; - const jschar *chars; - - if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) - goto syntax; - - /* Enforce "Well-formedness constraint: Unique Att Spec". */ - for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { - if (pn3->pn_atom == pn2->pn_atom) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_DUPLICATE_XML_ATTR, - js_ValueToPrintableString(cx, - ATOM_KEY(pn2->pn_atom))); - goto fail; - } - } - - str = ATOM_TO_STRING(pn2->pn_atom); - pn2 = pn2->pn_next; - JS_ASSERT(pn2); - if (pn2->pn_type != TOK_XMLATTR) - goto syntax; - - length = JSSTRING_LENGTH(str); - chars = JSSTRING_CHARS(str); - if (length >= 5 && - IS_XMLNS_CHARS(chars) && - (length == 5 || chars[5] == ':')) { - JSString *uri, *prefix; - - uri = ATOM_TO_STRING(pn2->pn_atom); - if (length == 5) { - /* 10.3.2.1. Step 6(h)(i)(1)(a). */ - prefix = cx->runtime->emptyString; - } else { - prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); - if (!prefix) - goto fail; - } - - /* - * Once the new ns is appended to xml->xml_namespaces, it is - * protected from GC by the object that owns xml -- which is - * either xml->object if outermost, or the object owning xml's - * oldest ancestor if !outermost. - */ - ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); - if (!ns) - goto fail; - - /* - * Don't add a namespace that's already in scope. If someone - * extracts a child property from its parent via [[Get]], then - * we enforce the invariant, noted many times in ECMA-357, that - * the child's namespaces form a possibly-improper superset of - * its ancestors' namespaces. - */ - if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { - if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || - !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { - goto fail; - } - } - - JS_ASSERT(n >= 2); - n -= 2; - *pnp = pn2->pn_next; - /* XXXbe recycle pn2 */ - continue; - } - - pnp = &pn2->pn_next; - } - - /* - * If called from js_ParseNodeToXMLObject, emulate the effect of the - * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to - * the String Type" (ECMA-357 10.3.1). - */ - if (flags & XSF_PRECOMPILED_ROOT) { - JS_ASSERT(length >= 1); - ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, - namespace_identity)); - ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); - if (!ns) - goto fail; - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - goto fail; - } - XMLArrayTrim(&xml->xml_namespaces); - - /* Second pass: process tag name and attributes, using namespaces. */ - pn2 = pn->pn_head; - qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); - if (!qn) - goto fail; - xml->name = qn; - - JS_ASSERT((n & 1) == 0); - n >>= 1; - if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) - goto fail; - - for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { - qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); - if (!qn) { - xml->xml_attrs.length = i; - goto fail; - } - - /* - * Enforce "Well-formedness constraint: Unique Att Spec", part 2: - * this time checking local name and namespace URI. - */ - for (j = 0; j < i; j++) { - attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); - attrjqn = attrj->name; - if (!js_CompareStrings(attrjqn->uri, qn->uri) && - !js_CompareStrings(attrjqn->localName, qn->localName)) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_DUPLICATE_XML_ATTR, - js_ValueToPrintableString(cx, - ATOM_KEY(pn2->pn_atom))); - goto fail; - } - } - - pn2 = pn2->pn_next; - JS_ASSERT(pn2); - JS_ASSERT(pn2->pn_type == TOK_XMLATTR); - - attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - if (!attr) - goto fail; - - XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); - attr->parent = xml; - attr->name = qn; - attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); - } - - /* Point tag closes its own namespace scope. */ - if (pn->pn_type == TOK_XMLPTAGC) - XMLARRAY_TRUNCATE(cx, inScopeNSes, length); - break; - - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: - case TOK_XMLPI: - str = ATOM_TO_STRING(pn->pn_atom); - qn = NULL; - if (pn->pn_type == TOK_XMLCOMMENT) { - if (flags & XSF_IGNORE_COMMENTS) - goto skip_child; - xml_class = JSXML_CLASS_COMMENT; - } else if (pn->pn_type == TOK_XMLPI) { - if (IS_XML(str)) { - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_RESERVED_ID, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(str))); - goto fail; - } - - if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) - goto skip_child; - - qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); - if (!qn) - goto fail; - - str = pn->pn_atom2 - ? ATOM_TO_STRING(pn->pn_atom2) - : cx->runtime->emptyString; - xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; - } else { - /* CDATA section content, or element text. */ - xml_class = JSXML_CLASS_TEXT; - } - - xml = js_NewXML(cx, xml_class); - if (!xml) - goto fail; - xml->name = qn; - if (pn->pn_type == TOK_XMLSPACE) - xml->xml_flags |= XMLF_WHITESPACE_TEXT; - xml->xml_value = str; - break; - - default: - goto syntax; - } - - JS_LeaveLocalRootScope(cx); - if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) - return NULL; - return xml; - -skip_child: - js_LeaveLocalRootScope(cx); - return PN2X_SKIP_CHILD; - -#undef PN2X_SKIP_CHILD - -syntax: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_XML_MARKUP); -fail: - JS_LeaveLocalRootScope(cx); - return NULL; -} - -/* - * XML helper, object-ops, and library functions. We start with the helpers, - * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. - */ -static JSBool -GetXMLSetting(JSContext *cx, const char *name, jsval *vp) -{ - jsval v; - - if (!js_FindConstructor(cx, NULL, js_XML_str, &v)) - return JS_FALSE; - if (!JSVAL_IS_FUNCTION(cx, v)) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); -} - -static JSBool -FillSettingsCache(JSContext *cx) -{ - int i; - const char *name; - jsval v; - JSBool isSet; - - /* Note: XML_PRETTY_INDENT is not a boolean setting. */ - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - name = xml_static_props[i].name; - if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) - return JS_FALSE; - if (isSet) - cx->xmlSettingFlags |= JS_BIT(i); - else - cx->xmlSettingFlags &= ~JS_BIT(i); - } - - cx->xmlSettingFlags |= XSF_CACHE_VALID; - return JS_TRUE; -} - -static JSBool -GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) -{ - int i; - - if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) - return JS_FALSE; - - for (i = 0; xml_static_props[i].name; i++) { - if (!strcmp(xml_static_props[i].name, name)) { - *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; - return JS_TRUE; - } - } - *bp = JS_FALSE; - return JS_TRUE; -} - -static JSBool -GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) -{ - jsval v; - - return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); -} - -static JSBool -GetXMLSettingFlags(JSContext *cx, uintN *flagsp) -{ - JSBool flag; - - /* Just get the first flag to validate the setting flags cache. */ - if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) - return JS_FALSE; - *flagsp = cx->xmlSettingFlags; - return JS_TRUE; -} - -static JSXML * -ParseXMLSource(JSContext *cx, JSString *src) -{ - jsval nsval; - JSXMLNamespace *ns; - size_t urilen, srclen, length, offset, dstlen; - jschar *chars; - const jschar *srcp, *endp; - void *mark; - JSTokenStream *ts; - uintN lineno; - JSStackFrame *fp; - JSOp op; - JSParseNode *pn; - JSXML *xml; - JSXMLArray nsarray; - uintN flags; - - static const char prefix[] = "<parent xmlns='"; - static const char middle[] = "'>"; - static const char suffix[] = "</parent>"; - -#define constrlen(constr) (sizeof(constr) - 1) - - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return NULL; - ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - - urilen = JSSTRING_LENGTH(ns->uri); - srclen = JSSTRING_LENGTH(src); - length = constrlen(prefix) + urilen + constrlen(middle) + srclen + - constrlen(suffix); - - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return NULL; - - dstlen = length; - js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); - offset = dstlen; - js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); - offset += urilen; - dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen); - offset += dstlen; - srcp = JSSTRING_CHARS(src); - js_strncpy(chars + offset, srcp, srclen); - offset += srclen; - dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen); - chars [offset + dstlen] = 0; - - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewBufferTokenStream(cx, chars, length); - if (!ts) - return NULL; - for (fp = cx->fp; fp && !fp->pc; fp = fp->down) - continue; - if (fp) { - op = (JSOp) *fp->pc; - if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { - ts->filename = fp->script->filename; - lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - for (endp = srcp + srclen; srcp < endp; srcp++) - if (*srcp == '\n') - --lineno; - ts->lineno = lineno; - } - } - - JS_KEEP_ATOMS(cx->runtime); - pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); - xml = NULL; - if (pn && XMLArrayInit(cx, &nsarray, 1)) { - if (GetXMLSettingFlags(cx, &flags)) - xml = ParseNodeToXML(cx, pn, &nsarray, flags); - - XMLArrayFinish(cx, &nsarray); - } - JS_UNKEEP_ATOMS(cx->runtime); - - JS_ARENA_RELEASE(&cx->tempPool, mark); - JS_free(cx, chars); - return xml; - -#undef constrlen -} - -/* - * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). - * - * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce - * the constraint: - * - * for all x belonging to XML: - * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] - * - * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here - * (in new sub-step 6(a), renumbering the others to (b) and (c)). - * - * Same goes for 10.4.1 Step 7(a). - * - * In order for XML.prototype.namespaceDeclarations() to work correctly, the - * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be - * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such - * undeclared namespaces associated with x not belonging to ancestorNS. - */ -static JSXML * -OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) -{ - JSXMLNamespace *ns; - - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); - xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!ns || !xml) - return xml; - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - return NULL; - ns->declared = JS_FALSE; - } - xml->parent = NULL; - return xml; -} - -static JSObject * -ToXML(JSContext *cx, jsval v) -{ - JSObject *obj; - JSXML *xml; - JSClass *clasp; - JSString *str; - uint32 length; - - if (JSVAL_IS_PRIMITIVE(v)) { - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - goto bad; - } else { - obj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, obj)) { - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length != 1) - goto bad; - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - return js_GetXMLObject(cx, xml); - } - } - return obj; - } - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { - JS_ASSERT(0); - } - - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { - goto bad; - } - } - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - if (IS_EMPTY(str)) { - length = 0; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - xml = NULL; -#endif - } else { - xml = ParseXMLSource(cx, str); - if (!xml) - return NULL; - length = JSXML_LENGTH(xml); - } - - if (length == 0) { - obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); - if (!obj) - return NULL; - } else if (length == 1) { - xml = OrphanXMLChild(cx, xml, 0); - if (!xml) - return NULL; - obj = js_GetXMLObject(cx, xml); - if (!obj) - return NULL; - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); - return NULL; - } - return obj; - -bad: - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_CONVERSION, - JS_GetStringBytes(str)); - } - return NULL; -} - -static JSBool -Append(JSContext *cx, JSXML *list, JSXML *kid); - -static JSObject * -ToXMLList(JSContext *cx, jsval v) -{ - JSObject *obj, *listobj; - JSXML *xml, *list, *kid; - JSClass *clasp; - JSString *str; - uint32 i, length; - - if (JSVAL_IS_PRIMITIVE(v)) { - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - goto bad; - } else { - obj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, obj)) { - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class != JSXML_CLASS_LIST) { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (!Append(cx, list, xml)) - return NULL; - return listobj; - } - return obj; - } - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { - JS_ASSERT(0); - } - - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { - goto bad; - } - } - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - if (IS_EMPTY(str)) { - xml = NULL; - length = 0; - } else { - if (!JS_EnterLocalRootScope(cx)) - return NULL; - xml = ParseXMLSource(cx, str); - if (!xml) { - JS_LeaveLocalRootScope(cx); - return NULL; - } - length = JSXML_LENGTH(xml); - } - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (listobj) { - list = (JSXML *) JS_GetPrivate(cx, listobj); - for (i = 0; i < length; i++) { - kid = OrphanXMLChild(cx, xml, i); - if (!kid) - return NULL; - if (!Append(cx, list, kid)) { - listobj = NULL; - break; - } - } - } - - if (xml) - JS_LeaveLocalRootScope(cx); - return listobj; - -bad: - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XMLLIST_CONVERSION, - JS_GetStringBytes(str)); - } - return NULL; -} - -/* - * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString - * and their library-public js_* counterparts. The guts of MakeXMLCDataString, - * MakeXMLCommentString, and MakeXMLPIString are further factored into a common - * MakeXMLSpecialString subroutine. - * - * These functions take ownership of sb->base, if sb is non-null, in all cases - * of success or failure. - */ -static JSString * -MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, - JSString *str, JSString *str2, - const jschar *prefix, size_t prefixlength, - const jschar *suffix, size_t suffixlength) -{ - JSStringBuffer localSB; - size_t length, length2, newlength; - jschar *bp, *base; - - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - - length = JSSTRING_LENGTH(str); - length2 = str2 ? JSSTRING_LENGTH(str2) : 0; - newlength = STRING_BUFFER_OFFSET(sb) + - prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + - suffixlength; - bp = base = (jschar *) - JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); - if (!bp) { - js_FinishStringBuffer(sb); - return NULL; - } - - bp += STRING_BUFFER_OFFSET(sb); - js_strncpy(bp, prefix, prefixlength); - bp += prefixlength; - js_strncpy(bp, JSSTRING_CHARS(str), length); - bp += length; - if (length2 != 0) { - *bp++ = (jschar) ' '; - js_strncpy(bp, JSSTRING_CHARS(str2), length2); - bp += length2; - } - js_strncpy(bp, suffix, suffixlength); - bp[suffixlength] = 0; - - str = js_NewString(cx, base, newlength, 0); - if (!str) - free(base); - return str; -} - -static JSString * -MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', - 'C', 'D', 'A', 'T', 'A', - '['}; - static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; - - return MakeXMLSpecialString(cx, sb, str, NULL, - cdata_prefix_ucNstr, 9, - cdata_suffix_ucNstr, 3); -} - -static JSString * -MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; - static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; - - return MakeXMLSpecialString(cx, sb, str, NULL, - comment_prefix_ucNstr, 4, - comment_suffix_ucNstr, 3); -} - -static JSString * -MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, - JSString *value) -{ - static const jschar pi_prefix_ucNstr[] = {'<', '?'}; - static const jschar pi_suffix_ucNstr[] = {'?', '>'}; - - return MakeXMLSpecialString(cx, sb, name, value, - pi_prefix_ucNstr, 2, - pi_suffix_ucNstr, 2); -} - -/* - * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends - * equals, a double quote, an attribute value, and a closing double quote. - */ -static void -AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) -{ - js_AppendCString(sb, "=\""); - valstr = js_EscapeAttributeValue(cx, valstr); - if (!valstr) { - free(sb->base); - sb->base = STRING_BUFFER_ERROR_BASE; - return; - } - js_AppendJSString(sb, valstr); - js_AppendChar(sb, '"'); -} - -/* - * ECMA-357 10.2.1.1 EscapeElementValue helper method. - * - * This function takes ownership of sb->base, if sb is non-null, in all cases - * of success or failure. - */ -static JSString * -EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - size_t length, newlength; - const jschar *cp, *start, *end; - jschar c; - - length = newlength = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (c == '<' || c == '>') - newlength += 3; - else if (c == '&') - newlength += 4; - - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { - JSStringBuffer localSB; - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - if (!sb->grow(sb, newlength)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - for (cp = start; cp < end; cp++) { - c = *cp; - if (c == '<') - js_AppendCString(sb, js_lt_entity_str); - else if (c == '>') - js_AppendCString(sb, js_gt_entity_str); - else if (c == '&') - js_AppendCString(sb, js_amp_entity_str); - else - js_AppendChar(sb, c); - } - JS_ASSERT(STRING_BUFFER_OK(sb)); - str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); - if (!str) - js_FinishStringBuffer(sb); - } - return str; -} - -/* - * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. - * This function takes ownership of sb->base, if sb is non-null, in all cases. - */ -static JSString * -EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - size_t length, newlength; - const jschar *cp, *start, *end; - jschar c; - - length = newlength = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (c == '"') - newlength += 5; - else if (c == '<') - newlength += 3; - else if (c == '&' || c == '\n' || c == '\r' || c == '\t') - newlength += 4; - - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { - JSStringBuffer localSB; - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - if (!sb->grow(sb, newlength)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - for (cp = start; cp < end; cp++) { - c = *cp; - if (c == '"') - js_AppendCString(sb, js_quot_entity_str); - else if (c == '<') - js_AppendCString(sb, js_lt_entity_str); - else if (c == '&') - js_AppendCString(sb, js_amp_entity_str); - else if (c == '\n') - js_AppendCString(sb, "
"); - else if (c == '\r') - js_AppendCString(sb, "
"); - else if (c == '\t') - js_AppendCString(sb, "	"); - else - js_AppendChar(sb, c); - } - JS_ASSERT(STRING_BUFFER_OK(sb)); - str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); - if (!str) - js_FinishStringBuffer(sb); - } - return str; -} - -/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ -static JSXMLNamespace * -GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) -{ - JSXMLNamespace *match, *ns; - uint32 i, n; - jsval argv[2]; - JSObject *nsobj; - - JS_ASSERT(qn->uri); - if (!qn->uri) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAMESPACE, - qn->prefix - ? js_ValueToPrintableString(cx, - STRING_TO_JSVAL(qn->prefix)) - : js_type_str[JSTYPE_VOID]); - return NULL; - } - - /* Look for a matching namespace in inScopeNSes, if provided. */ - match = NULL; - if (inScopeNSes) { - for (i = 0, n = inScopeNSes->length; i < n; i++) { - ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); - if (!ns) - continue; - - /* - * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: - * If we preserve prefixes, we must match null qn->prefix against - * an empty ns->prefix, in order to avoid generating redundant - * prefixed and default namespaces for cases such as: - * - * x = <t xmlns="http://foo.com"/> - * print(x.toXMLString()); - * - * Per 10.3.2.1, the namespace attribute in t has an empty string - * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): - * - * 1. If the [local name] property of a is "xmlns" - * a. Map ns.prefix to the empty string - * - * But t's name has a null prefix in this implementation, meaning - * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to - * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without - * saying how "no value" maps to an ECMA-357 value -- but it must - * map to the *undefined* prefix value). - * - * Since "" != undefined (or null, in the current implementation) - * the ECMA-357 spec will fail to match in [[GetNamespace]] called - * on t with argument {} U {(prefix="", uri="http://foo.com")}. - * This spec bug leads to ToXMLString results that duplicate the - * declared namespace. - */ - if (!js_CompareStrings(ns->uri, qn->uri) && - (ns->prefix == qn->prefix || - ((ns->prefix && qn->prefix) - ? !js_CompareStrings(ns->prefix, qn->prefix) - : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { - match = ns; - break; - } - } - } - - /* If we didn't match, make a new namespace from qn. */ - if (!match) { - argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; - argv[1] = STRING_TO_JSVAL(qn->uri); - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, - 2, argv); - if (!nsobj) - return NULL; - match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - } - return match; -} - -static JSString * -GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) -{ - const jschar *cp, *start, *end; - size_t length, newlength, offset; - uint32 i, n, m, serial; - jschar *bp, *dp; - JSBool done; - JSXMLNamespace *ns; - JSString *prefix; - - JS_ASSERT(!IS_EMPTY(uri)); - - /* - * Try peeling off the last filename suffix or pathname component till - * we have a valid XML name. This heuristic will prefer "xul" given - * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any - * likely URI of the form ".../xbl2/2005". - */ - start = JSSTRING_CHARS(uri); - cp = end = start + JSSTRING_LENGTH(uri); - while (--cp > start) { - if (*cp == '.' || *cp == '/' || *cp == ':') { - ++cp; - if (IsXMLName(cp, PTRDIFF(end, cp, jschar))) - break; - end = --cp; - } - } - length = PTRDIFF(end, cp, jschar); - - /* - * Now search through decls looking for a collision. If we collide with - * an existing prefix, start tacking on a hyphen and a serial number. - */ - serial = 0; - bp = NULL; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - newlength = 0; -#endif - do { - done = JS_TRUE; - for (i = 0, n = decls->length; i < n; i++) { - ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); - if (ns && ns->prefix && - JSSTRING_LENGTH(ns->prefix) == length && - !memcmp(JSSTRING_CHARS(ns->prefix), cp, - length * sizeof(jschar))) { - if (!bp) { - newlength = length + 2 + (size_t) log10(n); - bp = (jschar *) - JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!bp) - return NULL; - js_strncpy(bp, cp, length); - } - - ++serial; - JS_ASSERT(serial <= n); - dp = bp + length + 2 + (size_t) log10(serial); - *dp = 0; - for (m = serial; m != 0; m /= 10) - *--dp = (jschar)('0' + m % 10); - *--dp = '-'; - JS_ASSERT(dp == bp + length); - - done = JS_FALSE; - break; - } - } - } while (!done); - - if (!bp) { - offset = PTRDIFF(cp, start, jschar); - prefix = js_NewDependentString(cx, uri, offset, length, 0); - } else { - prefix = js_NewString(cx, bp, newlength, 0); - if (!prefix) - JS_free(cx, bp); - } - return prefix; -} - -static JSBool -namespace_match(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsb->prefix) - return nsa->prefix && !js_CompareStrings(nsa->prefix, nsb->prefix); - return !js_CompareStrings(nsa->uri, nsb->uri); -} - -/* ECMA-357 10.2.1 and 10.2.2 */ -static JSString * -XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, - uintN indentLevel) -{ - JSBool pretty, indentKids; - JSStringBuffer sb; - JSString *str, *prefix, *kidstr; - JSXMLArrayCursor cursor; - uint32 i, n; - JSXMLArray empty, decls, ancdecls; - JSXMLNamespace *ns, *ns2; - uintN nextIndentLevel; - JSXML *attr, *kid; - - if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) - return NULL; - - js_InitStringBuffer(&sb); - if (pretty) - js_RepeatChar(&sb, ' ', indentLevel); - str = NULL; - - switch (xml->xml_class) { - case JSXML_CLASS_TEXT: - /* Step 4. */ - if (pretty) { - str = ChompXMLWhitespace(cx, xml->xml_value); - if (!str) - return NULL; - } else { - str = xml->xml_value; - } - return EscapeElementValue(cx, &sb, str); - - case JSXML_CLASS_ATTRIBUTE: - /* Step 5. */ - return EscapeAttributeValue(cx, &sb, xml->xml_value); - - case JSXML_CLASS_COMMENT: - /* Step 6. */ - return MakeXMLCommentString(cx, &sb, xml->xml_value); - - case JSXML_CLASS_PROCESSING_INSTRUCTION: - /* Step 7. */ - return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); - - case JSXML_CLASS_LIST: - /* ECMA-357 10.2.2. */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - i = 0; - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (pretty && i != 0) - js_AppendChar(&sb, '\n'); - - kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); - if (!kidstr) - break; - - js_AppendJSString(&sb, kidstr); - ++i; - } - XMLArrayCursorFinish(&cursor); - if (kid) - goto list_out; - - if (!sb.base) { - if (!STRING_BUFFER_OK(&sb)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - return cx->runtime->emptyString; - } - - str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); - list_out: - if (!str) - js_FinishStringBuffer(&sb); - return str; - - default:; - } - - /* After this point, control must flow through label out: to exit. */ - if (!JS_EnterLocalRootScope(cx)) - return NULL; - - /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ - if (!ancestorNSes) { - XMLArrayInit(cx, &empty, 0); - ancestorNSes = ∅ - } - XMLArrayInit(cx, &decls, 0); - ancdecls.capacity = 0; - - /* Clone in-scope namespaces not in ancestorNSes into decls. */ - XMLArrayCursorInit(&cursor, &xml->xml_namespaces); - while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { - if (!ns->declared) - continue; - if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { - /* NOTE: may want to exclude unused namespaces here. */ - ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); - if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) - break; - } - } - XMLArrayCursorFinish(&cursor); - if (ns) - goto out; - - /* - * Union ancestorNSes and decls into ancdecls. Note that ancdecls does - * not own its member references. In the spec, ancdecls has no name, but - * is always written out as (AncestorNamespaces U namespaceDeclarations). - */ - if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) - goto out; - for (i = 0, n = ancestorNSes->length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); - if (!ns2) - continue; - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) - goto out; - } - for (i = 0, n = decls.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); - if (!ns2) - continue; - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) - goto out; - } - - /* Step 11, except we don't clone ns unless its prefix is undefined. */ - ns = GetNamespace(cx, xml->name, &ancdecls); - if (!ns) - goto out; - - /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ - if (!ns->prefix) { - /* - * Create a namespace prefix that isn't used by any member of decls. - * Assign the new prefix to a copy of ns. Flag this namespace as if - * it were declared, for assertion-testing's sake later below. - * - * Erratum: if ns->prefix and xml->name are both null (*undefined* in - * ECMA-357), we know that xml was named using the default namespace - * (proof: see GetNamespace and the Namespace constructor called with - * two arguments). So we ought not generate a new prefix here, when - * we can declare ns as the default namespace for xml. - * - * This helps descendants inherit the namespace instead of redundantly - * redeclaring it with generated prefixes in each descendant. - */ - if (!xml->name->prefix) { - prefix = cx->runtime->emptyString; - } else { - prefix = GeneratePrefix(cx, ns->uri, &ancdecls); - if (!prefix) - goto out; - } - ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); - if (!ns) - goto out; - - /* - * If the xml->name was unprefixed, we must remove any declared default - * namespace from decls before appending ns. How can you get a default - * namespace in decls that doesn't match the one from name? Apparently - * by calling x.setNamespace(ns) where ns has no prefix. The other way - * to fix this is to update x's in-scope namespaces when setNamespace - * is called, but that's not specified by ECMA-357. - * - * Likely Erratum here, depending on whether the lack of update to x's - * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an - * erratum or not. Note that changing setNamespace to update the list - * of in-scope namespaces will change x.namespaceDeclarations(). - */ - if (IS_EMPTY(prefix)) { - i = XMLArrayFindMember(&decls, ns, namespace_match); - if (i != XML_NOT_FOUND) - XMLArrayDelete(cx, &decls, i, JS_TRUE); - } - - /* - * In the spec, ancdecls has no name, but is always written out as - * (AncestorNamespaces U namespaceDeclarations). Since we compute - * that union in ancdecls, any time we append a namespace strong - * ref to decls, we must also append a weak ref to ancdecls. Order - * matters here: code at label out: releases strong refs in decls. - */ - if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || - !XMLARRAY_APPEND(cx, &decls, ns)) { - goto out; - } - } - - /* Format the element or point-tag into sb. */ - js_AppendChar(&sb, '<'); - - if (ns->prefix && !IS_EMPTY(ns->prefix)) { - js_AppendJSString(&sb, ns->prefix); - js_AppendChar(&sb, ':'); - } - js_AppendJSString(&sb, xml->name->localName); - - /* - * Step 16 makes a union to avoid writing two loops in step 17, to share - * common attribute value appending spec-code. We prefer two loops for - * faster code and less data overhead. - */ - - /* Step 17(c): append XML namespace declarations. */ - XMLArrayCursorInit(&cursor, &decls); - while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { - JS_ASSERT(ns2->declared); - - js_AppendCString(&sb, " xmlns"); - - /* 17(c)(ii): NULL means *undefined* here. */ - if (!ns2->prefix) { - prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); - if (!prefix) - break; - ns2->prefix = prefix; - } - - /* 17(c)(iii). */ - if (!IS_EMPTY(ns2->prefix)) { - js_AppendChar(&sb, ':'); - js_AppendJSString(&sb, ns2->prefix); - } - - /* 17(d-g). */ - AppendAttributeValue(cx, &sb, ns2->uri); - } - XMLArrayCursorFinish(&cursor); - if (ns2) - goto out; - - /* Step 17(b): append attributes. */ - XMLArrayCursorInit(&cursor, &xml->xml_attrs); - while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - js_AppendChar(&sb, ' '); - ns2 = GetNamespace(cx, attr->name, &ancdecls); - if (!ns2) - break; - - /* 17(b)(ii): NULL means *undefined* here. */ - if (!ns2->prefix) { - prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); - if (!prefix) - break; - - /* Again, we avoid copying ns2 until we know it's prefix-less. */ - ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); - if (!ns2) - break; - - /* - * In the spec, ancdecls has no name, but is always written out as - * (AncestorNamespaces U namespaceDeclarations). Since we compute - * that union in ancdecls, any time we append a namespace strong - * ref to decls, we must also append a weak ref to ancdecls. Order - * matters here: code at label out: releases strong refs in decls. - */ - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || - !XMLARRAY_APPEND(cx, &decls, ns2)) { - break; - } - } - - /* 17(b)(iii). */ - if (!IS_EMPTY(ns2->prefix)) { - js_AppendJSString(&sb, ns2->prefix); - js_AppendChar(&sb, ':'); - } - - /* 17(b)(iv). */ - js_AppendJSString(&sb, attr->name->localName); - - /* 17(d-g). */ - AppendAttributeValue(cx, &sb, attr->xml_value); - } - XMLArrayCursorFinish(&cursor); - if (attr) - goto out; - - /* Step 18: handle point tags. */ - n = xml->xml_kids.length; - if (n == 0) { - js_AppendCString(&sb, "/>"); - } else { - /* Steps 19 through 25: handle element content, and open the end-tag. */ - js_AppendChar(&sb, '>'); - indentKids = n > 1 || - (n == 1 && - (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && - kid->xml_class != JSXML_CLASS_TEXT); - - if (pretty && indentKids) { - if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) - goto out; - nextIndentLevel = indentLevel + i; - } else { - nextIndentLevel = 0; - } - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (pretty && indentKids) - js_AppendChar(&sb, '\n'); - - kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); - if (!kidstr) - break; - - js_AppendJSString(&sb, kidstr); - } - XMLArrayCursorFinish(&cursor); - if (kid) - goto out; - - if (pretty && indentKids) { - js_AppendChar(&sb, '\n'); - js_RepeatChar(&sb, ' ', indentLevel); - } - js_AppendCString(&sb, "</"); - - /* Step 26. */ - if (ns->prefix && !IS_EMPTY(ns->prefix)) { - js_AppendJSString(&sb, ns->prefix); - js_AppendChar(&sb, ':'); - } - - /* Step 27. */ - js_AppendJSString(&sb, xml->name->localName); - js_AppendChar(&sb, '>'); - } - - if (!STRING_BUFFER_OK(&sb)) { - JS_ReportOutOfMemory(cx); - goto out; - } - - str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); -out: - JS_LeaveLocalRootScope(cx); - if (!str && STRING_BUFFER_OK(&sb)) - js_FinishStringBuffer(&sb); - XMLArrayFinish(cx, &decls); - if (ancdecls.capacity != 0) - XMLArrayFinish(cx, &ancdecls); - return str; -} - -/* ECMA-357 10.2 */ -static JSString * -ToXMLString(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - JSXML *xml; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_CONVERSION, - js_type_str[JSVAL_IS_NULL(v) - ? JSTYPE_NULL - : JSTYPE_VOID]); - return NULL; - } - - if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) - return js_ValueToString(cx, v); - - if (JSVAL_IS_STRING(v)) - return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); - - obj = JSVAL_TO_OBJECT(v); - if (!OBJECT_IS_XML(cx, obj)) { - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) - return NULL; - str = js_ValueToString(cx, v); - if (!str) - return NULL; - return EscapeElementValue(cx, NULL, str); - } - - /* Handle non-element cases in this switch, returning from each case. */ - xml = (JSXML *) JS_GetPrivate(cx, obj); - return XMLToXMLString(cx, xml, NULL, 0); -} - -static JSXMLQName * -ToAttributeName(JSContext *cx, jsval v) -{ - JSString *name, *uri, *prefix; - JSObject *obj; - JSClass *clasp; - JSXMLQName *qn; - JSTempValueRooter tvr; - - if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); - uri = prefix = cx->runtime->emptyString; - } else { - if (JSVAL_IS_PRIMITIVE(v)) { - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (name) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_ATTR_NAME, - JS_GetStringBytes(name)); - } - return NULL; - } - - obj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass) - return (JSXMLQName *) JS_GetPrivate(cx, obj); - - if (clasp == &js_QNameClass.base) { - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - uri = qn->uri; - prefix = qn->prefix; - name = qn->localName; - } else { - if (clasp == &js_AnyNameClass) { - name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); - } else { - name = js_ValueToString(cx, v); - if (!name) - return NULL; - } - uri = prefix = cx->runtime->emptyString; - } - } - - qn = js_NewXMLQName(cx, uri, prefix, name); - if (!qn) - return NULL; - - /* - * Temp and local root scope APIs take GC-thing pointers tagged as jsvals - * and blindly untag. Since qn is a GC-thing pointer, we can treat it as - * an object pointer. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(qn), &tvr); - obj = js_GetAttributeNameObject(cx, qn); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!obj) - return NULL; - return qn; -} - -static JSXMLQName * -ToXMLName(JSContext *cx, jsval v, jsid *funidp) -{ - JSString *name; - JSObject *obj; - JSClass *clasp; - uint32 index; - JSXMLQName *qn; - JSAtom *atom; - - if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); - } else { - if (JSVAL_IS_PRIMITIVE(v)) { - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (name) - goto bad; - return NULL; - } - - obj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) - goto out; - if (clasp == &js_AnyNameClass) { - name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); - goto construct; - } - name = js_ValueToString(cx, v); - if (!name) - return NULL; - } - - /* - * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: - * - * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception - * - * First, _P_ should be _s_, to refer to the given string. - * - * Second, why does ToXMLName applied to the string type throw TypeError - * only for numeric literals without any leading or trailing whitespace? - * - * If the idea is to reject uint32 property names, then the check needs to - * be stricter, to exclude hexadecimal and floating point literals. - */ - if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) - goto bad; - - if (*JSSTRING_CHARS(name) == '@') { - name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); - if (!name) - return NULL; - *funidp = 0; - return ToAttributeName(cx, STRING_TO_JSVAL(name)); - } - -construct: - v = STRING_TO_JSVAL(name); - obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); - if (!obj) - return NULL; - -out: - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; - if (qn->uri && atom && - (qn->uri == ATOM_TO_STRING(atom) || - !js_CompareStrings(qn->uri, ATOM_TO_STRING(atom)))) { - if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) - return NULL; - } else { - *funidp = 0; - } - return qn; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAME, - js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); - return NULL; -} - -/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ -static JSBool -AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) -{ - JSXMLNamespace *match, *ns2; - uint32 i, n, m; - - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ - if (!ns->prefix) { - match = NULL; - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && !js_CompareStrings(ns2->uri, ns->uri)) { - match = ns2; - break; - } - } - if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) - return JS_FALSE; - } else { - if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) - return JS_TRUE; - match = NULL; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - m = XML_NOT_FOUND; -#endif - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && ns2->prefix && - !js_CompareStrings(ns2->prefix, ns->prefix)) { - match = ns2; - m = i; - break; - } - } - if (match && js_CompareStrings(match->uri, ns->uri)) { - ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, - JSXMLNamespace); - JS_ASSERT(ns2 == match); - match->prefix = NULL; - if (!AddInScopeNamespace(cx, xml, match)) - return JS_FALSE; - } - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - return JS_FALSE; - } - - /* OPTION: enforce that descendants have superset namespaces. */ - return JS_TRUE; -} - -/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ -static JSBool -Append(JSContext *cx, JSXML *list, JSXML *xml) -{ - uint32 i, j, k, n; - JSXML *kid; - - JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); - i = list->xml_kids.length; - n = 1; - if (xml->xml_class == JSXML_CLASS_LIST) { - list->xml_target = xml->xml_target; - list->xml_targetprop = xml->xml_targetprop; - n = JSXML_LENGTH(xml); - k = i + n; - if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) - return JS_FALSE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); - if (kid) - XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); - } - return JS_TRUE; - } - - list->xml_target = xml->parent; - if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - list->xml_targetprop = NULL; - else - list->xml_targetprop = xml->name; - if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) - return JS_FALSE; - return JS_TRUE; -} - -/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ -static JSXML * -DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); - -static JSXML * -DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) -{ - JSXML *copy; - JSBool ok; - - /* Our caller may not be protecting newborns with a local root scope. */ - if (!JS_EnterLocalRootScope(cx)) - return NULL; - copy = DeepCopyInLRS(cx, xml, flags); - if (copy) { - if (obj) { - /* Caller provided the object for this copy, hook 'em up. */ - ok = JS_SetPrivate(cx, obj, copy); - if (ok) - copy->object = obj; - } else { - ok = js_GetXMLObject(cx, copy) != NULL; - } - if (!ok) - copy = NULL; - } - JS_LeaveLocalRootScope(cx); - return copy; -} - -/* - * (i) We must be in a local root scope (InLRS). - * (ii) parent must have a rooted object. - * (iii) from's owning object must be locked if not thread-local. - */ -static JSBool -DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, - uintN flags) -{ - uint32 j, n; - JSXMLArrayCursor cursor; - JSBool ok; - JSXML *kid, *kid2; - JSString *str; - - JS_ASSERT(cx->localRootStack); - - n = from->length; - if (!XMLArraySetCapacity(cx, to, n)) - return JS_FALSE; - - XMLArrayCursorInit(&cursor, from); - j = 0; - ok = JS_TRUE; - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if ((flags & XSF_IGNORE_COMMENTS) && - kid->xml_class == JSXML_CLASS_COMMENT) { - continue; - } - if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && - kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { - continue; - } - if ((flags & XSF_IGNORE_WHITESPACE) && - (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { - continue; - } - kid2 = DeepCopyInLRS(cx, kid, flags); - if (!kid2) { - to->length = j; - ok = JS_FALSE; - break; - } - - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid2->xml_value); - if (!str) { - to->length = j; - ok = JS_FALSE; - break; - } - kid2->xml_value = str; - } - - XMLARRAY_SET_MEMBER(to, j, kid2); - ++j; - if (parent->xml_class != JSXML_CLASS_LIST) - kid2->parent = parent; - } - XMLArrayCursorFinish(&cursor); - if (!ok) - return JS_FALSE; - - if (j < n) - XMLArrayTrim(to); - return JS_TRUE; -} - -static JSXML * -DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) -{ - JSXML *copy; - JSXMLQName *qn; - JSBool ok; - uint32 i, n; - JSXMLNamespace *ns, *ns2; - - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(cx->localRootStack); - - copy = js_NewXML(cx, xml->xml_class); - if (!copy) - return NULL; - qn = xml->name; - if (qn) { - qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); - if (!qn) { - ok = JS_FALSE; - goto out; - } - } - copy->name = qn; - copy->xml_flags = xml->xml_flags; - - if (JSXML_HAS_VALUE(xml)) { - copy->xml_value = xml->xml_value; - ok = JS_TRUE; - } else { - ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); - if (!ok) - goto out; - - if (xml->xml_class == JSXML_CLASS_LIST) { - copy->xml_target = xml->xml_target; - copy->xml_targetprop = xml->xml_targetprop; - } else { - n = xml->xml_namespaces.length; - ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); - if (!ok) - goto out; - for (i = 0; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); - if (!ns2) { - copy->xml_namespaces.length = i; - ok = JS_FALSE; - goto out; - } - XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); - } - - ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, - 0); - if (!ok) - goto out; - } - } - -out: - if (!ok) - return NULL; - return copy; -} - -static void -ReportBadXMLName(JSContext *cx, jsval id) -{ - JSString *name; - - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); - if (name) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAME, - JS_GetStringBytes(name)); - } -} - -/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ -static JSBool -DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) -{ - uint32 index; - JSXML *kid; - - if (!js_IdIsIndex(id, &index)) { - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (kid) - kid->parent = NULL; - XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); - } - - *vp = JSVAL_TRUE; - return JS_TRUE; -} - -typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); - -static JSBool -MatchAttrName(JSXMLQName *nameqn, JSXML *attr) -{ - JSXMLQName *attrqn = attr->name; - - return (IS_STAR(nameqn->localName) || - !js_CompareStrings(attrqn->localName, nameqn->localName)) && - (!nameqn->uri || - !js_CompareStrings(attrqn->uri, nameqn->uri)); -} - -static JSBool -MatchElemName(JSXMLQName *nameqn, JSXML *elem) -{ - return (IS_STAR(nameqn->localName) || - (elem->xml_class == JSXML_CLASS_ELEMENT && - !js_CompareStrings(elem->name->localName, nameqn->localName))) && - (!nameqn->uri || - (elem->xml_class == JSXML_CLASS_ELEMENT && - !js_CompareStrings(elem->name->uri, nameqn->uri))); -} - -/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ -static JSBool -DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) -{ - uint32 i, n; - JSXML *attr, *kid; - - if (xml->xml_class == JSXML_CLASS_ELEMENT && - OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (attr && MatchAttrName(nameqn, attr)) { - if (!Append(cx, list, attr)) - return JS_FALSE; - } - } - } - - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - continue; - if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && - MatchElemName(nameqn, kid)) { - if (!Append(cx, list, kid)) - return JS_FALSE; - } - if (!DescendantsHelper(cx, kid, nameqn, list)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSXML * -Descendants(JSContext *cx, JSXML *xml, jsval id) -{ - jsid funid; - JSXMLQName *nameqn; - JSObject *listobj; - JSXML *list, *kid; - uint32 i, n; - JSBool ok; - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return NULL; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (funid) - return list; - - /* - * Protect nameqn's object and strings from GC by linking list to it - * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, - * which protects list. Any other object allocations occuring beneath - * DescendantsHelper use local roots. - */ - list->name = nameqn; - if (!JS_EnterLocalRootScope(cx)) - return NULL; - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = DescendantsHelper(cx, kid, nameqn, list); - if (!ok) - break; - } - } - } else { - ok = DescendantsHelper(cx, xml, nameqn, list); - } - JS_LeaveLocalRootScope(cx); - if (!ok) - return NULL; - list->name = NULL; - return list; -} - -static JSBool -xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -/* Recursive (JSXML *) parameterized version of Equals. */ -static JSBool -XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) -{ - JSXMLQName *qn, *vqn; - uint32 i, j, n; - JSXMLArrayCursor cursor, vcursor; - JSXML *kid, *vkid, *attr, *vattr; - JSBool ok; - JSObject *xobj, *vobj; - -retry: - if (xml->xml_class != vxml->xml_class) { - if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) - goto retry; - } - if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { - vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); - if (vxml) - goto retry; - } - *bp = JS_FALSE; - return JS_TRUE; - } - - qn = xml->name; - vqn = vxml->name; - if (qn) { - *bp = vqn && - !js_CompareStrings(qn->localName, vqn->localName) && - !js_CompareStrings(qn->uri, vqn->uri); - } else { - *bp = vqn == NULL; - } - if (!*bp) - return JS_TRUE; - - if (JSXML_HAS_VALUE(xml)) { - *bp = !js_CompareStrings(xml->xml_value, vxml->xml_value); - } else if (xml->xml_kids.length != vxml->xml_kids.length) { - *bp = JS_FALSE; - } else { - XMLArrayCursorInit(&cursor, &xml->xml_kids); - XMLArrayCursorInit(&vcursor, &vxml->xml_kids); - for (;;) { - kid = (JSXML *) XMLArrayCursorNext(&cursor); - vkid = (JSXML *) XMLArrayCursorNext(&vcursor); - if (!kid || !vkid) { - *bp = !kid && !vkid; - ok = JS_TRUE; - break; - } - xobj = js_GetXMLObject(cx, kid); - vobj = js_GetXMLObject(cx, vkid); - ok = xobj && vobj && - xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); - if (!ok || !*bp) - break; - } - XMLArrayCursorFinish(&vcursor); - XMLArrayCursorFinish(&cursor); - if (!ok) - return JS_FALSE; - - if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { - n = xml->xml_attrs.length; - if (n != vxml->xml_attrs.length) - *bp = JS_FALSE; - for (i = 0; *bp && i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); - if (j == XML_NOT_FOUND) { - *bp = JS_FALSE; - break; - } - vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); - if (!vattr) - continue; - *bp = !js_CompareStrings(attr->xml_value, vattr->xml_value); - } - } - } - - return JS_TRUE; -} - -/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ -static JSBool -Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) -{ - JSObject *vobj; - JSXML *vxml; - - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length == 1) { - vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!vxml) - return JS_TRUE; - vobj = js_GetXMLObject(cx, vxml); - if (!vobj) - return JS_FALSE; - return js_XMLObjectOps.equality(cx, vobj, v, bp); - } - if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) - *bp = JS_TRUE; - } - } else { - vobj = JSVAL_TO_OBJECT(v); - if (!OBJECT_IS_XML(cx, vobj)) { - *bp = JS_FALSE; - } else { - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - if (!XMLEquals(cx, xml, vxml, bp)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) -{ - JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); - - do { - if (xml == kid) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CYCLIC_VALUE, js_XML_str); - return JS_FALSE; - } - } while ((xml = xml->parent) != NULL); - - return JS_TRUE; -} - -/* ECMA-357 9.1.1.11 XML [[Insert]]. */ -static JSBool -Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) -{ - uint32 j, n; - JSXML *vxml, *kid; - JSObject *vobj; - JSString *str; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - n = 1; - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) { - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - if (vxml->xml_class == JSXML_CLASS_LIST) { - n = vxml->xml_kids.length; - if (n == 0) - return JS_TRUE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); - if (!kid) - continue; - if (!CheckCycle(cx, xml, kid)) - return JS_FALSE; - } - } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { - /* OPTION: enforce that descendants have superset namespaces. */ - if (!CheckCycle(cx, xml, vxml)) - return JS_FALSE; - } - } - } - if (!vxml) { - str = js_ValueToString(cx, v); - if (!str) - return JS_FALSE; - - vxml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!vxml) - return JS_FALSE; - vxml->xml_value = str; - } - - if (i > xml->xml_kids.length) - i = xml->xml_kids.length; - - if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) - return JS_FALSE; - - if (vxml->xml_class == JSXML_CLASS_LIST) { - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); - if (!kid) - continue; - kid->parent = xml; - XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); - - /* OPTION: enforce that descendants have superset namespaces. */ - } - } else { - vxml->parent = xml; - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); - } - return JS_TRUE; -} - -static JSBool -IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) -{ - JSString *str; - - if (index <= JSVAL_INT_MAX) { - *idvp = INT_TO_JSVAL(index); - } else { - str = js_NumberToString(cx, (jsdouble) index); - if (!str) - return JS_FALSE; - *idvp = STRING_TO_JSVAL(str); - } - return JS_TRUE; -} - -/* ECMA-357 9.1.1.12 XML [[Replace]]. */ -static JSBool -Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) -{ - uint32 i, n; - JSXML *vxml, *kid; - JSObject *vobj; - jsval junk; - JSString *str; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - if (!js_IdIsIndex(id, &i)) { - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - /* - * 9.1.1.12 - * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. - * It should therefore constrain callers to pass in _i <= x.[[Length]]_. - */ - n = xml->xml_kids.length; - if (i >= n) { - if (!IndexToIdVal(cx, n, &id)) - return JS_FALSE; - i = n; - } - - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { - case JSXML_CLASS_ELEMENT: - /* OPTION: enforce that descendants have superset namespaces. */ - if (!CheckCycle(cx, xml, vxml)) - return JS_FALSE; - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - case JSXML_CLASS_TEXT: - goto do_replace; - - case JSXML_CLASS_LIST: - if (i < n && !DeleteByIndex(cx, xml, id, &junk)) - return JS_FALSE; - if (!Insert(cx, xml, i, v)) - return JS_FALSE; - break; - - default: - str = js_ValueToString(cx, v); - if (!str) - return JS_FALSE; - - vxml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!vxml) - return JS_FALSE; - vxml->xml_value = str; - - do_replace: - vxml->parent = xml; - if (i < n) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid) - kid->parent = NULL; - } - if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) - return JS_FALSE; - break; - } - - return JS_TRUE; -} - -/* Forward declared -- its implementation uses other statics that call it. */ -static JSBool -ResolveValue(JSContext *cx, JSXML *list, JSXML **result); - -/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ -static JSBool -DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *kid, *parent; - JSBool isIndex; - JSXMLArray *array; - uint32 length, index, deleteCount; - JSXMLQName *nameqn; - jsid funid; - JSObject *nameobj, *kidobj; - JSXMLNameMatcher matcher; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - isIndex = js_IdIsIndex(id, &index); - if (JSXML_HAS_KIDS(xml)) { - array = &xml->xml_kids; - length = array->length; - } else { - array = NULL; - length = 0; - } - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.3. */ - if (isIndex && index < length) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (!kid) - goto out; - parent = kid->parent; - if (parent) { - JS_ASSERT(parent != xml); - JS_ASSERT(JSXML_HAS_KIDS(parent)); - - if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { - nameqn = kid->name; - nameobj = js_GetAttributeNameObject(cx, nameqn); - if (!nameobj || !js_GetXMLObject(cx, parent)) - return JS_FALSE; - - id = OBJECT_TO_JSVAL(nameobj); - if (!DeleteProperty(cx, parent->object, id, vp)) - return JS_FALSE; - } else { - index = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(index != XML_NOT_FOUND); - if (!IndexToIdVal(cx, index, &id)) - return JS_FALSE; - if (!DeleteByIndex(cx, parent, id, vp)) - return JS_FALSE; - } - } - - XMLArrayDelete(cx, array, index, JS_TRUE); - } else { - for (index = 0; index < length; index++) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) - return JS_FALSE; - } - } - } - } else { - /* ECMA-357 9.1.1.3. */ - if (isIndex) { - /* See NOTE in spec: this variation is reserved for future use. */ - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - goto out; - nameobj = nameqn->object; - - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - if (xml->xml_class != JSXML_CLASS_ELEMENT) - goto out; - array = &xml->xml_attrs; - length = array->length; - matcher = MatchAttrName; - } else { - matcher = MatchElemName; - } - if (length != 0) { - deleteCount = 0; - for (index = 0; index < length; index++) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (kid && matcher(nameqn, kid)) { - kid->parent = NULL; - XMLArrayDelete(cx, array, index, JS_FALSE); - ++deleteCount; - } else if (deleteCount != 0) { - XMLARRAY_SET_MEMBER(array, - index - deleteCount, - array->vector[index]); - } - } - array->length -= deleteCount; - } - } - -out: - *vp = JSVAL_TRUE; - return JS_TRUE; -} - -/* - * Class compatibility mask flag bits stored in xml_methods[i].extra. If XML - * and XMLList are unified (an incompatible change to ECMA-357), then we don't - * need any of this. - */ -#define XML_MASK 0x1 -#define XMLLIST_MASK 0x2 -#define GENERIC_MASK (XML_MASK | XMLLIST_MASK) -#define CLASS_TO_MASK(c) (1 + ((c) == JSXML_CLASS_LIST)) - -static JSBool -GetFunction(JSContext *cx, JSObject *obj, JSXML *xml, jsid id, jsval *vp) -{ - JSFunction *fun; - - do { - /* XXXbe really want a separate scope for function::*. */ - if (!js_GetProperty(cx, obj, id, vp)) - return JS_FALSE; - if (JSVAL_IS_FUNCTION(cx, *vp)) { - if (xml && OBJECT_IS_XML(cx, obj)) { - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); - if (fun->spare && - (fun->spare & CLASS_TO_MASK(xml->xml_class)) == 0) { - /* XML method called on XMLList or vice versa. */ - *vp = JSVAL_VOID; - } - } - break; - } - } while ((obj = OBJ_GET_PROTO(cx, obj)) != NULL); - return JS_TRUE; -} - -static JSBool -SyncInScopeNamespaces(JSContext *cx, JSXML *xml) -{ - JSXMLArray *nsarray; - uint32 i, n; - JSXMLNamespace *ns; - - nsarray = &xml->xml_namespaces; - while ((xml = xml->parent) != NULL) { - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { - if (!XMLARRAY_APPEND(cx, nsarray, ns)) - return JS_FALSE; - } - } - } - return JS_TRUE; -} - -/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ -static JSBool -GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *list, *kid; - uint32 index; - JSObject *kidobj, *listobj, *nameobj; - JSXMLQName *nameqn; - jsid funid; - JSBool ok; - JSXMLArrayCursor cursor; - jsval kidval; - JSXMLArray *array; - JSXMLNameMatcher matcher; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) - return JS_TRUE; - -#ifdef __GNUC__ - list = NULL; /* quell GCC overwarning */ -#endif - -retry: - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.1 starts here. */ - if (js_IdIsIndex(id, &index)) { - /* - * Erratum: 9.2 is not completely clear that indexed properties - * correspond to kids, but that's what it seems to say, and it's - * what any sane user would want. - */ - if (index < xml->xml_kids.length) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - - *vp = OBJECT_TO_JSVAL(kidobj); - } else { - *vp = JSVAL_VOID; - } - return JS_TRUE; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return GetFunction(cx, obj, xml, funid, vp); - - /* - * Recursion through GetProperty may allocate more list objects, so - * we make use of local root scopes here. Each new allocation will - * push the newborn onto the local root stack. - */ - ok = JS_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - /* - * NB: nameqn is already protected from GC by cx->newborn[GCX_OBJECT] - * until listobj is created. After that, a local root keeps listobj - * alive, and listobj's private keeps nameqn alive via targetprop. - */ - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - } else { - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) { - ok = JS_FALSE; - break; - } - ok = GetProperty(cx, kidobj, id, &kidval); - if (!ok) - break; - kidobj = JSVAL_TO_OBJECT(kidval); - kid = (JSXML *) JS_GetPrivate(cx, kidobj); - if (JSXML_LENGTH(kid) > 0) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } - } else { - /* ECMA-357 9.1.1.1 starts here. */ - if (js_IdIsIndex(id, &index)) { - obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); - if (!obj) - return JS_FALSE; - xml = (JSXML *) JS_GetPrivate(cx, obj); - goto retry; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return GetFunction(cx, obj, xml, funid, vp); - nameobj = nameqn->object; - - /* - * Recursion through GetProperty may allocate more list objects, so - * we make use of local root scopes here. Each new allocation will - * push the newborn onto the local root stack. - */ - ok = JS_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - } else { - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - - if (JSXML_HAS_KIDS(xml)) { - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - XMLArrayCursorInit(&cursor, array); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (matcher(nameqn, kid)) { - if (array == &xml->xml_kids && - kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = SyncInScopeNamespaces(cx, kid); - if (!ok) - break; - } - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - XMLArrayCursorFinish(&cursor); - } - } - } - - /* Common tail code for list and non-list cases. */ - JS_LeaveLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - /* - * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given list's - * [[TargetProperty]] to the property that is being appended. This means - * that any use of the internal [[Get]] property returns a list which, - * when used by e.g. [[Insert]] duplicates the last element matched by id. - * See bug 336921. - */ - list->xml_targetprop = nameqn; - *vp = OBJECT_TO_JSVAL(listobj); - return JS_TRUE; -} - -static JSXML * -CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) -{ - JS_ASSERT(xml->object != obj); - - xml = DeepCopy(cx, xml, obj, 0); - if (!xml) - return NULL; - - JS_ASSERT(xml->object == obj); - return xml; -} - -#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ - (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) - -static JSString * -KidToString(JSContext *cx, JSXML *xml, uint32 index) -{ - JSXML *kid; - JSObject *kidobj; - - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) - return cx->runtime->emptyString; - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return NULL; - return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); -} - -/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ -static JSBool -PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok, primitiveAssign; - enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; - jsval roots[3]; - JSTempValueRooter tvr; - JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; - JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; - JSXMLQName *targetprop, *nameqn, *attrqn; - uint32 index, i, j, k, n, q; - jsval attrval, nsval, junk; - jsid funid; - JSString *left, *right, *space; - JSXMLNamespace *ns; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(*vp)) { - vobj = JSVAL_TO_OBJECT(*vp); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - /* Control flow after here must exit via label out. */ - ok = JS_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); - roots[ID_ROOT] = id; - roots[VAL_ROOT] = *vp; - JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.2. */ - if (js_IdIsIndex(id, &index)) { - /* Step 1 sets i to the property index. */ - i = index; - - /* 2(a-b). */ - if (xml->xml_target) { - ok = ResolveValue(cx, xml->xml_target, &rxml); - if (!ok) - goto out; - if (!rxml) - goto out; - JS_ASSERT(rxml->object); - } else { - rxml = NULL; - } - - /* 2(c). */ - if (index >= xml->xml_kids.length) { - /* 2(c)(i). */ - if (rxml) { - if (rxml->xml_class == JSXML_CLASS_LIST) { - if (rxml->xml_kids.length != 1) - goto out; - rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); - if (!rxml) - goto out; - ok = js_GetXMLObject(cx, rxml) != NULL; - if (!ok) - goto out; - } - - /* - * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets - * _y.[[Parent]] = r_ where _r_ is the result of - * [[ResolveValue]] called on _x.[[TargetObject]] in - * 2(a)(i). This can result in text parenting text: - * - * var MYXML = new XML(); - * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>")); - * - * (testcase from Werner Sharp <wsharp@macromedia.com>). - * - * To match insertChildAfter, insertChildBefore, - * prependChild, and setChildren, we should silently - * do nothing in this case. - */ - if (!JSXML_HAS_KIDS(rxml)) - goto out; - } - - /* 2(c)(ii) is distributed below as several js_NewXML calls. */ - targetprop = xml->xml_targetprop; - if (!targetprop || IS_STAR(targetprop->localName)) { - /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ - kid = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!kid) - goto bad; - } else { - nameobj = js_GetXMLQNameObject(cx, targetprop); - if (!nameobj) - goto bad; - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - /* - * 2(c)(iii)(1-3). - * Note that rxml can't be null here, because target - * and targetprop are non-null. - */ - ok = GetProperty(cx, rxml->object, id, &attrval); - if (!ok) - goto out; - attrobj = JSVAL_TO_OBJECT(attrval); - attr = (JSXML *) JS_GetPrivate(cx, attrobj); - if (JSXML_LENGTH(attr) != 0) - goto out; - - kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - } else { - /* 2(c)(v). */ - kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); - } - if (!kid) - goto bad; - - /* An important bit of 2(c)(ii). */ - kid->name = targetprop; - } - - /* Final important bit of 2(c)(ii). */ - kid->parent = rxml; - - /* 2(c)(vi-vii). */ - i = xml->xml_kids.length; - if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { - /* - * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. - * y.[[Parent]] is here called kid->parent, which we know - * from 2(c)(ii) is _r_, here called rxml. So let's just - * test that! Erratum, the spec should be simpler here. - */ - if (rxml) { - JS_ASSERT(JSXML_HAS_KIDS(rxml)); - n = rxml->xml_kids.length; - j = n - 1; - if (n != 0 && i != 0) { - for (n = j, j = 0; j < n; j++) { - if (rxml->xml_kids.vector[j] == - xml->xml_kids.vector[i-1]) { - break; - } - } - } - - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); - if (!ok) - goto out; - } - - /* - * 2(c)(vii)(2-3). - * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a - * typo for [[TargetProperty]]. - */ - if (vxml) { - kid->name = (vxml->xml_class == JSXML_CLASS_LIST) - ? vxml->xml_targetprop - : vxml->name; - } - } - - /* 2(c)(viii). */ - ok = Append(cx, xml, kid); - if (!ok) - goto out; - } - - /* 2(d). */ - if (!vxml || - vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - roots[VAL_ROOT] = *vp; - } - - /* 2(e). */ - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - goto out; - parent = kid->parent; - if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { - nameobj = js_GetAttributeNameObject(cx, kid->name); - if (!nameobj) - goto bad; - id = OBJECT_TO_JSVAL(nameobj); - - if (parent) { - /* 2(e)(i). */ - parentobj = parent->object; - ok = PutProperty(cx, parentobj, id, vp); - if (!ok) - goto out; - - /* 2(e)(ii). */ - ok = GetProperty(cx, parentobj, id, vp); - if (!ok) - goto out; - attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); - - /* 2(e)(iii). */ - xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; - } - } - - /* 2(f). */ - else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { - /* 2(f)(i) Create a shallow copy _c_ of _V_. */ - copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!copyobj) - goto bad; - copy = (JSXML *) JS_GetPrivate(cx, copyobj); - n = vxml->xml_kids.length; - ok = XMLArraySetCapacity(cx, ©->xml_kids, n); - if (!ok) - goto out; - for (k = 0; k < n; k++) { - kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); - XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); - } - - JS_ASSERT(parent != xml); - if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(q != XML_NOT_FOUND); - - ok = IndexToIdVal(cx, q, &id); - if (!ok) - goto out; - ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); - if (!ok) - goto out; - -#ifdef DEBUG - /* Erratum: this loop in the spec is useless. */ - for (j = 0, n = copy->xml_kids.length; j < n; j++) { - kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); - JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) - == kid2); - } -#endif - } - - /* - * 2(f)(iv-vi). - * Erratum: notice the unhandled zero-length V basis case and - * the off-by-one errors for the n != 0 cases in the spec. - */ - if (n == 0) { - XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); - } else { - ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); - if (!ok) - goto out; - - for (j = 0; j < n; j++) - xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; - } - } - - /* 2(g). */ - else if (vxml || JSXML_HAS_VALUE(kid)) { - if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(q != XML_NOT_FOUND); - - ok = IndexToIdVal(cx, q, &id); - if (!ok) - goto out; - ok = Replace(cx, parent, id, *vp); - if (!ok) - goto out; - - vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); - if (!vxml) - goto out; - roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); - } - - /* - * 2(g)(iii). - * Erratum: _V_ may not be of type XML, but all index-named - * properties _x[i]_ in an XMLList _x_ must be of type XML, - * according to 9.2.1.1 Overview and other places in the spec. - * - * Thanks to 2(d), we know _V_ (*vp here) is either a string - * or an XML/XMLList object. If *vp is a string, call ToXML - * on it to satisfy the constraint. - */ - if (!vxml) { - JS_ASSERT(JSVAL_IS_STRING(*vp)); - vobj = ToXML(cx, *vp); - if (!vobj) - goto bad; - roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); - } - - /* 2(h). */ - else { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - id = ATOM_KEY(cx->runtime->atomState.starAtom); - ok = PutProperty(cx, kidobj, id, vp); - if (!ok) - goto out; - } - } else { - /* - * 3. - * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null - * or an r with r.[[Length]] != 1, throw TypeError. - */ - n = JSXML_LENGTH(xml); - if (n > 1) - goto type_error; - if (n == 0) { - ok = ResolveValue(cx, xml, &rxml); - if (!ok) - goto out; - if (!rxml || JSXML_LENGTH(rxml) != 1) - goto type_error; - ok = Append(cx, xml, rxml); - if (!ok) - goto out; - } - JS_ASSERT(JSXML_LENGTH(xml) == 1); - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!kid) - goto out; - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - ok = PutProperty(cx, kidobj, id, vp); - if (!ok) - goto out; - } - } else { - /* - * ECMA-357 9.1.1.2. - * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted - * effort in ToString or [[DeepCopy]]. - */ - if (js_IdIsIndex(id, &index)) { - /* See NOTE in spec: this variation is reserved for future use. */ - ReportBadXMLName(cx, id); - goto bad; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - goto bad; - if (funid) { - ok = js_SetProperty(cx, obj, funid, vp); - goto out; - } - nameobj = nameqn->object; - - if (JSXML_HAS_VALUE(xml)) - goto out; - - if (!vxml || - vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - } else { - rxml = DeepCopyInLRS(cx, vxml, 0); - if (!rxml || !js_GetXMLObject(cx, rxml)) - goto bad; - vxml = rxml; - *vp = OBJECT_TO_JSVAL(vxml->object); - } - roots[VAL_ROOT] = *vp; - - /* - * 6. - * Erratum: why is this done here, so early? use is way later.... - */ - ok = js_GetDefaultXMLNamespace(cx, &nsval); - if (!ok) - goto out; - - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - /* 7(a). */ - if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) - goto out; - - /* 7(b-c). */ - if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { - n = vxml->xml_kids.length; - if (n == 0) { - *vp = STRING_TO_JSVAL(cx->runtime->emptyString); - } else { - left = KidToString(cx, vxml, 0); - if (!left) - goto bad; - - space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); - for (i = 1; i < n; i++) { - left = js_ConcatStrings(cx, left, space); - if (!left) - goto bad; - right = KidToString(cx, vxml, i); - if (!right) - goto bad; - left = js_ConcatStrings(cx, left, right); - if (!left) - goto bad; - } - - roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); - } - } else { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - roots[VAL_ROOT] = *vp; - } - - /* 7(d-e). */ - match = NULL; - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - attrqn = attr->name; - if (!js_CompareStrings(attrqn->localName, nameqn->localName) && - (!nameqn->uri || - !js_CompareStrings(attrqn->uri, nameqn->uri))) { - if (!match) { - match = attr; - } else { - nameobj = js_GetAttributeNameObject(cx, attrqn); - if (!nameobj) - goto bad; - - id = OBJECT_TO_JSVAL(nameobj); - ok = DeleteProperty(cx, obj, id, &junk); - if (!ok) - goto out; - --i; - } - } - } - - /* 7(f). */ - attr = match; - if (!attr) { - /* 7(f)(i-ii). */ - if (!nameqn->uri) { - left = right = cx->runtime->emptyString; - } else { - left = nameqn->uri; - right = nameqn->prefix; - } - nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); - if (!nameqn) - goto bad; - - /* 7(f)(iii). */ - attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - if (!attr) - goto bad; - attr->parent = xml; - attr->name = nameqn; - - /* 7(f)(iv). */ - ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); - if (!ok) - goto out; - - /* 7(f)(v-vi). */ - ns = GetNamespace(cx, nameqn, NULL); - if (!ns) - goto bad; - ok = AddInScopeNamespace(cx, xml, ns); - if (!ok) - goto out; - } - - /* 7(g). */ - attr->xml_value = JSVAL_TO_STRING(*vp); - goto out; - } - - /* 8-9. */ - if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && - !IS_STAR(nameqn->localName)) { - goto out; - } - - /* 10-11. */ - id = JSVAL_VOID; - primitiveAssign = !vxml && !IS_STAR(nameqn->localName); - - /* 12. */ - k = n = xml->xml_kids.length; - kid2 = NULL; - while (k != 0) { - --k; - kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); - if (kid && MatchElemName(nameqn, kid)) { - if (!JSVAL_IS_VOID(id)) { - ok = DeleteByIndex(cx, xml, id, &junk); - if (!ok) - goto out; - } - ok = IndexToIdVal(cx, k, &id); - if (!ok) - goto out; - kid2 = kid; - } - } - - /* - * Erratum: ECMA-357 specified child insertion inconsistently: - * insertChildBefore and insertChildAfter insert an arbitrary XML - * instance, and therefore can create cycles, but appendChild as - * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on - * its argument. But the "Semantics" in 13.4.4.3 do not include - * any [[DeepCopy]] call. - * - * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) - * required adding cycle detection, and allowing duplicate kids to - * be created (see comment 6 in the bug). Allowing duplicate kid - * references means the loop above will delete all but the lowest - * indexed reference, and each [[DeleteByIndex]] nulls the kid's - * parent. Thus the need to restore parent here. This is covered - * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. - */ - if (kid2) { - JS_ASSERT(kid2->parent == xml || !kid2->parent); - if (!kid2->parent) - kid2->parent = xml; - } - - /* 13. */ - if (JSVAL_IS_VOID(id)) { - /* 13(a). */ - ok = IndexToIdVal(cx, n, &id); - if (!ok) - goto out; - - /* 13(b). */ - if (primitiveAssign) { - if (!nameqn->uri) { - ns = (JSXMLNamespace *) - JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - left = ns->uri; - right = ns->prefix; - } else { - left = nameqn->uri; - right = nameqn->prefix; - } - nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); - if (!nameqn) - goto bad; - - /* 13(b)(iii). */ - vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); - if (!vobj) - goto bad; - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - vxml->parent = xml; - vxml->name = nameqn; - - /* 13(b)(iv-vi). */ - ns = GetNamespace(cx, nameqn, NULL); - if (!ns) - goto bad; - ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); - if (!ok) - goto out; - ok = AddInScopeNamespace(cx, vxml, ns); - if (!ok) - goto out; - } - } - - /* 14. */ - if (primitiveAssign) { - JSXMLArrayCursor cursor; - - js_IdIsIndex(id, &index); - XMLArrayCursorInit(&cursor, &xml->xml_kids); - cursor.index = index; - kid = (JSXML *) XMLArrayCursorItem(&cursor); - if (JSXML_HAS_KIDS(kid)) { - XMLArrayFinish(cx, &kid->xml_kids); - ok = XMLArrayInit(cx, &kid->xml_kids, 1); - } - - /* 14(b-c). */ - /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ - if (ok) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { - roots[VAL_ROOT] = *vp; - if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) - ok = Replace(cx, kid, JSVAL_ZERO, *vp); - } - } - XMLArrayCursorFinish(&cursor); - } else { - /* 15(a). */ - ok = Replace(cx, xml, id, *vp); - } - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - JS_LeaveLocalRootScope(cx); - return ok; - -type_error: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XMLLIST_PUT, - js_ValueToPrintableString(cx, id)); -bad: - ok = JS_FALSE; - goto out; -} - -/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ -static JSBool -ResolveValue(JSContext *cx, JSXML *list, JSXML **result) -{ - JSXML *target, *base; - JSXMLQName *targetprop; - JSObject *targetpropobj; - jsval id, tv; - - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(cx->localRootStack); - - if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { - if (!js_GetXMLObject(cx, list)) - return JS_FALSE; - *result = list; - return JS_TRUE; - } - - target = list->xml_target; - targetprop = list->xml_targetprop; - if (!target || !targetprop || IS_STAR(targetprop->localName)) { - *result = NULL; - return JS_TRUE; - } - - targetpropobj = js_GetXMLQNameObject(cx, targetprop); - if (!targetpropobj) - return JS_FALSE; - if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { - *result = NULL; - return JS_TRUE; - } - - if (!ResolveValue(cx, target, &base)) - return JS_FALSE; - if (!base) { - *result = NULL; - return JS_TRUE; - } - if (!js_GetXMLObject(cx, base)) - return JS_FALSE; - - id = OBJECT_TO_JSVAL(targetpropobj); - if (!GetProperty(cx, base->object, id, &tv)) - return JS_FALSE; - target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); - - if (JSXML_LENGTH(target) == 0) { - if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { - *result = NULL; - return JS_TRUE; - } - tv = STRING_TO_JSVAL(cx->runtime->emptyString); - if (!PutProperty(cx, base->object, id, &tv)) - return JS_FALSE; - if (!GetProperty(cx, base->object, id, &tv)) - return JS_FALSE; - target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); - } - - *result = target; - return JS_TRUE; -} - -/* - * HasProperty must be able to return a found JSProperty and the object in - * which it was found, if id is of the form function::name. For other ids, - * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp - * and null in *objp. - * - * DROP_PROPERTY helps HasProperty callers drop function properties without - * trying to drop the magic FOUND_XML_PROPERTY cookie. - */ -#define FOUND_XML_PROPERTY ((JSProperty *) 1) -#define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ - ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ - : (void) 0) - -/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ -static JSBool -HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, - JSProperty **propp) -{ - JSXML *xml, *kid; - JSXMLArrayCursor cursor; - JSObject *kidobj; - JSXMLQName *qn; - jsid funid; - JSXMLArray *array; - JSXMLNameMatcher matcher; - uint32 i, n; - - *objp = NULL; - *propp = NULL; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class == JSXML_CLASS_LIST) { - n = JSXML_LENGTH(xml); - if (js_IdIsIndex(id, &i)) { - if (i < n) - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) - break; - if (*propp) - break; - } - } - XMLArrayCursorFinish(&cursor); - if (kid) - return *propp != NULL; - } else { - if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { - if (i == 0) - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - - qn = ToXMLName(cx, id, &funid); - if (!qn) - return JS_FALSE; - if (funid) - return js_LookupProperty(cx, obj, funid, objp, propp); - - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - for (i = 0, n = array->length; i < n; i++) { - kid = XMLARRAY_MEMBER(array, i, JSXML); - if (kid && matcher(qn, kid)) { - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - } - } - - return JS_TRUE; -} - -static void -xml_finalize(JSContext *cx, JSObject *obj) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (!xml) - return; - if (xml->object == obj) - xml->object = NULL; - UNMETER(xml_stats.livexmlobj); -} - -static void -xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len, void *arg) -{ - uint32 i; - JSXML *elt; - - for (i = 0; i < len; i++) { - elt = vec[i]; - { -#ifdef GC_MARK_DEBUG - char buf[120]; - - if (elt->xml_class == JSXML_CLASS_LIST) { - strcpy(buf, js_XMLList_str); - } else if (JSXML_HAS_NAME(elt)) { - JSXMLQName *qn = elt->name; - - JS_snprintf(buf, sizeof buf, "%s::%s", - qn->uri ? JS_GetStringBytes(qn->uri) : "*", - JS_GetStringBytes(qn->localName)); - } else { - JSString *str = elt->xml_value; - size_t srclen = JSSTRING_LENGTH(str); - size_t dstlen = sizeof buf; - - if (srclen >= sizeof buf / 6) - srclen = sizeof buf / 6 - 1; - js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, - buf, &dstlen); - } -#else - const char *buf = NULL; -#endif - JS_MarkGCThing(cx, elt, buf, arg); - } - } -} - -/* - * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to - * be native. Therefore, xml_lookupProperty must return a valid JSProperty - * pointer parameter via *propp to signify "property found". Since the only - * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from - * js_FindXMLProperty (in this file) and js_FindProperty (in jsobj.c, called - * from jsinterp.c), the only time we add a JSScopeProperty here is when an - * unqualified name or XML name is being accessed. - * - * This scope property both speeds up subsequent js_Find*Property calls, and - * keeps the JSOP_NAME code in js_Interpret happy by giving it an sprop with - * (getter, setter) == (GetProperty, PutProperty). We can't use that getter - * and setter as js_XMLClass's getProperty and setProperty, because doing so - * would break the XML methods, which are function-valued properties of the - * XML.prototype object. - * - * NB: xml_deleteProperty must take care to remove any property added here. - */ -static JSBool -xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - JSScopeProperty *sprop; - - if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) - return JS_FALSE; - - if (*propp == FOUND_XML_PROPERTY) { - sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, - SPROP_INVALID_SLOT, JSPROP_ENUMERATE, - 0, 0); - if (!sprop) - return JS_FALSE; - - JS_LOCK_OBJ(cx, obj); - *objp = obj; - *propp = (JSProperty *) sprop; - } - return JS_TRUE; -} - -static JSBool -xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp) -{ - if (JSVAL_IS_FUNCTION(cx, value) || getter || setter || - (attrs & JSPROP_ENUMERATE) == 0 || - (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { - return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, - propp); - } - - if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) - return JS_FALSE; - if (propp) - *propp = NULL; - return JS_TRUE; -} - -static JSBool -xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - if (id == JS_DEFAULT_XML_NAMESPACE_ID) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - return GetProperty(cx, obj, ID_TO_VALUE(id), vp); -} - -static JSBool -xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return PutProperty(cx, obj, ID_TO_VALUE(id), vp); -} - -static JSBool -FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - JSBool *foundp) -{ - JSObject *pobj; - - if (prop) { - *foundp = JS_TRUE; - } else { - if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) - return JS_FALSE; - if (prop) - DROP_PROPERTY(cx, pobj, prop); - *foundp = (prop != NULL); - } - return JS_TRUE; -} - -static JSBool -xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool found; - - if (!FoundProperty(cx, obj, id, prop, &found)) - return JS_FALSE; - *attrsp = found ? JSPROP_ENUMERATE : 0; - return JS_TRUE; -} - -static JSBool -xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool found; - - if (!FoundProperty(cx, obj, id, prop, &found)) - return JS_FALSE; - if (found) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SET_XML_ATTRS); - } - return !found; -} - -static JSBool -xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - /* - * If this object has its own (mutable) scope, and if id isn't an index, - * then we may have added a property to the scope in xml_lookupProperty - * for it to return to mean "found" and to provide a handle for access - * operations to call the property's getter or setter. The property also - * helps speed up unqualified accesses via the property cache, avoiding - * what amount to two HasProperty searches. - * - * But now it's time to remove any such property, to purge the property - * cache and remove the scope entry. - */ - if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { - if (!js_DeleteProperty(cx, obj, id, rval)) - return JS_FALSE; - } - - return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); -} - -static JSBool -xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - JSXML *xml; - - if (hint == JSTYPE_OBJECT) { - /* Called from for..in code in js_Interpret: return an XMLList. */ - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class != JSXML_CLASS_LIST) { - obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); - if (!obj) - return JS_FALSE; - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); -} - -static JSBool -xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSXML *xml; - uint32 length, index; - JSXMLArrayCursor *cursor; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - length = JSXML_LENGTH(xml); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (length == 0) { - cursor = NULL; - } else { - cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); - if (!cursor) - return JS_FALSE; - XMLArrayCursorInit(cursor, &xml->xml_kids); - } - *statep = PRIVATE_TO_JSVAL(cursor); - if (idp) - *idp = INT_TO_JSID(length); - break; - - case JSENUMERATE_NEXT: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor && cursor->array && (index = cursor->index) < length) { - *idp = INT_TO_JSID(index); - cursor->index = index + 1; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor) { - XMLArrayCursorFinish(cursor); - JS_free(cx, cursor); - } - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -static JSBool -xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - return JS_TRUE; -} - -static uint32 -xml_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - JS_MarkGCThing(cx, xml, js_private_str, arg); - return js_Mark(cx, obj, arg); -} - -static void -xml_clear(JSContext *cx, JSObject *obj) -{ -} - -static JSBool -HasSimpleContent(JSXML *xml) -{ - JSXML *kid; - JSBool simple; - uint32 i, n; - -again: - switch (xml->xml_class) { - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - return JS_FALSE; - case JSXML_CLASS_LIST: - if (xml->xml_kids.length == 0) - return JS_TRUE; - if (xml->xml_kids.length == 1) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (kid) { - xml = kid; - goto again; - } - } - /* FALL THROUGH */ - default: - simple = JS_TRUE; - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - simple = JS_FALSE; - break; - } - } - return simple; - } -} - -/* - * 11.2.2.1 Step 3(d) onward. - */ -static JSObject * -xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSXML *xml; - JSTempValueRooter tvr; - jsval roots[2]; - enum { - FUN_ROOT = 0, - OBJ_ROOT = 1 - }; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); - xml = (JSXML *) JS_GetPrivate(cx, obj); - memset(roots, 0, sizeof(roots)); - JS_PUSH_TEMP_ROOT(cx, sizeof roots / sizeof *roots, roots, &tvr); - - /* From this point the control must flow through out: or bad: */ - retry: - if (!GetFunction(cx, obj, xml, id, &roots[FUN_ROOT])) - goto bad; - if (JSVAL_IS_VOID(roots[FUN_ROOT]) && OBJECT_IS_XML(cx, obj)) { - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - obj = js_GetXMLObject(cx, xml); - if (!obj) - goto bad; - roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); - goto retry; - } - } - } else if (HasSimpleContent(xml)) { - JSString *str; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - goto bad; - if (!js_ValueToObject(cx, STRING_TO_JSVAL(str), &obj)) - goto bad; - roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); - if (!js_GetProperty(cx, obj, id, &roots[FUN_ROOT])) - goto bad; - } - } - out: - *vp = roots[FUN_ROOT]; - if (obj) { - /* - * If we just POP tvr, then it is possible that nothing roots obj, see - * bug 353165. To allow our callers to assume at least weakly rooting - * of the result, we root obj via newborn array. Similarly we root the - * value of roots[FUNCTION] since getMethod callers have a bad habit - * of passing a pointer to unrooted local value as vp. - */ - cx->newborn[GCX_OBJECT] = (JSGCThing *)obj; - cx->lastInternalResult = roots[FUN_ROOT]; - } - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; - bad: - obj = NULL; - goto out; -} - -static JSBool -xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return js_SetProperty(cx, obj, id, vp); -} - -static JSBool -xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp, jsval *vp) -{ - JSXML *xml, *kid; - uint32 length, index; - JSXMLArrayCursor *cursor; - JSObject *kidobj; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - length = JSXML_LENGTH(xml); - JS_ASSERT(INT_FITS_IN_JSVAL(length)); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (length == 0) { - cursor = NULL; - } else { - cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); - if (!cursor) - return JS_FALSE; - XMLArrayCursorInit(cursor, &xml->xml_kids); - } - *statep = PRIVATE_TO_JSVAL(cursor); - if (idp) - *idp = INT_TO_JSID(length); - if (vp) - *vp = JSVAL_VOID; - break; - - case JSENUMERATE_NEXT: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor && cursor->array && (index = cursor->index) < length) { - while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { - if (++index == length) - goto destroy; - } - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - JS_ASSERT(INT_FITS_IN_JSVAL(index)); - *idp = INT_TO_JSID(index); - *vp = OBJECT_TO_JSVAL(kidobj); - cursor->index = index + 1; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor) { - destroy: - XMLArrayCursorFinish(cursor); - JS_free(cx, cursor); - } - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -static JSBool -xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXML *xml, *vxml; - JSObject *vobj; - JSBool ok; - JSString *str, *vstr; - jsdouble d, d2; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = Equals(cx, xml, v, bp); - } else if (vxml) { - if (vxml->xml_class == JSXML_CLASS_LIST) { - ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); - } else { - if (((xml->xml_class == JSXML_CLASS_TEXT || - xml->xml_class == JSXML_CLASS_ATTRIBUTE) && - HasSimpleContent(vxml)) || - ((vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && - HasSimpleContent(xml))) { - ok = JS_EnterLocalRootScope(cx); - if (ok) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - vstr = js_ValueToString(cx, v); - ok = str && vstr; - if (ok) - *bp = !js_CompareStrings(str, vstr); - JS_LeaveLocalRootScope(cx); - } - } else { - ok = XMLEquals(cx, xml, vxml, bp); - } - } - } else { - ok = JS_EnterLocalRootScope(cx); - if (ok) { - if (HasSimpleContent(xml)) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - vstr = js_ValueToString(cx, v); - ok = str && vstr; - if (ok) - *bp = !js_CompareStrings(str, vstr); - } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) { - ok = JS_FALSE; - } else if (JSVAL_IS_STRING(v)) { - *bp = !js_CompareStrings(str, JSVAL_TO_STRING(v)); - } else { - ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); - if (ok) { - d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) - : *JSVAL_TO_DOUBLE(v); - *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); - } - } - } else { - *bp = JS_FALSE; - } - JS_LeaveLocalRootScope(cx); - } - } - return ok; -} - -static JSBool -xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) -{ - JSBool ok; - JSObject *listobj, *robj; - JSXML *list, *lxml, *rxml; - - ok = JS_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - goto out; - } - - list = (JSXML *) JS_GetPrivate(cx, listobj); - lxml = (JSXML *) JS_GetPrivate(cx, obj); - ok = Append(cx, list, lxml); - if (!ok) - goto out; - - if (VALUE_IS_XML(cx, v)) { - rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - } else { - robj = ToXML(cx, v); - if (!robj) { - ok = JS_FALSE; - goto out; - } - rxml = (JSXML *) JS_GetPrivate(cx, robj); - } - ok = Append(cx, list, rxml); - if (!ok) - goto out; - - *vp = OBJECT_TO_JSVAL(listobj); -out: - JS_LeaveLocalRootScope(cx); - return ok; -} - -/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ -JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { - { js_NewObjectMap, js_DestroyObjectMap, - xml_lookupProperty, xml_defineProperty, - xml_getProperty, xml_setProperty, - xml_getAttributes, xml_setAttributes, - xml_deleteProperty, xml_defaultValue, - xml_enumerate, js_CheckAccess, - NULL, NULL, - NULL, NULL, - NULL, xml_hasInstance, - js_SetProtoOrParent, js_SetProtoOrParent, - xml_mark, xml_clear, - NULL, NULL }, - xml_getMethod, xml_setMethod, - xml_enumerateValues, xml_equality, - xml_concatenate -}; - -static JSObjectOps * -xml_getObjectOps(JSContext *cx, JSClass *clasp) -{ - return &js_XMLObjectOps.base; -} - -JS_FRIEND_DATA(JSClass) js_XMLClass = { - js_XML_str, JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, - xml_getObjectOps, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -static JSObject * -CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, - uintN argc, jsval *argv) -{ - JSObject *tmp; - jsval rval; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) - return NULL; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); - return JSVAL_TO_OBJECT(rval); -} - -#define XML_METHOD_PROLOG \ - JS_BEGIN_MACRO \ - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ - if (!xml) \ - return JS_FALSE; \ - JS_END_MACRO - -static JSBool -xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSObject *nsobj; - JSXMLNamespace *ns; - - XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); - if (!nsobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nsobj); - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - if (!AddInScopeNamespace(cx, xml, ns)) - return JS_FALSE; - ns->declared = JS_TRUE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *vxml; - jsval name, v; - JSObject *vobj; - - XML_METHOD_PROLOG; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - if (!js_GetAnyName(cx, &name)) - return JS_FALSE; - - if (!GetProperty(cx, obj, name, &v)) - return JS_FALSE; - - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - vobj = JSVAL_TO_OBJECT(v); - JS_ASSERT(OBJECT_IS_XML(cx, vobj)); - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); - - if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) - return JS_FALSE; - if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXMLQName *qn; - - qn = ToAttributeName(cx, argv[0]); - if (!qn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ - return GetProperty(cx, obj, argv[0], rval); -} - -/* XML and XMLList */ -static JSBool -xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - JSXMLQName *qn; - JSTempValueRooter tvr; - JSBool ok; - - name = ATOM_KEY(cx->runtime->atomState.starAtom); - qn = ToAttributeName(cx, name); - if (!qn) - return JS_FALSE; - name = OBJECT_TO_JSVAL(qn->object); - JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); - ok = GetProperty(cx, obj, name, rval); - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, - jsval *rval) -{ - uint32 index; - JSXML *kid; - JSObject *kidobj; - - /* ECMA-357 13.4.4.6 */ - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - - if (js_IdIsIndex(name, &index)) { - if (index >= JSXML_LENGTH(xml)) { - *rval = JSVAL_VOID; - } else { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) { - *rval = JSVAL_VOID; - } else { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(kidobj); - } - } - return JS_TRUE; - } - - return GetProperty(cx, obj, name, rval); -} - -static JSBool -xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSXMLArrayCursor cursor; - jsval name, v; - JSObject *listobj, *kidobj; - - XML_METHOD_PROLOG; - name = argv[0]; - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 13.5.4.4 */ - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(listobj); - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - break; - if (!xml_child_helper(cx, kidobj, kid, name, &v)) - break; - if (JSVAL_IS_VOID(v)) { - /* The property didn't exist in this kid. */ - continue; - } - - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && - !Append(cx, list, vxml)) { - break; - } - } - XMLArrayCursorFinish(&cursor); - return !kid; - } - - return xml_child_helper(cx, obj, xml, name, rval); -} - -static JSBool -xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *parent; - uint32 i, n; - - XML_METHOD_PROLOG; - parent = xml->parent; - if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { - if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) - break; - } - JS_ASSERT(i < n); - return js_NewNumberValue(cx, i, rval); -} - -/* XML and XMLList */ -static JSBool -xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - - name = ATOM_KEY(cx->runtime->atomState.starAtom); - return GetProperty(cx, obj, name, rval); -} - -/* XML and XMLList */ -static JSBool -xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSObject *listobj, *kidobj; - JSBool ok; - uint32 i, n; - jsval v; - - XML_METHOD_PROLOG; - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(listobj); - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.6 Step 2. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = JS_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - ok = kidobj - ? xml_comments(cx, kidobj, argc, argv, &v) - : JS_FALSE; - JS_LeaveLocalRootScope(cx); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - } else { - /* 13.4.4.9 Step 2. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval value; - JSBool eq; - JSXMLArrayCursor cursor; - JSObject *kidobj; - - XML_METHOD_PROLOG; - value = argv[0]; - if (xml->xml_class == JSXML_CLASS_LIST) { - eq = JS_FALSE; - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) - break; - if (eq) - break; - } - XMLArrayCursorFinish(&cursor); - if (kid) - return JS_FALSE; - } else { - if (!xml_equality(cx, obj, value, &eq)) - return JS_FALSE; - } - *rval = BOOLEAN_TO_JSVAL(eq); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *copy; - - XML_METHOD_PROLOG; - copy = DeepCopy(cx, xml, NULL, 0); - if (!copy) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(copy->object); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list; - jsval name; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - list = Descendants(cx, xml, name); - if (!list) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(list->object); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - jsval name, v; - JSXMLQName *nameqn; - jsid funid; - JSObject *listobj, *kidobj; - JSBool ok; - JSXMLArrayCursor cursor; - uint32 i, n; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - nameqn = ToXMLName(cx, name, &funid); - if (!nameqn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameqn->object); - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(listobj); - if (funid) - return JS_TRUE; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - list->xml_targetprop = nameqn; - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.6 */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = JS_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - ok = kidobj - ? xml_elements(cx, kidobj, argc, argv, &v) - : JS_FALSE; - JS_LeaveLocalRootScope(cx); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } else { - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && - MatchElemName(nameqn, kid)) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - JSObject *pobj; - JSProperty *prop; - - if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) - return JS_FALSE; - - name = argv[0]; - if (!HasProperty(cx, obj, name, &pobj, &prop)) - return JS_FALSE; - if (!prop) { - return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, - rval); - } - DROP_PROPERTY(cx, pobj, prop); - *rval = JSVAL_TRUE; - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; -again: - switch (xml->xml_class) { - case JSXML_CLASS_ATTRIBUTE: - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - case JSXML_CLASS_TEXT: - *rval = JSVAL_FALSE; - break; - case JSXML_CLASS_LIST: - if (xml->xml_kids.length == 0) { - *rval = JSVAL_TRUE; - } else if (xml->xml_kids.length == 1) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (kid) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - obj = kidobj; - xml = (JSXML *) JS_GetPrivate(cx, obj); - goto again; - } - } - /* FALL THROUGH */ - default: - *rval = JSVAL_FALSE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - *rval = JSVAL_TRUE; - break; - } - } - break; - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); - return JS_TRUE; -} - -typedef struct JSTempRootedNSArray { - JSTempValueRooter tvr; - JSXMLArray array; - jsval value; /* extra root for temporaries */ -} JSTempRootedNSArray; - -JS_STATIC_DLL_CALLBACK(void) -mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) -{ - JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; - - namespace_mark_vector(cx, - (JSXMLNamespace **)tmp->array.vector, - tmp->array.length, NULL); - XMLArrayCursorMark(cx, tmp->array.cursors); - if (JSVAL_IS_GCTHING(tmp->value)) - GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value", NULL); -} - -static void -InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) -{ - XMLArrayInit(cx, &tmp->array, 0); - tmp->value = JSVAL_NULL; - JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); -} - -static void -FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) -{ - JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); - JS_POP_TEMP_ROOT(cx, &tmp->tvr); - XMLArrayFinish(cx, &tmp->array); -} - -/* - * Populate a new JS array with elements of JSTempRootedNSArray.array and - * place the result into rval. rval must point to a rooted location. - */ -static JSBool -TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) -{ - JSObject *arrayobj; - uint32 i, n; - JSXMLNamespace *ns; - JSObject *nsobj; - - arrayobj = js_NewArrayObject(cx, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(arrayobj); - for (i = 0, n = tmp->array.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); - if (!ns) - continue; - nsobj = js_GetXMLNamespaceObject(cx, ns); - if (!nsobj) - return JS_FALSE; - tmp->value = OBJECT_TO_JSVAL(nsobj); - if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) -{ - uint32 length, i, j, n; - JSXMLNamespace *ns, *ns2; - - length = nsarray->length; - do { - if (xml->xml_class != JSXML_CLASS_ELEMENT) - continue; - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - - for (j = 0; j < length; j++) { - ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); - if (ns2 && - ((ns2->prefix && ns->prefix) - ? !js_CompareStrings(ns2->prefix, ns->prefix) - : !js_CompareStrings(ns2->uri, ns->uri))) { - break; - } - } - - if (j == length) { - if (!XMLARRAY_APPEND(cx, nsarray, ns)) - return JS_FALSE; - ++length; - } - } - } while ((xml = xml->parent) != NULL); - JS_ASSERT(length == nsarray->length); - - return JS_TRUE; -} - -static JSBool -xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSTempRootedNSArray namespaces; - JSBool ok; - - XML_METHOD_PROLOG; - - InitTempNSArray(cx, &namespaces); - ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && - TempNSArrayToJSArray(cx, &namespaces, rval); - FinishTempNSArray(cx, &namespaces); - return ok; -} - -static JSBool -xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval arg; - uint32 i; - - XML_METHOD_PROLOG; - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - arg = argv[0]; - if (JSVAL_IS_NULL(arg)) { - kid = NULL; - i = 0; - } else { - if (!VALUE_IS_XML(cx, arg)) - return JS_TRUE; - kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); - if (i == XML_NOT_FOUND) - return JS_TRUE; - ++i; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - if (!Insert(cx, xml, i, argv[1])) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval arg; - uint32 i; - - XML_METHOD_PROLOG; - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - arg = argv[0]; - if (JSVAL_IS_NULL(arg)) { - kid = NULL; - i = xml->xml_kids.length; - } else { - if (!VALUE_IS_XML(cx, arg)) - return JS_TRUE; - kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); - if (i == XML_NOT_FOUND) - return JS_TRUE; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - if (!Insert(cx, xml, i, argv[1])) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_LIST) { - *rval = JSVAL_ONE; - } else { - if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; - return JS_TRUE; -} - -static JSBool -xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml; - JSObject *nameobj; - - XML_METHOD_PROLOG; - if (!xml->name) { - *rval = JSVAL_NULL; - } else { - nameobj = js_GetXMLQNameObject(cx, xml->name); - if (!nameobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nameobj); - } - return JS_TRUE; -} - -static JSBool -xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *prefix; - JSTempRootedNSArray inScopeNSes; - JSBool ok; - jsuint i, length; - JSXMLNamespace *ns; - JSObject *nsobj; - - XML_METHOD_PROLOG; - if (argc == 0 && - (xml->xml_class == JSXML_CLASS_TEXT || - xml->xml_class == JSXML_CLASS_COMMENT || - xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - - if (argc == 0) { - prefix = NULL; - } else { - prefix = js_ValueToString(cx, argv[0]); - if (!prefix) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(prefix); /* local root */ - } - - /* After this point the control must flow through label out. */ - InitTempNSArray(cx, &inScopeNSes); - ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); - if (!ok) - goto out; - - if (!prefix) { - ns = GetNamespace(cx, xml->name, &inScopeNSes.array); - if (!ns) { - ok = JS_FALSE; - goto out; - } - } else { - ns = NULL; - for (i = 0, length = inScopeNSes.array.length; i < length; i++) { - ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); - if (ns && ns->prefix && !js_CompareStrings(ns->prefix, prefix)) - break; - ns = NULL; - } - } - - if (!ns) { - *rval = JSVAL_VOID; - } else { - nsobj = js_GetXMLNamespaceObject(cx, ns); - if (!nsobj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(nsobj); - } - - out: - FinishTempNSArray(cx, &inScopeNSes); - return JS_TRUE; -} - -static JSBool -xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *yml; - JSBool ok; - JSTempRootedNSArray ancestors, declared; - uint32 i, n; - JSXMLNamespace *ns; - - XML_METHOD_PROLOG; - if (JSXML_HAS_VALUE(xml) || xml->xml_class == JSXML_CLASS_LIST) - return JS_TRUE; - - /* From here, control flow must goto out to finish these arrays. */ - ok = JS_TRUE; - InitTempNSArray(cx, &ancestors); - InitTempNSArray(cx, &declared); - yml = xml; - - while ((yml = yml->parent) != NULL) { - JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); - for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); - if (ns && - !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { - ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); - if (!ok) - goto out; - } - } - } - - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - if (!ns->declared) - continue; - if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { - ok = XMLARRAY_APPEND(cx, &declared.array, ns); - if (!ok) - goto out; - } - } - - ok = TempNSArrayToJSArray(cx, &declared, rval); - -out: - /* Finishing must be in reverse order of initialization to follow LIFO. */ - FinishTempNSArray(cx, &declared); - FinishTempNSArray(cx, &ancestors); - return ok; -} - -static const char js_attribute_str[] = "attribute"; -static const char js_text_str[] = "text"; - -/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ -const char *js_xml_class_str[] = { - "list", - "element", - js_attribute_str, - "processing-instruction", - js_text_str, - "comment" -}; - -static JSBool -xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *str; - - XML_METHOD_PROLOG; - str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) -{ - jsval junk; - - if (xml->xml_class == JSXML_CLASS_LIST) - return DeleteProperty(cx, obj, id, &junk); - return DeleteByIndex(cx, xml, id, &junk); -} - -/* - * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace - * text between tags to be removed by normalize. - */ -static JSBool -IsXMLSpace(JSString *str) -{ - const jschar *cp, *end; - - cp = JSSTRING_CHARS(str); - end = cp + JSSTRING_LENGTH(str); - while (cp < end) { - if (!JS_ISXMLSPACE(*cp)) - return JS_FALSE; - ++cp; - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid, *kid2; - uint32 i, n; - JSObject *kidobj; - JSString *str; - jsval junk; - - XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - continue; - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) - return JS_FALSE; - } else if (kid->xml_class == JSXML_CLASS_TEXT) { - while (i + 1 < n && - (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && - kid2->xml_class == JSXML_CLASS_TEXT) { - str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); - if (!str) - return JS_FALSE; - if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) - return JS_FALSE; - n = xml->xml_kids.length; - kid->xml_value = str; - } - if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { - if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) - return JS_FALSE; - n = xml->xml_kids.length; - --i; - } - } - } - - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *parent, *kid; - uint32 i, n; - JSObject *parentobj; - - XML_METHOD_PROLOG; - parent = xml->parent; - if (xml->xml_class == JSXML_CLASS_LIST) { - *rval = JSVAL_VOID; - n = xml->xml_kids.length; - if (n == 0) - return JS_TRUE; - - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!kid) - return JS_TRUE; - parent = kid->parent; - for (i = 1; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->parent != parent) - return JS_TRUE; - } - } - - if (!parent) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - - parentobj = js_GetXMLObject(cx, parent); - if (!parentobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(parentobj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - jsval name, v; - JSXMLQName *nameqn; - jsid funid; - JSObject *listobj, *kidobj; - JSBool ok; - JSXMLArrayCursor cursor; - uint32 i, n; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - nameqn = ToXMLName(cx, name, &funid); - if (!nameqn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameqn->object); - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(listobj); - if (funid) - return JS_TRUE; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - list->xml_targetprop = nameqn; - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = JS_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - ok = kidobj - ? xml_processingInstructions(cx, kidobj, argc, argv, &v) - : JS_FALSE; - JS_LeaveLocalRootScope(cx); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } else { - /* 13.4.4.28 Step 4. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && - (IS_STAR(nameqn->localName) || - !js_CompareStrings(nameqn->localName, kid->name->localName))) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -static JSBool -xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return Insert(cx, xml, 0, argv[0]); -} - -/* XML and XMLList */ -static JSBool -xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - jsval name; - uint32 index; - - XML_METHOD_PROLOG; - name = argv[0]; - *rval = JSVAL_FALSE; - if (js_IdIsIndex(name, &index)) { - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.18. */ - *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); - } else { - /* 13.4.4.30. */ - *rval = BOOLEAN_TO_JSVAL(index == 0); - } - } - return JS_TRUE; -} - -static JSBool -namespace_full_match(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsa->prefix && nsb->prefix && - js_CompareStrings(nsa->prefix, nsb->prefix)) { - return JS_FALSE; - } - return !js_CompareStrings(nsa->uri, nsb->uri); -} - -static JSBool -xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) -{ - JSXMLNamespace *thisns, *attrns; - uint32 i, n; - JSXML *attr, *kid; - - thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); - JS_ASSERT(thisns); - if (thisns == ns) - return JS_TRUE; - - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); - JS_ASSERT(attrns); - if (attrns == ns) - return JS_TRUE; - } - - i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); - if (i != XML_NOT_FOUND) - XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); - - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - if (!xml_removeNamespace_helper(cx, kid, ns)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSObject *nsobj; - JSXMLNamespace *ns; - - XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); - if (!nsobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nsobj); - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - - /* NOTE: remove ns from each ancestor if not used by that ancestor. */ - return xml_removeNamespace_helper(cx, xml, ns); -} - -static JSBool -xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *vxml, *kid; - jsval name, value, id, junk; - uint32 index; - JSObject *nameobj; - JSXMLQName *nameqn; - - XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - value = argv[1]; - vxml = VALUE_IS_XML(cx, value) - ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) - : NULL; - if (!vxml) { - if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) - return JS_FALSE; - value = argv[1]; - } else { - vxml = DeepCopy(cx, vxml, NULL, 0); - if (!vxml) - return JS_FALSE; - value = argv[1] = OBJECT_TO_JSVAL(vxml->object); - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - name = argv[0]; - if (js_IdIsIndex(name, &index)) - return Replace(cx, xml, name, value); - - /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ - nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); - if (!nameobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameobj); - nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); - - id = JSVAL_VOID; - index = xml->xml_kids.length; - while (index != 0) { - --index; - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (kid && MatchElemName(nameqn, kid)) { - if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) - return JS_FALSE; - if (!IndexToIdVal(cx, index, &id)) - return JS_FALSE; - } - } - if (JSVAL_IS_VOID(id)) - return JS_TRUE; - return Replace(cx, xml, id, value); -} - -static JSBool -xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), - &argv[0])) { - return JS_FALSE; - } - - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - jsval name; - JSXMLQName *nameqn; - JSString *namestr; - - XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - name = argv[0]; - if (!JSVAL_IS_PRIMITIVE(name) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { - nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); - namestr = nameqn->localName; - } else { - if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) - return JS_FALSE; - name = argv[0]; - namestr = JSVAL_TO_STRING(name); - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - xml->name->localName = namestr; - return JS_TRUE; -} - -static JSBool -xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *nsowner; - jsval name; - JSXMLQName *nameqn; - JSObject *nameobj; - JSXMLArray *nsarray; - uint32 i, n; - JSXMLNamespace *ns; - - XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - name = argv[0]; - if (!JSVAL_IS_PRIMITIVE(name) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && - !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) - ->uri) { - name = argv[0] = STRING_TO_JSVAL(nameqn->localName); - } - - nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); - if (!nameobj) - return JS_FALSE; - nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); - - /* ECMA-357 13.4.4.35 Step 4. */ - if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - nameqn->uri = cx->runtime->emptyString; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - xml->name = nameqn; - - /* - * Erratum: nothing in 13.4.4.35 talks about making the name match the - * in-scope namespaces, either by finding an in-scope namespace with a - * matching uri and setting the new name's prefix to that namespace's - * prefix, or by extending the in-scope namespaces for xml (which are in - * xml->parent if xml is an attribute or a PI). - */ - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - nsowner = xml; - } else { - if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - nsowner = xml->parent; - } - - if (nameqn->prefix) { - /* - * The name being set has a prefix, which originally came from some - * namespace object (which may be the null namespace, where both the - * prefix and uri are the empty string). We must go through a full - * GetNamespace in case that namespace is in-scope in nsowner. - * - * If we find such an in-scope namespace, we return true right away, - * in this block. Otherwise, we fall through to the final return of - * AddInScopeNamespace(cx, nsowner, ns). - */ - ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); - if (!ns) - return JS_FALSE; - - /* XXXbe have to test membership to see whether GetNamespace added */ - if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) - return JS_TRUE; - } else { - /* - * At this point, we know nameqn->prefix is null, so nameqn->uri can't - * be the empty string (the null namespace always uses the empty string - * for both prefix and uri). - * - * This means we must inline GetNamespace and specialize it to match - * uri only, never prefix. If we find a namespace with nameqn's uri - * already in nsowner->xml_namespaces, then all that we need do is set - * nameqn->prefix to that namespace's prefix. - * - * If no such namespace exists, we can create one without going through - * the constructor, because we know nameqn->uri is non-empty (so prefix - * does not need to be converted from null to empty by QName). - */ - JS_ASSERT(!IS_EMPTY(nameqn->uri)); - - nsarray = &nsowner->xml_namespaces; - for (i = 0, n = nsarray->length; i < n; i++) { - ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); - if (ns && !js_CompareStrings(ns->uri, nameqn->uri)) { - nameqn->prefix = ns->prefix; - return JS_TRUE; - } - } - - ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); - if (!ns) - return JS_FALSE; - } - - return AddInScopeNamespace(cx, nsowner, ns); -} - -static JSBool -xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *nsowner; - JSObject *nsobj, *qnobj; - JSXMLNamespace *ns; - jsval qnargv[2]; - - XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_ELEMENT && - xml->xml_class != JSXML_CLASS_ATTRIBUTE) { - return JS_TRUE; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml || !js_GetXMLQNameObject(cx, xml->name)) - return JS_FALSE; - - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); - if (!nsobj) - return JS_FALSE; - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - ns->declared = JS_TRUE; - - qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); - qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); - qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); - if (!qnobj) - return JS_FALSE; - - xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); - - /* - * Erratum: the spec fails to update the governing in-scope namespaces. - * See the erratum noted in xml_setName, above. - */ - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - nsowner = xml; - } else { - if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - nsowner = xml->parent; - } - return AddInScopeNamespace(cx, nsowner, ns); -} - -/* XML and XMLList */ -static JSBool -xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSObject *listobj, *kidobj; - uint32 i, n; - JSBool ok; - jsval v; - - XML_METHOD_PROLOG; - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(listobj); - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = JS_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - ok = kidobj - ? xml_text(cx, kidobj, argc, argv, &v) - : JS_FALSE; - JS_LeaveLocalRootScope(cx); - if (!ok) - return JS_FALSE; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) - return JS_FALSE; - } - } - } else { - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_TEXT) { - if (!Append(cx, list, kid)) - return JS_FALSE; - } - } - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSString * -xml_toString_helper(JSContext *cx, JSXML *xml) -{ - JSString *str, *kidstr; - JSXML *kid; - JSXMLArrayCursor cursor; - - if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || - xml->xml_class == JSXML_CLASS_TEXT) { - return xml->xml_value; - } - - if (!HasSimpleContent(xml)) - return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); - - str = cx->runtime->emptyString; - JS_EnterLocalRootScope(cx); - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class != JSXML_CLASS_COMMENT && - kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { - kidstr = xml_toString_helper(cx, kid); - if (!kidstr) { - str = NULL; - break; - } - str = js_ConcatStrings(cx, str, kidstr); - if (!str) - break; - } - } - XMLArrayCursorFinish(&cursor); - JS_LeaveLocalRootScope(cx); - return str; -} - -static JSBool -xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *str; - - XML_METHOD_PROLOG; - str = xml_toString_helper(cx, xml); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec xml_methods[] = { - {"addNamespace", xml_addNamespace, 1,0,XML_MASK}, - {"appendChild", xml_appendChild, 1,0,XML_MASK}, - {js_attribute_str, xml_attribute, 1,0,GENERIC_MASK}, - {"attributes", xml_attributes, 0,0,GENERIC_MASK}, - {"child", xml_child, 1,0,GENERIC_MASK}, - {"childIndex", xml_childIndex, 0,0,XML_MASK}, - {"children", xml_children, 0,0,GENERIC_MASK}, - {"comments", xml_comments, 0,0,GENERIC_MASK}, - {"contains", xml_contains, 1,0,GENERIC_MASK}, - {"copy", xml_copy, 0,0,GENERIC_MASK}, - {"descendants", xml_descendants, 1,0,GENERIC_MASK}, - {"elements", xml_elements, 1,0,GENERIC_MASK}, - {"hasOwnProperty", xml_hasOwnProperty, 1,0,GENERIC_MASK}, - {"hasComplexContent", xml_hasComplexContent, 1,0,GENERIC_MASK}, - {"hasSimpleContent", xml_hasSimpleContent, 1,0,GENERIC_MASK}, - {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,XML_MASK}, - {"insertChildAfter", xml_insertChildAfter, 2,0,XML_MASK}, - {"insertChildBefore", xml_insertChildBefore, 2,0,XML_MASK}, - {js_length_str, xml_length, 0,0,GENERIC_MASK}, - {js_localName_str, xml_localName, 0,0,XML_MASK}, - {js_name_str, xml_name, 0,0,XML_MASK}, - {js_namespace_str, xml_namespace, 1,0,XML_MASK}, - {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,XML_MASK}, - {"nodeKind", xml_nodeKind, 0,0,XML_MASK}, - {"normalize", xml_normalize, 0,0,GENERIC_MASK}, - {js_xml_parent_str, xml_parent, 0,0,GENERIC_MASK}, - {"processingInstructions",xml_processingInstructions,1,0,GENERIC_MASK}, - {"prependChild", xml_prependChild, 1,0,XML_MASK}, - {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,GENERIC_MASK}, - {"removeNamespace", xml_removeNamespace, 1,0,XML_MASK}, - {"replace", xml_replace, 2,0,XML_MASK}, - {"setChildren", xml_setChildren, 1,0,XML_MASK}, - {"setLocalName", xml_setLocalName, 1,0,XML_MASK}, - {"setName", xml_setName, 1,0,XML_MASK}, - {"setNamespace", xml_setNamespace, 1,0,XML_MASK}, - {js_text_str, xml_text, 0,0,GENERIC_MASK}, - {js_toString_str, xml_toString, 0,0,GENERIC_MASK}, - {js_toXMLString_str, xml_toXMLString, 0,0,GENERIC_MASK}, - {js_toSource_str, xml_toXMLString, 0,0,GENERIC_MASK}, - {js_valueOf_str, xml_valueOf, 0,0,GENERIC_MASK}, - {0,0,0,0,0} -}; - -static JSBool -CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) -{ - int i; - const char *name; - jsval v; - - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - name = xml_static_props[i].name; - if (!JS_GetProperty(cx, from, name, &v)) - return JS_FALSE; - if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) - return JS_FALSE; - } - - name = xml_static_props[i].name; - if (!JS_GetProperty(cx, from, name, &v)) - return JS_FALSE; - if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -SetDefaultXMLSettings(JSContext *cx, JSObject *obj) -{ - int i; - jsval v; - - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - v = JSVAL_TRUE; - if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) - return JS_FALSE; - } - v = INT_TO_JSVAL(2); - return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); -} - -static JSBool -xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *settings; - - settings = JS_NewObject(cx, NULL, NULL, NULL); - if (!settings) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(settings); - return CopyXMLSettings(cx, obj, settings); -} - -static JSBool -xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - JSBool ok; - JSObject *settings; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - cx->xmlSettingFlags = 0; - ok = SetDefaultXMLSettings(cx, obj); - } else { - if (JSVAL_IS_PRIMITIVE(v)) - return JS_TRUE; - settings = JSVAL_TO_OBJECT(v); - cx->xmlSettingFlags = 0; - ok = CopyXMLSettings(cx, settings, obj); - } - if (ok) - cx->xmlSettingFlags |= XSF_CACHE_VALID; - return ok; -} - -static JSBool -xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *settings; - - settings = JS_NewObject(cx, NULL, NULL, NULL); - if (!settings) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(settings); - return SetDefaultXMLSettings(cx, settings); -} - -static JSFunctionSpec xml_static_methods[] = { - {"settings", xml_settings, 0,0,0}, - {"setSettings", xml_setSettings, 1,0,0}, - {"defaultSettings", xml_defaultSettings, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSXML *xml, *copy; - JSObject *xobj, *vobj; - JSClass *clasp; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - v = STRING_TO_JSVAL(cx->runtime->emptyString); - - xobj = ToXML(cx, v); - if (!xobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(xobj); - xml = (JSXML *) JS_GetPrivate(cx, xobj); - - if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, vobj); - if (clasp == &js_XMLClass || - (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { - /* No need to lock obj, it's newly constructed and thread local. */ - copy = DeepCopy(cx, xml, obj, 0); - if (!copy) - return JS_FALSE; - JS_ASSERT(copy->object == obj); - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - } - return JS_TRUE; -} - -static JSBool -XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSObject *vobj, *listobj; - JSXML *xml, *list; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - v = STRING_TO_JSVAL(cx->runtime->emptyString); - - if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) { - xml = (JSXML *) JS_GetPrivate(cx, vobj); - if (xml->xml_class == JSXML_CLASS_LIST) { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(listobj); - - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (!Append(cx, list, xml)) - return JS_FALSE; - return JS_TRUE; - } - } - } - - /* Toggle on XML support since the script has explicitly requested it. */ - listobj = ToXMLList(cx, v); - if (!listobj) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(listobj); - return JS_TRUE; -} - -#define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) -#define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) -#define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) - -static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { - JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ - JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ - JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ -}; - -#ifdef DEBUG_notme -JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); -uint32 xml_serial; -#endif - -JSXML * -js_NewXML(JSContext *cx, JSXMLClass xml_class) -{ - JSXML *xml; - - xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); - if (!xml) - return NULL; - - xml->object = NULL; - xml->domnode = NULL; - xml->parent = NULL; - xml->name = NULL; - xml->xml_class = xml_class; - xml->xml_flags = 0; - if (JSXML_CLASS_HAS_VALUE(xml_class)) { - xml->xml_value = cx->runtime->emptyString; - } else { - XMLArrayInit(cx, &xml->xml_kids, 0); - if (xml_class == JSXML_CLASS_LIST) { - xml->xml_target = NULL; - xml->xml_targetprop = NULL; - } else { - XMLArrayInit(cx, &xml->xml_namespaces, 0); - XMLArrayInit(cx, &xml->xml_attrs, 0); - } - } - -#ifdef DEBUG_notme - JS_APPEND_LINK(&xml->links, &xml_leaks); - xml->serial = xml_serial++; -#endif - METER(xml_stats.xml); - METER(xml_stats.livexml); - return xml; -} - -static void -xml_mark_tail(JSContext *cx, JSXML *xml, void *arg) -{ - XMLArrayTrim(&xml->xml_kids); - - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_target) - JS_MarkGCThing(cx, xml->xml_target, "target", arg); - if (xml->xml_targetprop) - JS_MarkGCThing(cx, xml->xml_targetprop, "targetprop", arg); - } else { - namespace_mark_vector(cx, - (JSXMLNamespace **) xml->xml_namespaces.vector, - xml->xml_namespaces.length, - arg); - XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); - XMLArrayTrim(&xml->xml_namespaces); - - xml_mark_vector(cx, - (JSXML **) xml->xml_attrs.vector, - xml->xml_attrs.length, - arg); - XMLArrayCursorMark(cx, xml->xml_attrs.cursors); - XMLArrayTrim(&xml->xml_attrs); - } -} - -void -js_MarkXML(JSContext *cx, JSXML *xml, void *arg) -{ - JS_MarkGCThing(cx, xml->object, js_object_str, arg); - JS_MarkGCThing(cx, xml->name, js_name_str, arg); - JS_MarkGCThing(cx, xml->parent, js_xml_parent_str, arg); - - if (JSXML_HAS_VALUE(xml)) { - JS_MarkGCThing(cx, xml->xml_value, "value", arg); - } else { - xml_mark_vector(cx, - (JSXML **) xml->xml_kids.vector, - xml->xml_kids.length, - arg); - XMLArrayCursorMark(cx, xml->xml_kids.cursors); - - xml_mark_tail(cx, xml, arg); - } -} - -void -js_FinalizeXML(JSContext *cx, JSXML *xml) -{ - if (JSXML_HAS_KIDS(xml)) { - XMLArrayFinish(cx, &xml->xml_kids); - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - XMLArrayFinish(cx, &xml->xml_namespaces); - XMLArrayFinish(cx, &xml->xml_attrs); - } - } - -#ifdef DEBUG_notme - JS_REMOVE_LINK(&xml->links); -#endif - - UNMETER(xml_stats.livexml); -} - -JSObject * -js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) -{ - jsval nsval; - JSXMLNamespace *ns; - JSXMLArray nsarray; - JSXML *xml; - - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return NULL; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); - ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - - if (!XMLArrayInit(cx, &nsarray, 1)) - return NULL; - - XMLARRAY_APPEND(cx, &nsarray, ns); - xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); - XMLArrayFinish(cx, &nsarray); - if (!xml) - return NULL; - - return xml->object; -} - -JSObject * -js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) -{ - JSXML *xml; - JSObject *obj; - JSTempValueRooter tvr; - - xml = js_NewXML(cx, xml_class); - if (!xml) - return NULL; - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(xml), &tvr); - obj = js_GetXMLObject(cx, xml); - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -static JSObject * -NewXMLObject(JSContext *cx, JSXML *xml) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, xml)) { - cx->newborn[GCX_OBJECT] = NULL; - return NULL; - } - METER(xml_stats.xmlobj); - METER(xml_stats.livexmlobj); - return obj; -} - -JSObject * -js_GetXMLObject(JSContext *cx, JSXML *xml) -{ - JSObject *obj; - - obj = xml->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == xml); - return obj; - } - - /* - * A JSXML cannot be shared among threads unless it has an object. - * A JSXML cannot be given an object unless: - * (a) it has no parent; or - * (b) its parent has no object (therefore is thread-private); or - * (c) its parent's object is locked. - * - * Once given an object, a JSXML is immutable. - */ - JS_ASSERT(!xml->parent || - !xml->parent->object || - JS_IS_OBJ_LOCKED(cx, xml->parent->object)); - - obj = NewXMLObject(cx, xml); - if (!obj) - return NULL; - xml->object = obj; - return obj; -} - -JSObject * -js_InitNamespaceClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, - namespace_props, namespace_methods, NULL, NULL); -} - -JSObject * -js_InitQNameClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, - qname_props, qname_methods, NULL, NULL); -} - -JSObject * -js_InitAttributeNameClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, - qname_props, qname_methods, NULL, NULL); -} - -JSObject * -js_InitAnyNameClass(JSContext *cx, JSObject *obj) -{ - jsval v; - - if (!js_GetAnyName(cx, &v)) - return NULL; - return JSVAL_TO_OBJECT(v); -} - -JSObject * -js_InitXMLClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *pobj, *ctor; - JSFunctionSpec *fs; - JSFunction *fun; - JSXML *xml; - JSProperty *prop; - JSScopeProperty *sprop; - jsval cval, argv[1], junk; - - /* Define the isXMLName function. */ - if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) - return NULL; - - /* Define the XML class constructor and prototype. */ - proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, - NULL, NULL, - xml_static_props, xml_static_methods); - if (!proto) - return NULL; - - /* - * XXX Hack alert: expand JS_DefineFunctions here to copy fs->extra into - * fun->spare, clearing fun->extra. No xml_methods require extra local GC - * roots allocated after actual arguments on the VM stack, but we need a - * way to tell which methods work only on XML objects, which work only on - * XMLList objects, and which work on either. - */ - for (fs = xml_methods; fs->name; fs++) { - fun = JS_DefineFunction(cx, proto, fs->name, fs->call, fs->nargs, - fs->flags); - if (!fun) - return NULL; - fun->extra = 0; - fun->spare = fs->extra; - } - - xml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!xml || !JS_SetPrivate(cx, proto, xml)) - return NULL; - xml->object = proto; - METER(xml_stats.xmlobj); - METER(xml_stats.livexmlobj); - - /* - * Prepare to set default settings on the XML constructor we just made. - * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, - * which is xml_getProperty, which creates a new XMLList every time! We - * must instead call js_LookupProperty directly. - */ - if (!js_LookupProperty(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - &pobj, &prop)) { - return NULL; - } - JS_ASSERT(prop); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); - cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); - OBJ_DROP_PROPERTY(cx, pobj, prop); - JS_ASSERT(JSVAL_IS_FUNCTION(cx, cval)); - - /* Set default settings. */ - ctor = JSVAL_TO_OBJECT(cval); - argv[0] = JSVAL_VOID; - if (!xml_setSettings(cx, ctor, 1, argv, &junk)) - return NULL; - - /* Define the XMLList function and give it the same prototype as XML. */ - fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); - if (!fun) - return NULL; - if (!js_SetClassPrototype(cx, fun->object, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { - return NULL; - } - return proto; -} - -JSObject * -js_InitXMLClasses(JSContext *cx, JSObject *obj) -{ - if (!js_InitNamespaceClass(cx, obj)) - return NULL; - if (!js_InitQNameClass(cx, obj)) - return NULL; - if (!js_InitAttributeNameClass(cx, obj)) - return NULL; - if (!js_InitAnyNameClass(cx, obj)) - return NULL; - return js_InitXMLClass(cx, obj); -} - -JSBool -js_GetFunctionNamespace(JSContext *cx, jsval *vp) -{ - JSRuntime *rt; - JSObject *obj; - JSAtom *atom; - JSString *prefix, *uri; - - /* An invalid URI, for internal use only, guaranteed not to collide. */ - static const char anti_uri[] = "@mozilla.org/js/function"; - - rt = cx->runtime; - obj = rt->functionNamespaceObject; - if (!obj) { - atom = js_Atomize(cx, js_function_str, 8, 0); - JS_ASSERT(atom); - prefix = ATOM_TO_STRING(atom); - - atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); - if (!atom) - return JS_FALSE; - rt->atomState.lazy.functionNamespaceURIAtom = atom; - - uri = ATOM_TO_STRING(atom); - obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); - if (!obj) - return JS_FALSE; - - /* - * Avoid entraining any in-scope Object.prototype. The loss of - * Namespace.prototype is not detectable, as there is no way to - * refer to this instance in scripts. When used to qualify method - * names, its prefix and uri references are copied to the QName. - */ - OBJ_SET_PROTO(cx, obj, NULL); - OBJ_SET_PARENT(cx, obj, NULL); - rt->functionNamespaceObject = obj; - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- - * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, - * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a - * lightweight function activation). There's no requirement that fp->varobj - * lie directly on fp->scopeChain, although it should be reachable using the - * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). - * - * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it - * creates a default namespace via 'new Namespace()'. In contrast, Set uses - * its v argument as the uri of a new Namespace, with "" as the prefix. See - * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, - * the default XML namespace will be set to ("", n.uri). So the uri string - * is really the only usefully stored value of the default namespace. - */ -JSBool -js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) -{ - JSStackFrame *fp; - JSObject *nsobj, *obj, *tmp; - jsval v; - - fp = cx->fp; - nsobj = fp->xmlNamespace; - if (nsobj) { - *vp = OBJECT_TO_JSVAL(nsobj); - return JS_TRUE; - } - - obj = NULL; - for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { - obj = tmp; - if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - fp->xmlNamespace = JSVAL_TO_OBJECT(v); - *vp = v; - return JS_TRUE; - } - } - - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); - if (!nsobj) - return JS_FALSE; - v = OBJECT_TO_JSVAL(nsobj); - if (obj && - !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, - JS_PropertyStub, JS_PropertyStub, - JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - fp->xmlNamespace = nsobj; - *vp = v; - return JS_TRUE; -} - -JSBool -js_SetDefaultXMLNamespace(JSContext *cx, jsval v) -{ - jsval argv[2]; - JSObject *nsobj, *varobj; - JSStackFrame *fp; - - argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); - argv[1] = v; - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, - 2, argv); - if (!nsobj) - return JS_FALSE; - v = OBJECT_TO_JSVAL(nsobj); - - fp = cx->fp; - varobj = fp->varobj; - if (varobj) { - if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, - JS_PropertyStub, JS_PropertyStub, - JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - } else { - JS_ASSERT(fp->fun && !(fp->fun->flags & JSFUN_HEAVYWEIGHT)); - } - fp->xmlNamespace = JSVAL_TO_OBJECT(v); - return JS_TRUE; -} - -JSBool -js_ToAttributeName(JSContext *cx, jsval *vp) -{ - JSXMLQName *qn; - - qn = ToAttributeName(cx, *vp); - if (!qn) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(qn->object); - return JS_TRUE; -} - -JSString * -js_EscapeAttributeValue(JSContext *cx, JSString *str) -{ - return EscapeAttributeValue(cx, NULL, str); -} - -JSString * -js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) -{ - size_t len, len2, newlen; - jschar *chars; - - if (JSSTRING_IS_DEPENDENT(str) || - !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { - str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), - 0); - if (!str) - return NULL; - } - - len = str->length; - len2 = JSSTRING_LENGTH(str2); - newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; - chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); - if (!chars) - return NULL; - - /* - * Reallocating str (because we know it has no other references) requires - * purging any deflated string cached for it. - */ - js_PurgeDeflatedStringCache(str); - - str->chars = chars; - str->length = newlen; - chars += len; - if (isName) { - *chars++ = ' '; - js_strncpy(chars, JSSTRING_CHARS(str2), len2); - chars += len2; - } else { - *chars++ = '='; - *chars++ = '"'; - js_strncpy(chars, JSSTRING_CHARS(str2), len2); - chars += len2; - *chars++ = '"'; - } - *chars = 0; - return str; -} - -JSString * -js_EscapeElementValue(JSContext *cx, JSString *str) -{ - return EscapeElementValue(cx, NULL, str); -} - -JSString * -js_ValueToXMLString(JSContext *cx, jsval v) -{ - return ToXMLString(cx, v); -} - -static JSBool -anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = ATOM_KEY(cx->runtime->atomState.starAtom); - return JS_TRUE; -} - -JSBool -js_GetAnyName(JSContext *cx, jsval *vp) -{ - JSRuntime *rt; - JSObject *obj; - JSXMLQName *qn; - - rt = cx->runtime; - obj = rt->anynameObject; - if (!obj) { - qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, - ATOM_TO_STRING(rt->atomState.starAtom)); - if (!qn) - return JS_FALSE; - - obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->newborn[GCX_OBJECT] = NULL; - return JS_FALSE; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - /* - * Avoid entraining any in-scope Object.prototype. This loses the - * default toString inheritance, but no big deal: we want a better - * custom one for clearer diagnostics. - */ - if (!JS_DefineFunction(cx, obj, js_toString_str, anyname_toString, - 0, 0)) { - return JS_FALSE; - } - OBJ_SET_PROTO(cx, obj, NULL); - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - rt->anynameObject = obj; - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -JSBool -js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) -{ - JSXMLQName *qn; - jsid funid, id; - JSObject *obj, *pobj, *lastobj; - JSProperty *prop; - const char *printable; - - qn = ToXMLName(cx, name, &funid); - if (!qn) - return JS_FALSE; - id = OBJECT_TO_JSID(qn->object); - - obj = cx->fp->scopeChain; - do { - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* - * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML - * object to carry scope chain linkage in js_FilterXMLList. - */ - pobj = OBJ_THIS_OBJECT(cx, obj); - if (OBJECT_IS_XML(cx, pobj)) { - *objp = pobj; - *namep = ID_TO_VALUE(id); - return JS_TRUE; - } - } - - lastobj = obj; - } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); - - printable = js_ValueToPrintableString(cx, name); - if (printable) { - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_UNDEFINED_XML_NAME, printable); - } - return JS_FALSE; -} - -JSBool -js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) -{ - return GetProperty(cx, obj, name, vp); -} - -JSBool -js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) -{ - return PutProperty(cx, obj, name, vp); -} - -static JSXML * -GetPrivate(JSContext *cx, JSObject *obj, const char *method) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_METHOD, - js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); - } - return xml; -} - -JSBool -js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *list; - - xml = GetPrivate(cx, obj, "descendants internal method"); - if (!xml) - return JS_FALSE; - - list = Descendants(cx, xml, id); - if (!list) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(list->object); - return JS_TRUE; -} - -JSBool -js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) -{ - JSXML *list; - uint32 n; - jsval junk; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - for (n = list->xml_kids.length; n != 0; --n) { - if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) -{ - JSBool ok, match; - JSStackFrame *fp; - JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; - JSXML *xml, *list, *result, *kid; - JSXMLArrayCursor cursor; - - ok = JS_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - /* All control flow after this point must exit via label out or bad. */ - fp = cx->fp; - scobj = fp->scopeChain; - withobj = NULL; - xml = GetPrivate(cx, obj, "filtering predicate operator"); - if (!xml) - goto bad; - - if (xml->xml_class == JSXML_CLASS_LIST) { - list = xml; - } else { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - goto bad; - list = (JSXML *) JS_GetPrivate(cx, listobj); - ok = Append(cx, list, xml); - if (!ok) - goto out; - } - - resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!resobj) - goto bad; - result = (JSXML *) JS_GetPrivate(cx, resobj); - - /* Hoist the scope chain update out of the loop over kids. */ - withobj = js_NewWithObject(cx, NULL, scobj, -1); - if (!withobj) - goto bad; - fp->scopeChain = withobj; - - XMLArrayCursorInit(&cursor, &list->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - break; - OBJ_SET_PROTO(cx, withobj, kidobj); - ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); - if (ok && match) - ok = Append(cx, result, kid); - if (!ok) - break; - } - XMLArrayCursorFinish(&cursor); - if (!ok) - goto out; - if (kid) - goto bad; - - *vp = OBJECT_TO_JSVAL(resobj); - -out: - if (withobj) { - fp->scopeChain = scobj; - JS_SetPrivate(cx, withobj, NULL); - } - JS_LeaveLocalRootScope(cx); - return ok; -bad: - ok = JS_FALSE; - goto out; -} - -JSObject * -js_ValueToXMLObject(JSContext *cx, jsval v) -{ - return ToXML(cx, v); -} - -JSObject * -js_ValueToXMLListObject(JSContext *cx, jsval v) -{ - return ToXMLList(cx, v); -} - -JSObject * -js_CloneXMLObject(JSContext *cx, JSObject *obj) -{ - uintN flags; - JSXML *xml; - - if (!GetXMLSettingFlags(cx, &flags)) - return NULL; - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (flags & (XSF_IGNORE_COMMENTS | - XSF_IGNORE_PROCESSING_INSTRUCTIONS | - XSF_IGNORE_WHITESPACE)) { - xml = DeepCopy(cx, xml, NULL, flags); - if (!xml) - return NULL; - return xml->object; - } - return NewXMLObject(cx, xml); -} - -JSObject * -js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, - JSString *value) -{ - uintN flags; - JSObject *obj; - JSXML *xml; - JSXMLQName *qn; - - if (!GetXMLSettingFlags(cx, &flags)) - return NULL; - - if ((xml_class == JSXML_CLASS_COMMENT && - (flags & XSF_IGNORE_COMMENTS)) || - (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && - (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { - return js_NewXMLObject(cx, JSXML_CLASS_TEXT); - } - - obj = js_NewXMLObject(cx, xml_class); - if (!obj) - return NULL; - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (name) { - qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); - if (!qn) - return NULL; - xml->name = qn; - } - xml->xml_value = value; - return obj; -} - -JSString * -js_MakeXMLCDATAString(JSContext *cx, JSString *str) -{ - return MakeXMLCDATAString(cx, NULL, str); -} - -JSString * -js_MakeXMLCommentString(JSContext *cx, JSString *str) -{ - return MakeXMLCommentString(cx, NULL, str); -} - -JSString * -js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) -{ - return MakeXMLPIString(cx, NULL, name, str); -} - -#endif /* JS_HAS_XML_SUPPORT */ |
