/js/src/jsxml.cpp
http://github.com/zpao/v8monkey · C++ · 8109 lines · 6438 code · 936 blank · 735 comment · 1808 complexity · e16b88c7531ad81e8cde99e3c49fc788 MD5 · raw file
Large files are truncated click here to view the full file
- /* -*- 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 "jsversion.h"
- #if JS_HAS_XML_SUPPORT
- #include <math.h>
- #include <stdlib.h>
- #include <string.h>
- #include "mozilla/Util.h"
- #include "jstypes.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 "jsgcmark.h"
- #include "jslock.h"
- #include "jsnum.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsscope.h"
- #include "jsscript.h"
- #include "jsstr.h"
- #include "jsxml.h"
- #include "frontend/Parser.h"
- #include "frontend/TokenStream.h"
- #include "vm/GlobalObject.h"
- #include "jsatominlines.h"
- #include "jsinferinlines.h"
- #include "jsobjinlines.h"
- #include "jsstrinlines.h"
- #include "vm/Stack-inl.h"
- #include "vm/String-inl.h"
- #ifdef DEBUG
- #include <string.h> /* for #ifdef DEBUG memset calls */
- #endif
- using namespace mozilla;
- using namespace js;
- using namespace js::gc;
- using namespace js::types;
- template<class T, class U>
- struct IdentityOp
- {
- typedef JSBool (* compare)(const T *a, const U *b);
- };
- template<class T>
- static JSBool
- pointer_match(const T *a, const T *b)
- {
- return a == b;
- }
- /*
- * 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
- * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
- */
- static inline bool
- js_EnterLocalRootScope(JSContext *cx)
- {
- return true;
- }
- static inline void
- js_LeaveLocalRootScope(JSContext *cx)
- {
- }
- static inline void
- js_LeaveLocalRootScopeWithResult(JSContext *cx, Value rval)
- {
- }
- static inline void
- js_LeaveLocalRootScopeWithResult(JSContext *cx, void *rval)
- {
- }
- /*
- * Random utilities and global functions.
- */
- 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[] = """;
- const char js_leftcurly_entity_str[] = "{";
- #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*')
- static JSBool
- GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
- static JSBool
- IsDeclared(const JSObject *obj)
- {
- jsval v;
- JS_ASSERT(obj->getClass() == &NamespaceClass);
- v = obj->getNamespaceDeclared();
- JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
- return v == JSVAL_TRUE;
- }
- static JSBool
- xml_isXMLName(JSContext *cx, uintN argc, jsval *vp)
- {
- *vp = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argc ? vp[2] : JSVAL_VOID));
- return JS_TRUE;
- }
- size_t sE4XObjectsCreated = 0;
- /*
- * This wrapper is needed because NewBuiltinClassInstance doesn't
- * call the constructor, and we need a place to set the
- * HAS_EQUALITY bit.
- */
- static inline JSObject *
- NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
- {
- if (!cx->runningWithTrustedPrincipals())
- ++sE4XObjectsCreated;
- return NewBuiltinClassInstance(cx, clasp);
- }
- #define DEFINE_GETTER(name,code) \
- static JSBool \
- name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
- { \
- code; \
- return true; \
- }
- /*
- * Namespace class and library functions.
- */
- DEFINE_GETTER(NamePrefix_getter,
- if (obj->getClass() == &NamespaceClass) *vp = obj->getNamePrefixVal())
- DEFINE_GETTER(NameURI_getter,
- if (obj->getClass() == &NamespaceClass) *vp = obj->getNameURIVal())
- static JSBool
- namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
- {
- JSObject *obj2;
- JS_ASSERT(v->isObjectOrNull());
- obj2 = v->toObjectOrNull();
- *bp = (!obj2 || obj2->getClass() != &NamespaceClass)
- ? JS_FALSE
- : EqualStrings(obj->getNameURI(), obj2->getNameURI());
- return JS_TRUE;
- }
- JS_FRIEND_DATA(Class) js::NamespaceClass = {
- "Namespace",
- JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) |
- JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
- JS_PropertyStub, /* addProperty */
- JS_PropertyStub, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
- JS_EnumerateStub,
- JS_ResolveStub,
- JS_ConvertStub,
- JS_FinalizeStub,
- NULL, /* reserved0 */
- NULL, /* checkAccess */
- NULL, /* call */
- NULL, /* construct */
- NULL, /* xdrObject */
- NULL, /* hasInstance */
- NULL, /* mark */
- {
- namespace_equality,
- NULL, /* outerObject */
- NULL, /* innerObject */
- NULL, /* iteratorObject */
- NULL, /* wrappedObject */
- }
- };
- #define NAMESPACE_ATTRS \
- (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
- static JSPropertySpec namespace_props[] = {
- {js_prefix_str, 0, NAMESPACE_ATTRS, NamePrefix_getter, 0},
- {js_uri_str, 0, NAMESPACE_ATTRS, NameURI_getter, 0},
- {0,0,0,0,0}
- };
- static JSBool
- namespace_toString(JSContext *cx, uintN argc, Value *vp)
- {
- JSObject *obj = ToObject(cx, &vp[1]);
- if (!obj)
- return JS_FALSE;
- if (!obj->isNamespace()) {
- ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &NamespaceClass);
- return JS_FALSE;
- }
- *vp = obj->getNameURIVal();
- return JS_TRUE;
- }
- static JSFunctionSpec namespace_methods[] = {
- JS_FN(js_toString_str, namespace_toString, 0,0),
- JS_FS_END
- };
- static JSObject *
- NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared)
- {
- JSObject *obj;
- obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass);
- if (!obj)
- return NULL;
- JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
- JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
- JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
- /* Per ECMA-357, 13.2.5, these properties must be "own". */
- if (!JS_DefineProperties(cx, obj, namespace_props))
- return NULL;
- if (prefix)
- obj->setNamePrefix(prefix);
- if (uri)
- obj->setNameURI(uri);
- if (declared)
- obj->setNamespaceDeclared(JSVAL_TRUE);
- return obj;
- }
- /*
- * QName class and library functions.
- */
- DEFINE_GETTER(QNameNameURI_getter,
- if (obj->getClass() == &QNameClass)
- *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal())
- DEFINE_GETTER(QNameLocalName_getter,
- if (obj->getClass() == &QNameClass)
- *vp = obj->getQNameLocalNameVal())
- static JSBool
- qname_identity(JSObject *qna, const JSObject *qnb)
- {
- JSLinearString *uri1 = qna->getNameURI();
- JSLinearString *uri2 = qnb->getNameURI();
- if (!uri1 ^ !uri2)
- return JS_FALSE;
- if (uri1 && !EqualStrings(uri1, uri2))
- return JS_FALSE;
- return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName());
- }
- static JSBool
- qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp)
- {
- JSObject *obj2;
- obj2 = v->toObjectOrNull();
- *bp = (!obj2 || obj2->getClass() != &QNameClass)
- ? JS_FALSE
- : qname_identity(qn, obj2);
- return JS_TRUE;
- }
- JS_FRIEND_DATA(Class) js::QNameClass = {
- "QName",
- JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
- JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
- JS_PropertyStub, /* addProperty */
- JS_PropertyStub, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
- JS_EnumerateStub,
- JS_ResolveStub,
- JS_ConvertStub,
- JS_FinalizeStub,
- NULL, /* reserved0 */
- NULL, /* checkAccess */
- NULL, /* call */
- NULL, /* construct */
- NULL, /* xdrObject */
- NULL, /* hasInstance */
- NULL, /* mark */
- {
- qname_equality,
- NULL, /* outerObject */
- NULL, /* innerObject */
- NULL, /* iteratorObject */
- NULL, /* wrappedObject */
- }
- };
- /*
- * 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(Class) js::AttributeNameClass = {
- js_AttributeName_str,
- JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
- JSCLASS_IS_ANONYMOUS,
- JS_PropertyStub, /* addProperty */
- JS_PropertyStub, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
- JS_EnumerateStub,
- JS_ResolveStub,
- JS_ConvertStub,
- JS_FinalizeStub
- };
- JS_FRIEND_DATA(Class) js::AnyNameClass = {
- js_AnyName_str,
- JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
- JSCLASS_IS_ANONYMOUS,
- JS_PropertyStub, /* addProperty */
- JS_PropertyStub, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
- JS_EnumerateStub,
- JS_ResolveStub,
- JS_ConvertStub,
- JS_FinalizeStub
- };
- #define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
- static JSPropertySpec qname_props[] = {
- {js_uri_str, 0, QNAME_ATTRS, QNameNameURI_getter, 0},
- {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0},
- {0,0,0,0,0}
- };
- static JSString *
- ConvertQNameToString(JSContext *cx, JSObject *obj)
- {
- JS_ASSERT(obj->isQName());
- JSString *uri = obj->getNameURI();
- JSString *str;
- if (!uri) {
- /* No uri means wildcard qualifier. */
- str = cx->runtime->atomState.starQualifierAtom;
- } else if (uri->empty()) {
- /* Empty string for uri means localName is in no namespace. */
- str = cx->runtime->emptyString;
- } else {
- JSString *qualstr = cx->runtime->atomState.qualifierAtom;
- str = js_ConcatStrings(cx, uri, qualstr);
- if (!str)
- return NULL;
- }
- str = js_ConcatStrings(cx, str, obj->getQNameLocalName());
- if (!str)
- return NULL;
- if (obj->getClass() == &AttributeNameClass) {
- JS::Anchor<JSString *> anchor(str);
- size_t length = str->length();
- jschar *chars = (jschar *) cx->malloc_((length + 2) * sizeof(jschar));
- if (!chars)
- return JS_FALSE;
- *chars = '@';
- const jschar *strChars = str->getChars(cx);
- if (!strChars) {
- cx->free_(chars);
- return NULL;
- }
- js_strncpy(chars + 1, strChars, length);
- chars[++length] = 0;
- str = js_NewString(cx, chars, length);
- if (!str) {
- cx->free_(chars);
- return NULL;
- }
- }
- return str;
- }
- static JSBool
- qname_toString(JSContext *cx, uintN argc, Value *vp)
- {
- JSObject *obj = ToObject(cx, &vp[1]);
- if (!obj)
- return false;
- if (!obj->isQName()) {
- ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &QNameClass);
- return false;
- }
- JSString *str = ConvertQNameToString(cx, obj);
- if (!str)
- return false;
- vp->setString(str);
- return true;
- }
- static JSFunctionSpec qname_methods[] = {
- JS_FN(js_toString_str, qname_toString, 0,0),
- JS_FS_END
- };
- static bool
- InitXMLQName(JSContext *cx, JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
- JSAtom *localName)
- {
- JS_ASSERT(obj->isQName());
- JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
- JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
- JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
- /* Per ECMA-357, 13.3.5, these properties must be "own". */
- if (!JS_DefineProperties(cx, obj, qname_props))
- return false;
- if (uri)
- obj->setNameURI(uri);
- if (prefix)
- obj->setNamePrefix(prefix);
- if (localName)
- obj->setQNameLocalName(localName);
- return true;
- }
- static JSObject *
- NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
- JSAtom *localName)
- {
- JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass);
- if (!obj)
- return NULL;
- if (!InitXMLQName(cx, obj, uri, prefix, localName))
- return NULL;
- return obj;
- }
- static JSObject *
- NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
- JSAtom *localName)
- {
- /*
- * AttributeName is an internal anonymous class which instances are not
- * exposed to scripts.
- */
- JSObject *parent = GetGlobalForScopeChain(cx);
- JSObject *obj = NewObjectWithGivenProto(cx, &AttributeNameClass, NULL, parent);
- if (!obj)
- return NULL;
- JS_ASSERT(obj->isQName());
- if (!InitXMLQName(cx, obj, uri, prefix, localName))
- return NULL;
- return obj;
- }
- JSObject *
- js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval)
- {
- Value argv[2];
- /*
- * ECMA-357 11.1.2,
- * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
- * production, step 2.
- */
- if (nsval.isObject() &&
- nsval.toObject().getClass() == &AnyNameClass) {
- argv[0].setNull();
- } else {
- argv[0] = nsval;
- }
- argv[1] = lnval;
- return JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 2, argv);
- }
- static JSBool
- IsXMLName(const jschar *cp, size_t n)
- {
- JSBool rv;
- jschar c;
- rv = JS_FALSE;
- if (n != 0 && unicode::IsXMLNamespaceStart(*cp)) {
- while (--n != 0) {
- c = *++cp;
- if (!unicode::IsXMLNamespacePart(c))
- return rv;
- }
- rv = JS_TRUE;
- }
- return rv;
- }
- JSBool
- js_IsXMLName(JSContext *cx, jsval v)
- {
- JSLinearString *name = NULL;
- 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) &&
- JSVAL_TO_OBJECT(v)->isQName()) {
- name = JSVAL_TO_OBJECT(v)->getQNameLocalName();
- } else {
- older = JS_SetErrorReporter(cx, NULL);
- JSString *str = ToString(cx, v);
- if (str)
- name = str->ensureLinear(cx);
- JS_SetErrorReporter(cx, older);
- if (!name) {
- JS_ClearPendingException(cx);
- return JS_FALSE;
- }
- }
- return IsXMLName(name->chars(), name->length());
- }
- /*
- * When argc is -1, it indicates argv is empty but the code should behave as
- * if argc is 1 and argv[0] is JSVAL_VOID.
- */
- static JSBool
- NamespaceHelper(JSContext *cx, intN argc, jsval *argv, jsval *rval)
- {
- jsval urival, prefixval;
- JSObject *uriobj;
- JSBool isNamespace, isQName;
- Class *clasp;
- JSLinearString *empty, *prefix, *uri;
- isNamespace = isQName = JS_FALSE;
- #ifdef __GNUC__ /* suppress bogus gcc warnings */
- uriobj = NULL;
- #endif
- if (argc <= 0) {
- urival = JSVAL_VOID;
- } else {
- urival = argv[argc > 1];
- if (!JSVAL_IS_PRIMITIVE(urival)) {
- uriobj = JSVAL_TO_OBJECT(urival);
- clasp = uriobj->getClass();
- isNamespace = (clasp == &NamespaceClass);
- isQName = (clasp == &QNameClass);
- }
- }
- /* Namespace called as function. */
- if (argc == 1 && isNamespace) {
- /* Namespace called with one Namespace argument is identity. */
- *rval = urival;
- return JS_TRUE;
- }
- JSObject *obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass);
- if (!obj)
- return JS_FALSE;
- /* Per ECMA-357, 13.2.5, these properties must be "own". */
- if (!JS_DefineProperties(cx, obj, namespace_props))
- return JS_FALSE;
- *rval = OBJECT_TO_JSVAL(obj);
- empty = cx->runtime->emptyString;
- obj->setNamePrefix(empty);
- obj->setNameURI(empty);
- if (argc == 1 || argc == -1) {
- if (isNamespace) {
- obj->setNameURI(uriobj->getNameURI());
- obj->setNamePrefix(uriobj->getNamePrefix());
- } else if (isQName && (uri = uriobj->getNameURI())) {
- obj->setNameURI(uri);
- obj->setNamePrefix(uriobj->getNamePrefix());
- } else {
- JSString *str = ToString(cx, urival);
- if (!str)
- return JS_FALSE;
- uri = str->ensureLinear(cx);
- if (!uri)
- return JS_FALSE;
- obj->setNameURI(uri);
- if (!uri->empty())
- obj->clearNamePrefix();
- }
- } else if (argc == 2) {
- if (!isQName || !(uri = uriobj->getNameURI())) {
- JSString *str = ToString(cx, urival);
- if (!str)
- return JS_FALSE;
- uri = str->ensureLinear(cx);
- if (!uri)
- return JS_FALSE;
- }
- obj->setNameURI(uri);
- prefixval = argv[0];
- if (uri->empty()) {
- if (!JSVAL_IS_VOID(prefixval)) {
- JSString *str = ToString(cx, prefixval);
- if (!str)
- return JS_FALSE;
- if (!str->empty()) {
- JSAutoByteString bytes;
- if (js_ValueToPrintable(cx, StringValue(str), &bytes)) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
- }
- return JS_FALSE;
- }
- }
- } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
- obj->clearNamePrefix();
- } else {
- JSString *str = ToString(cx, prefixval);
- if (!str)
- return JS_FALSE;
- prefix = str->ensureLinear(cx);
- if (!prefix)
- return JS_FALSE;
- obj->setNamePrefix(prefix);
- }
- }
- return JS_TRUE;
- }
- static JSBool
- Namespace(JSContext *cx, uintN argc, Value *vp)
- {
- return NamespaceHelper(cx, argc, vp + 2, vp);
- }
- /*
- * When argc is -1, it indicates argv is empty but the code should behave as
- * if argc is 1 and argv[0] is JSVAL_VOID.
- */
- static JSBool
- QNameHelper(JSContext *cx, intN argc, jsval *argv, jsval *rval)
- {
- jsval nameval, nsval;
- JSBool isQName, isNamespace;
- JSObject *qn;
- JSLinearString *uri, *prefix;
- JSObject *obj2;
- JSAtom *name;
- if (argc <= 0) {
- nameval = JSVAL_VOID;
- isQName = JS_FALSE;
- } else {
- nameval = argv[argc > 1];
- isQName =
- !JSVAL_IS_PRIMITIVE(nameval) &&
- JSVAL_TO_OBJECT(nameval)->getClass() == &QNameClass;
- }
- /* 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. */
- JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass);
- if (!obj)
- return JS_FALSE;
- *rval = OBJECT_TO_JSVAL(obj);
- if (isQName) {
- /* If namespace is not specified and name is a QName, clone it. */
- qn = JSVAL_TO_OBJECT(nameval);
- if (argc == 1) {
- uri = qn->getNameURI();
- prefix = qn->getNamePrefix();
- name = qn->getQNameLocalName();
- goto out;
- }
- /* Namespace and qname were passed -- use the qname's localName. */
- nameval = qn->getQNameLocalNameVal();
- }
- if (argc == 0) {
- name = cx->runtime->emptyString;
- } else if (argc < 0) {
- name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
- } else {
- if (!js_ValueToAtom(cx, nameval, &name))
- return false;
- }
- if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
- nsval = argv[0];
- } else if (IS_STAR(name)) {
- nsval = JSVAL_NULL;
- } else {
- if (!js_GetDefaultXMLNamespace(cx, &nsval))
- return JS_FALSE;
- JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
- JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
- &NamespaceClass);
- }
- if (JSVAL_IS_NULL(nsval)) {
- /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
- prefix = uri = 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)) {
- obj2 = JSVAL_TO_OBJECT(nsval);
- isNamespace = (obj2->getClass() == &NamespaceClass);
- isQName = (obj2->getClass() == &QNameClass);
- }
- #ifdef __GNUC__ /* suppress bogus gcc warnings */
- else obj2 = NULL;
- #endif
- if (isNamespace) {
- uri = obj2->getNameURI();
- prefix = obj2->getNamePrefix();
- } else if (isQName && (uri = obj2->getNameURI())) {
- JS_ASSERT(argc > 1);
- prefix = obj2->getNamePrefix();
- } else {
- JS_ASSERT(argc > 1);
- JSString *str = ToString(cx, nsval);
- if (!str)
- return JS_FALSE;
- uri = str->ensureLinear(cx);
- 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 = uri->empty() ? cx->runtime->emptyString : NULL;
- }
- }
- out:
- return InitXMLQName(cx, obj, uri, prefix, name);
- }
- static JSBool
- QName(JSContext *cx, uintN argc, Value *vp)
- {
- return QNameHelper(cx, argc, vp + 2, vp);
- }
- /*
- * XMLArray library functions.
- */
- static JSBool
- namespace_identity(const JSObject *nsa, const JSObject *nsb)
- {
- JSLinearString *prefixa = nsa->getNamePrefix();
- JSLinearString *prefixb = nsb->getNamePrefix();
- if (prefixa && prefixb) {
- if (!EqualStrings(prefixa, prefixb))
- return JS_FALSE;
- } else {
- if (prefixa || prefixb)
- return JS_FALSE;
- }
- return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
- }
- static JSBool
- attr_identity(const JSXML *xmla, const JSXML *xmlb)
- {
- return qname_identity(xmla->name, xmlb->name);
- }
- void
- js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSXML> *cursor)
- {
- for (; cursor; cursor = cursor->next) {
- if (cursor->root)
- MarkXML(trc, (const HeapPtr<JSXML> &)cursor->root, "cursor_root");
- }
- }
- void
- js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSObject> *cursor)
- {
- for (; cursor; cursor = cursor->next) {
- if (cursor->root)
- MarkObject(trc, (const HeapPtr<JSObject> &)cursor->root, "cursor_root");
- }
- }
- template<class T>
- static HeapPtr<T> *
- ReallocateVector(HeapPtr<T> *vector, size_t count)
- {
- #if JS_BITS_PER_WORD == 32
- if (count > ~(size_t)0 / sizeof(HeapPtr<T>))
- return NULL;
- #endif
- size_t size = count * sizeof(HeapPtr<T>);
- return (HeapPtr<T> *) OffTheBooks::realloc_(vector, size);
- }
- /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
- template<class T>
- bool
- JSXMLArray<T>::setCapacity(JSContext *cx, uint32_t newCapacity)
- {
- if (newCapacity == 0) {
- /* We could let realloc(p, 0) free this, but purify gets confused. */
- if (vector) {
- if (cx)
- cx->free_(vector);
- else
- Foreground::free_(vector);
- }
- vector = NULL;
- } else {
- HeapPtr<T> *tmp = ReallocateVector(vector, newCapacity);
- if (!tmp) {
- if (cx)
- JS_ReportOutOfMemory(cx);
- return false;
- }
- vector = tmp;
- }
- capacity = JSXML_PRESET_CAPACITY | newCapacity;
- return true;
- }
- template<class T>
- void
- JSXMLArray<T>::trim()
- {
- if (capacity & JSXML_PRESET_CAPACITY)
- return;
- if (length < capacity)
- setCapacity(NULL, length);
- }
- template<class T>
- void
- JSXMLArray<T>::finish(JSContext *cx)
- {
- if (!cx->runtime->gcRunning) {
- /* We need to clear these to trigger a write barrier. */
- for (uint32_t i = 0; i < length; i++)
- vector[i].~HeapPtr<T>();
- }
- cx->free_(vector);
- while (JSXMLArrayCursor<T> *cursor = cursors)
- cursor->disconnect();
- #ifdef DEBUG
- memset(this, 0xd5, sizeof *this);
- #endif
- }
- #define XML_NOT_FOUND UINT32_MAX
- template<class T, class U>
- static uint32_t
- XMLArrayFindMember(const JSXMLArray<T> *array, U *elt, typename IdentityOp<T, U>::compare identity)
- {
- HeapPtr<T> *vector;
- uint32_t i, n;
- /* The identity op must not reallocate array->vector. */
- vector = array->vector;
- for (i = 0, n = array->length; i < n; i++) {
- if (identity(vector[i].get(), 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
- template<class T>
- static JSBool
- XMLArrayAddMember(JSContext *cx, JSXMLArray<T> *array, uint32_t index, T *elt)
- {
- uint32_t capacity, i;
- int log2;
- HeapPtr<T> *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 (!(vector = ReallocateVector(array->vector, capacity))) {
- JS_ReportOutOfMemory(cx);
- return JS_FALSE;
- }
- array->capacity = capacity;
- array->vector = vector;
- for (i = array->length; i < index; i++)
- vector[i].init(NULL);
- }
- array->vector[index].init(NULL);
- array->length = index + 1;
- }
- array->vector[index] = elt;
- return JS_TRUE;
- }
- template<class T>
- static JSBool
- XMLArrayInsert(JSContext *cx, JSXMLArray<T> *array, uint32_t i, uint32_t n)
- {
- uint32_t j, k;
- JSXMLArrayCursor<T> *cursor;
- j = array->length;
- JS_ASSERT(i <= j);
- if (!array->setCapacity(cx, j + n))
- return JS_FALSE;
- k = j;
- while (k != j + n) {
- array->vector[k].init(NULL);
- k++;
- }
- array->length = j + n;
- JS_ASSERT(n != (uint32_t)-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;
- }
- template<class T>
- static T *
- XMLArrayDelete(JSContext *cx, JSXMLArray<T> *array, uint32_t index, JSBool compress)
- {
- uint32_t length;
- HeapPtr<T> *vector;
- T *elt;
- JSXMLArrayCursor<T> *cursor;
- length = array->length;
- if (index >= length)
- return NULL;
- vector = array->vector;
- elt = vector[index];
- if (compress) {
- vector[length - 1].~HeapPtr<T>();
- 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;
- }
- template<class T>
- static void
- XMLArrayTruncate(JSContext *cx, JSXMLArray<T> *array, uint32_t length)
- {
- HeapPtr<T> *vector;
- JS_ASSERT(!array->cursors);
- if (length >= array->length)
- return;
- for (uint32_t i = length; i < array->length; i++)
- array->vector[i].~HeapPtr<T>();
- if (length == 0) {
- if (array->vector)
- cx->free_(array->vector);
- vector = NULL;
- } else {
- vector = ReallocateVector(array->vector, length);
- 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, e, f)
- #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, e, f) != \
- XML_NOT_FOUND)
- #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
- ? (a)->vector[i].get() \
- : NULL)
- #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
- if ((a)->length <= (i)) { \
- (a)->length = (i) + 1; \
- ((a)->vector[i].init(e)); \
- } else { \
- ((a)->vector[i] = e); \
- } \
- JS_END_MACRO
- #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, 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) (XMLArrayDelete<t>(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.
- */
- 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";
- #define XSF_IGNORE_COMMENTS JS_BIT(0)
- #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
- #define XSF_IGNORE_WHITESPACE JS_BIT(2)
- #define XSF_PRETTY_PRINTING JS_BIT(3)
- static JSPropertySpec xml_static_props[] = {
- {js_ignoreComments_str, 0, JSPROP_PERMANENT, NULL, NULL},
- {js_ignoreProcessingInstructions_str, 0, JSPROP_PERMANENT, NULL, NULL},
- {js_ignoreWhitespace_str, 0, JSPROP_PERMANENT, NULL, NULL},
- {js_prettyPrinting_str, 0, JSPROP_PERMANENT, NULL, NULL},
- {js_prettyIndent_str, 0, JSPROP_PERMANENT, NULL, NULL},
- {0,0,0,0,0}
- };
- /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
- #define IS_XML(str) \
- (str->length() == 3 && IS_XML_CHARS(str->chars()))
- #define IS_XMLNS(str) \
- (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
- static inline bool
- IS_XML_CHARS(const jschar *chars)
- {
- return (chars[0] == 'x' || chars[0] == 'X') &&
- (chars[1] == 'm' || chars[1] == 'M') &&
- (chars[2] == 'l' || chars[2] == 'L');
- }
- static inline bool
- HAS_NS_AFTER_XML(const jschar *chars)
- {
- return (chars[3] == 'n' || chars[3] == 'N') &&
- (chars[4] == 's' || 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/";
- void
- JSXML::finalize(JSContext *cx, bool builtin)
- {
- if (JSXML_HAS_KIDS(this)) {
- xml_kids.finish(cx);
- if (xml_class == JSXML_CLASS_ELEMENT) {
- xml_namespaces.finish(cx);
- xml_attrs.finish(cx);
- }
- }
- #ifdef DEBUG_notme
- JS_REMOVE_LINK(&links);
- #endif
- }
- static JSObject *
- ParseNodeToQName(Parser *parser, ParseNode *pn,
- JSXMLArray<JSObject> *inScopeNSes, JSBool isAttributeName)
- {
- JSContext *cx = parser->context;
- JSLinearString *uri, *prefix;
- size_t length, offset;
- const jschar *start, *limit, *colon;
- uint32_t n;
- JSObject *ns;
- JSLinearString *nsprefix;
- JS_ASSERT(pn->isArity(PN_NULLARY));
- JSAtom *str = pn->pn_atom;
- start = str->chars();
- length = str->length();
- JS_ASSERT(length != 0 && *start != '@');
- JS_ASSERT(length != 1 || *start != '*');
- JSAtom *localName;
- uri = cx->runtime->emptyString;
- limit = start + length;
- colon = js_strchr_limit(start, ':', limit);
- if (colon) {
- offset = colon - start;
- prefix = js_NewDependentString(cx, str, 0, offset);
- if (!prefix)
- return NULL;
- if (STARTS_WITH_XML(start, offset)) {
- if (offset == 3) {
- uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str));
- if (!uri)
- return NULL;
- } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
- uri = JS_ASSERT_STRING_IS_FLAT(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, JSObject);
- nsprefix = ns->getNamePrefix();
- if (nsprefix && EqualStrings(nsprefix, prefix)) {
- uri = ns->getNameURI();
- break;
- }
- }
- }
- if (!uri) {
- Value v = StringValue(prefix);
- JSAutoByteString bytes;
- if (js_ValueToPrintable(parser->context, v, &bytes)) {
- ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
- JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
- }
- return NULL;
- }
- localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1));
- 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, JSObject);
- nsprefix = ns->getNamePrefix();
- if (!nsprefix || nsprefix->empty()) {
- uri = ns->getNameURI();
- break;
- }
- }
- prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
- }
- localName = str;
- }
- return NewXMLQName(parser->context, uri, prefix, localName);
- }
- static JSString *
- ChompXMLWhitespace(JSContext *cx, JSString *str)
- {
- size_t length, newlength, offset;
- const jschar *cp, *start, *end;
- jschar c;
- length = str->length();
- start = str->getChars(cx);
- if (!start)
- return NULL;
- for (cp = start, end = cp + length; cp < end; cp++) {
- c = *cp;
- if (!unicode::IsXMLSpace(c))
- break;
- }
- while (end > cp) {
- c = end[-1];
- if (!unicode::IsXMLSpace(c))
- break;
- --end;
- }
- newlength = end - cp;
- if (newlength == length)
- return str;
- offset = cp - start;
- return js_NewDependentString(cx, str, offset, newlength);
- }
- static JSXML *
- ParseNodeToXML(Parser *parser, ParseNode *pn,
- JSXMLArray<JSObject> *inScopeNSes, uintN flags)
- {
- JSContext *cx = parser->context;
- JSXML *xml, *kid, *attr, *attrj;
- JSLinearString *str;
- uint32_t length, n, i, j;
- ParseNode *pn2, *pn3, *head, **pnp;
- JSObject *ns;
- JSObject *qn, *attrjqn;
- JSXMLClass xml_class;
- int stackDummy;
- if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy)) {
- ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR,
- JSMSG_OVER_RECURSED);
- return NULL;
- }
- #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->getKind()) {
- case PNK_XMLELEM:
- length = inScopeNSes->length;
- pn2 = pn->pn_head;
- xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
- if (!xml)
- goto fail;
- n = pn->pn_count;
- JS_ASSERT(n >= 2);
- n -= 2;
- if (!xml->xml_kids.setCapacity(cx, n))
- goto fail;
- i = 0;
- while ((pn2 = pn2->pn_next) != NULL) {
- if (!pn2->pn_next) {
- /* Don't append the end tag! */
- JS_ASSERT(pn2->isKind(PNK_XMLETAGO));
- break;
- }
- if ((flags & XSF_IGNORE_WHITESPACE) &&
- n > 1 && pn2->isKind(PNK_XMLSPACE)) {
- --n;
- continue;
- }
- kid = ParseNodeToXML(parser, 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) {
- JSString *str = ChompXMLWhitespace(cx, kid->xml_value);
- if (!str)
- goto fail;
- kid->xml_value = str;
- }
- }
- JS_ASSERT(i == n);
- if (n < pn->pn_count - 2)
- xml->xml_kids.trim();
- XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
- break;
- case PNK_XMLLIST:
- xml = js_NewXML(cx, JSXML_CLASS_LIST);
- if (!xml)
- goto fail;
- n = pn->pn_count;
- if (!xml->xml_kids.setCapacity(cx, 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->isKind(PNK_XMLSPACE)) {
- --n;
- continue;
- }
- kid = ParseNodeToXML(parser, 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)
- xml->xml_kids.trim();
- break;
- case PNK_XMLSTAGO:
- case PNK_XMLPTAGC:
- length = inScopeNSes->length;
- pn2 = pn->pn_head;
- JS_ASSERT(pn2->isKind(PNK_XMLNAME));
- if (pn2->isArity(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->isKind(PNK_XMLNAME) || !pn2->isArity(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) {
- Value v = StringValue(pn2->pn_atom);
- JSAutoByteString bytes;
- if (js_ValueToPrintable(cx, v, &bytes)) {
- ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
- JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
- bytes.ptr());
- }
- goto fail;
- }
- }
- JSAtom *atom = pn2->pn_atom;
- pn2 = pn2->pn_next;
- JS_ASSERT(pn2);
- if (!pn2->isKind(PNK_XMLATTR))
- goto syntax;
- chars = atom->chars();
- length = atom->length();
- if (length >= 5 &&
- IS_XMLNS_CHARS(chars) &&
- (length == 5 || chars[5] == ':')) {
- JSLinearString *uri, *prefix;
- uri = 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);
- 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 = 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;
- }
- xml->xml_namespaces.trim();
- /* Second pass: process tag name and attributes, using namespaces. */
- pn2 = pn->pn_head;
- qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_FALSE);
- if (!qn)
- goto fail;
- xml->name = qn;
- JS_ASSERT((n & 1) == 0);
- n >>= 1;
- if (!xml->xml_attrs.setCapacity(cx, n))
- goto fail;
- for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
- qn = ParseNodeToQName(parser, 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 (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) &&
- EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) {
- Value v = StringValue(pn2->pn_atom);
- JSAutoByteString bytes;
- if (js_ValueToPrintable(cx, v, &bytes)) {
- ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
- JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
- bytes.ptr());
- }
- goto fail;
- }
- }
- pn2 = pn2->pn_next;
- JS_ASSERT(pn2);
- JS_ASSERT(pn2->isKind(PNK_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 = pn2->pn_atom;
- }
- /* Point tag closes its own namespace scope. */
- if (pn->isKind(PNK_XMLPTAGC))
- XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
- break;
- case PNK_XMLSPACE:
- case PNK_XMLTEXT:
- case PNK_XMLCDATA:
- case PNK_XMLCOMMENT:
- case PNK_XMLPI:
- str = pn->pn_atom;
- qn = NULL;
- if (pn->isKind(PNK_XMLCOMMENT)) {
- if (flags & XSF_IGNORE_COMMENTS)
- goto skip_child;
- xml_class = JSXML_CLASS_COMMENT;
- } else if (pn->isKind(PNK_XMLPI)) {
- XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
- if (IS_XML(str)) {
- Value v = StringValue(str);
- JSAutoByteString bytes;
- if (js_ValueToPrintable(cx, v, &bytes)) {
- ReportCompileErrorNumber(cx, &parser->tokenStream, &pi,
- JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
- }
- goto fail;
- }
- if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
- goto skip_child;
- qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE);
- if (!qn)
- goto fail;
- str = pi.data();
- xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
- } else {…