1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is SpiderMonkey E4X code, released August, 2004.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "jsversion.h"
42 #if JS_HAS_XML_SUPPORT
70 #include "jsstaticcheck.h"
73 #include "jsatominlines.h"
74 #include "jscntxtinlines.h"
75 #include "jsinterpinlines.h"
76 #include "jsobjinlines.h"
77 #include "jsstrinlines.h"
80 #include <string.h> /* for #ifdef DEBUG memset calls */
84 using namespace js::gc;
88 * - in the js shell, you must use the -x command line option, or call
89 * options('xml') before compiling anything that uses XML literals
93 * - Fuse objects and their JSXML* private data into single GC-things
94 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
95 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
99 js_EnterLocalRootScope(JSContext *cx)
105 js_LeaveLocalRootScope(JSContext *cx)
110 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
115 js_LeaveLocalRootScopeWithResult(JSContext *cx, Value rval)
120 js_LeaveLocalRootScopeWithResult(JSContext *cx, void *rval)
127 jsrefcount xmlnamespace;
132 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
133 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
135 #define METER(x) /* nothing */
136 #define UNMETER(x) /* nothing */
140 * Random utilities and global functions.
142 const char js_AttributeName_str[] = "AttributeName";
143 const char js_isXMLName_str[] = "isXMLName";
144 const char js_XMLList_str[] = "XMLList";
145 const char js_localName_str[] = "localName";
146 const char js_xml_parent_str[] = "parent";
147 const char js_prefix_str[] = "prefix";
148 const char js_toXMLString_str[] = "toXMLString";
149 const char js_uri_str[] = "uri";
151 const char js_amp_entity_str[] = "&";
152 const char js_gt_entity_str[] = ">";
153 const char js_lt_entity_str[] = "<";
154 const char js_quot_entity_str[] = """;
155 const char js_leftcurly_entity_str[] = "{";
157 #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*')
160 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
163 IsDeclared(const JSObject *obj)
167 JS_ASSERT(obj->getClass() == &js_NamespaceClass);
168 v = obj->getNamespaceDeclared();
169 JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
170 return v == JSVAL_TRUE;
174 xml_isXMLName(JSContext *cx, uintN argc, jsval *vp)
176 *vp = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argc ? vp[2] : JSVAL_VOID));
181 * This wrapper is needed because NewBuiltinClassInstance doesn't
182 * call the constructor, and we need a place to set the
185 static inline JSObject *
186 NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
188 JSObject *obj = NewBuiltinClassInstance(cx, clasp);
190 obj->syncSpecialEquality();
194 #define DEFINE_GETTER(name,code) \
196 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
203 * Namespace class and library functions.
205 DEFINE_GETTER(NamePrefix_getter,
206 if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefixVal())
207 DEFINE_GETTER(NameURI_getter,
208 if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURIVal())
211 namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
215 JS_ASSERT(v->isObjectOrNull());
216 obj2 = v->toObjectOrNull();
217 *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass)
219 : EqualStrings(obj->getNameURI(), obj2->getNameURI());
223 JS_FRIEND_DATA(Class) js_NamespaceClass = {
225 JSCLASS_CONSTRUCT_PROTOTYPE |
226 JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) |
227 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
228 PropertyStub, /* addProperty */
229 PropertyStub, /* delProperty */
230 PropertyStub, /* getProperty */
231 StrictPropertyStub, /* setProperty */
236 NULL, /* reserved0 */
237 NULL, /* checkAccess */
239 NULL, /* construct */
240 NULL, /* xdrObject */
241 NULL, /* hasInstance */
245 NULL, /* outerObject */
246 NULL, /* innerObject */
247 NULL, /* iteratorObject */
248 NULL, /* wrappedObject */
252 #define NAMESPACE_ATTRS \
253 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
255 static JSPropertySpec namespace_props[] = {
256 {js_prefix_str, 0, NAMESPACE_ATTRS, NamePrefix_getter, 0},
257 {js_uri_str, 0, NAMESPACE_ATTRS, NameURI_getter, 0},
262 namespace_toString(JSContext *cx, uintN argc, Value *vp)
264 JSObject *obj = ToObject(cx, &vp[1]);
267 if (!JS_InstanceOf(cx, obj, Jsvalify(&js_NamespaceClass), Jsvalify(vp + 2)))
269 *vp = Valueify(obj->getNameURIVal());
273 static JSFunctionSpec namespace_methods[] = {
274 JS_FN(js_toString_str, namespace_toString, 0,0),
279 NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared)
283 obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
286 JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
287 JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
288 JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
290 obj->setNamePrefix(prefix);
292 obj->setNameURI(uri);
294 obj->setNamespaceDeclared(JSVAL_TRUE);
295 METER(xml_stats.xmlnamespace);
300 * QName class and library functions.
302 DEFINE_GETTER(QNameNameURI_getter,
303 if (obj->getClass() == &js_QNameClass)
304 *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal())
305 DEFINE_GETTER(QNameLocalName_getter,
306 if (obj->getClass() == &js_QNameClass)
307 *vp = obj->getQNameLocalNameVal())
310 qname_identity(JSObject *qna, JSObject *qnb)
312 JSLinearString *uri1 = qna->getNameURI();
313 JSLinearString *uri2 = qnb->getNameURI();
317 if (uri1 && !EqualStrings(uri1, uri2))
319 return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName());
323 qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp)
327 obj2 = v->toObjectOrNull();
328 *bp = (!obj2 || obj2->getClass() != &js_QNameClass)
330 : qname_identity(qn, obj2);
334 JS_FRIEND_DATA(Class) js_QNameClass = {
336 JSCLASS_CONSTRUCT_PROTOTYPE |
337 JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
338 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
339 PropertyStub, /* addProperty */
340 PropertyStub, /* delProperty */
341 PropertyStub, /* getProperty */
342 StrictPropertyStub, /* setProperty */
347 NULL, /* reserved0 */
348 NULL, /* checkAccess */
350 NULL, /* construct */
351 NULL, /* xdrObject */
352 NULL, /* hasInstance */
356 NULL, /* outerObject */
357 NULL, /* innerObject */
358 NULL, /* iteratorObject */
359 NULL, /* wrappedObject */
364 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
365 * are like QName, except that they have no property getters. They share the
366 * qname_toString method, and therefore are exposed as constructable objects
367 * in this implementation.
369 JS_FRIEND_DATA(Class) js_AttributeNameClass = {
370 js_AttributeName_str,
371 JSCLASS_CONSTRUCT_PROTOTYPE |
372 JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
373 JSCLASS_MARK_IS_TRACE | JSCLASS_IS_ANONYMOUS,
374 PropertyStub, /* addProperty */
375 PropertyStub, /* delProperty */
376 PropertyStub, /* getProperty */
377 StrictPropertyStub, /* setProperty */
384 JS_FRIEND_DATA(Class) js_AnyNameClass = {
386 JSCLASS_CONSTRUCT_PROTOTYPE |
387 JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
388 JSCLASS_MARK_IS_TRACE | JSCLASS_IS_ANONYMOUS,
389 PropertyStub, /* addProperty */
390 PropertyStub, /* delProperty */
391 PropertyStub, /* getProperty */
392 StrictPropertyStub, /* setProperty */
399 #define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
401 static JSPropertySpec qname_props[] = {
402 {js_uri_str, 0, QNAME_ATTRS, QNameNameURI_getter, 0},
403 {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0},
408 ConvertQNameToString(JSContext *cx, JSObject *obj)
410 JS_ASSERT(obj->isQName());
411 JSString *uri = obj->getNameURI();
414 /* No uri means wildcard qualifier. */
415 str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
416 } else if (uri->empty()) {
417 /* Empty string for uri means localName is in no namespace. */
418 str = cx->runtime->emptyString;
420 JSString *qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
421 str = js_ConcatStrings(cx, uri, qualstr);
425 str = js_ConcatStrings(cx, str, obj->getQNameLocalName());
429 if (obj->getClass() == &js_AttributeNameClass) {
430 JS::Anchor<JSString *> anchor(str);
431 size_t length = str->length();
432 jschar *chars = (jschar *) cx->malloc((length + 2) * sizeof(jschar));
436 const jschar *strChars = str->getChars(cx);
441 js_strncpy(chars + 1, strChars, length);
443 str = js_NewString(cx, chars, length);
453 qname_toString(JSContext *cx, uintN argc, Value *vp)
455 JSObject *obj = ToObject(cx, &vp[1]);
459 if (!InstanceOf(cx, obj, &js_QNameClass, vp + 2))
462 JSString *str = ConvertQNameToString(cx, obj);
470 static JSFunctionSpec qname_methods[] = {
471 JS_FN(js_toString_str, qname_toString, 0,0),
477 InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
478 JSLinearString *localName)
480 JS_ASSERT(obj->isQName());
481 JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
482 JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
483 JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
485 obj->setNameURI(uri);
487 obj->setNamePrefix(prefix);
489 obj->setQNameLocalName(localName);
493 NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
494 JSLinearString *localName)
496 JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
499 InitXMLQName(obj, uri, prefix, localName);
500 METER(xml_stats.qname);
505 NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
506 JSLinearString *localName)
509 * AttributeName is an internal anonymous class which instances are not
510 * exposed to scripts.
512 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AttributeNameClass, NULL, NULL);
515 JS_ASSERT(obj->isQName());
516 InitXMLQName(obj, uri, prefix, localName);
517 METER(xml_stats.qname);
522 js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval)
528 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
529 * production, step 2.
531 if (nsval.isObject() &&
532 nsval.toObject().getClass() == &js_AnyNameClass) {
538 return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, argv);
542 IsXMLName(const jschar *cp, size_t n)
548 if (n != 0 && JS_ISXMLNSSTART(*cp)) {
560 js_IsXMLName(JSContext *cx, jsval v)
562 JSLinearString *name = NULL;
563 JSErrorReporter older;
566 * Inline specialization of the QName constructor called with v passed as
567 * the only argument, to compute the localName for the constructed qname,
568 * without actually allocating the object or computing its uri and prefix.
569 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
571 if (!JSVAL_IS_PRIMITIVE(v) &&
572 JSVAL_TO_OBJECT(v)->isQName()) {
573 name = JSVAL_TO_OBJECT(v)->getQNameLocalName();
575 older = JS_SetErrorReporter(cx, NULL);
576 JSString *str = js_ValueToString(cx, Valueify(v));
578 name = str->ensureLinear(cx);
579 JS_SetErrorReporter(cx, older);
581 JS_ClearPendingException(cx);
586 return IsXMLName(name->chars(), name->length());
590 * When argc is -1, it indicates argv is empty but the code should behave as
591 * if argc is 1 and argv[0] is JSVAL_VOID.
594 NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
597 jsval urival, prefixval;
599 JSBool isNamespace, isQName;
601 JSLinearString *empty, *prefix, *uri;
603 isNamespace = isQName = JS_FALSE;
604 #ifdef __GNUC__ /* suppress bogus gcc warnings */
610 urival = argv[argc > 1];
611 if (!JSVAL_IS_PRIMITIVE(urival)) {
612 uriobj = JSVAL_TO_OBJECT(urival);
613 clasp = uriobj->getClass();
614 isNamespace = (clasp == &js_NamespaceClass);
615 isQName = (clasp == &js_QNameClass);
620 /* Namespace called as function. */
621 if (argc == 1 && isNamespace) {
622 /* Namespace called with one Namespace argument is identity. */
627 obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
631 *rval = OBJECT_TO_JSVAL(obj);
632 METER(xml_stats.xmlnamespace);
634 empty = cx->runtime->emptyString;
635 obj->setNamePrefix(empty);
636 obj->setNameURI(empty);
638 if (argc == 1 || argc == -1) {
640 obj->setNameURI(uriobj->getNameURI());
641 obj->setNamePrefix(uriobj->getNamePrefix());
642 } else if (isQName && (uri = uriobj->getNameURI())) {
643 obj->setNameURI(uri);
644 obj->setNamePrefix(uriobj->getNamePrefix());
646 JSString *str = js_ValueToString(cx, Valueify(urival));
649 uri = str->ensureLinear(cx);
652 obj->setNameURI(uri);
654 obj->clearNamePrefix();
656 } else if (argc == 2) {
657 if (!isQName || !(uri = uriobj->getNameURI())) {
658 JSString *str = js_ValueToString(cx, Valueify(urival));
661 uri = str->ensureLinear(cx);
665 obj->setNameURI(uri);
669 if (!JSVAL_IS_VOID(prefixval)) {
670 JSString *str = js_ValueToString(cx, Valueify(prefixval));
674 JSAutoByteString bytes;
675 if (js_ValueToPrintable(cx, StringValue(str), &bytes)) {
676 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
677 JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
682 } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
683 obj->clearNamePrefix();
685 JSString *str = js_ValueToString(cx, Valueify(prefixval));
688 prefix = str->ensureLinear(cx);
691 obj->setNamePrefix(prefix);
698 Namespace(JSContext *cx, uintN argc, Value *vp)
700 JSObject *thisobj = NULL;
701 (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
702 return NamespaceHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
706 * When argc is -1, it indicates argv is empty but the code should behave as
707 * if argc is 1 and argv[0] is JSVAL_VOID.
710 QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval)
712 jsval nameval, nsval;
713 JSBool isQName, isNamespace;
715 JSLinearString *uri, *prefix, *name;
719 nameval = JSVAL_VOID;
722 nameval = argv[argc > 1];
724 !JSVAL_IS_PRIMITIVE(nameval) &&
725 JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass;
729 /* QName called as function. */
730 if (argc == 1 && isQName) {
731 /* QName called with one QName argument is identity. */
736 /* Create and return a new QName object exactly as if constructed. */
737 obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
741 *rval = OBJECT_TO_JSVAL(obj);
742 METER(xml_stats.qname);
745 /* If namespace is not specified and name is a QName, clone it. */
746 qn = JSVAL_TO_OBJECT(nameval);
748 uri = qn->getNameURI();
749 prefix = qn->getNamePrefix();
750 name = qn->getQNameLocalName();
754 /* Namespace and qname were passed -- use the qname's localName. */
755 nameval = qn->getQNameLocalNameVal();
759 name = cx->runtime->emptyString;
760 } else if (argc < 0) {
761 name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
763 JSString *str = js_ValueToString(cx, Valueify(nameval));
766 name = str->ensureLinear(cx);
769 argv[argc > 1] = STRING_TO_JSVAL(name);
772 if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
774 } else if (IS_STAR(name)) {
777 if (!js_GetDefaultXMLNamespace(cx, &nsval))
779 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
780 JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
784 if (JSVAL_IS_NULL(nsval)) {
785 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
789 * Inline specialization of the Namespace constructor called with
790 * nsval passed as the only argument, to compute the uri and prefix
791 * for the constructed namespace, without actually allocating the
792 * object or computing other members. See ECMA-357 13.3.2 6(a) and
795 isNamespace = isQName = JS_FALSE;
796 if (!JSVAL_IS_PRIMITIVE(nsval)) {
797 obj2 = JSVAL_TO_OBJECT(nsval);
798 isNamespace = (obj2->getClass() == &js_NamespaceClass);
799 isQName = (obj2->getClass() == &js_QNameClass);
801 #ifdef __GNUC__ /* suppress bogus gcc warnings */
806 uri = obj2->getNameURI();
807 prefix = obj2->getNamePrefix();
808 } else if (isQName && (uri = obj2->getNameURI())) {
810 prefix = obj2->getNamePrefix();
813 JSString *str = js_ValueToString(cx, Valueify(nsval));
816 uri = str->ensureLinear(cx);
819 argv[0] = STRING_TO_JSVAL(uri); /* local root */
821 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
822 prefix = uri->empty() ? cx->runtime->emptyString : NULL;
827 InitXMLQName(obj, uri, prefix, name);
832 QName(JSContext *cx, uintN argc, Value *vp)
834 JSObject *thisobj = NULL;
835 (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
836 return QNameHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
840 * XMLArray library functions.
843 namespace_identity(const void *a, const void *b)
845 const JSObject *nsa = (const JSObject *) a;
846 const JSObject *nsb = (const JSObject *) b;
847 JSLinearString *prefixa = nsa->getNamePrefix();
848 JSLinearString *prefixb = nsb->getNamePrefix();
850 if (prefixa && prefixb) {
851 if (!EqualStrings(prefixa, prefixb))
854 if (prefixa || prefixb)
857 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
861 attr_identity(const void *a, const void *b)
863 const JSXML *xmla = (const JSXML *) a;
864 const JSXML *xmlb = (const JSXML *) b;
866 return qname_identity(xmla->name, xmlb->name);
870 JSXMLArrayCursor::trace(JSTracer *trc) {
874 for (JSXMLArrayCursor *cursor = this; cursor; cursor = cursor->next)
875 js::gc::MarkGCThing(trc, cursor->root, "cursor_root", index++);
879 XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
884 /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
886 JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity)
888 if (newCapacity == 0) {
889 /* We could let realloc(p, 0) free this, but purify gets confused. */
901 #if JS_BITS_PER_WORD == 32
902 (size_t)newCapacity > ~(size_t)0 / sizeof(void *) ||
904 !(tmp = (void **) js_realloc(vector, newCapacity * sizeof(void *)))) {
906 JS_ReportOutOfMemory(cx);
911 capacity = JSXML_PRESET_CAPACITY | newCapacity;
918 if (capacity & JSXML_PRESET_CAPACITY)
920 if (length < capacity)
921 setCapacity(NULL, length);
925 JSXMLArray::finish(JSContext *cx)
929 while (JSXMLArrayCursor *cursor = cursors)
930 cursor->disconnect();
933 memset(this, 0xd5, sizeof *this);
937 #define XML_NOT_FOUND ((uint32) -1)
940 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
945 /* The identity op must not reallocate array->vector. */
946 vector = array->vector;
948 for (i = 0, n = array->length; i < n; i++) {
949 if (identity(vector[i], elt))
953 for (i = 0, n = array->length; i < n; i++) {
954 if (vector[i] == elt)
958 return XML_NOT_FOUND;
962 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
963 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
964 * should be greater than increment.
966 #define LINEAR_THRESHOLD 256
967 #define LINEAR_INCREMENT 32
970 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
976 if (index >= array->length) {
977 if (index >= JSXML_CAPACITY(array)) {
978 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
979 capacity = index + 1;
980 if (index >= LINEAR_THRESHOLD) {
981 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
983 JS_CEILING_LOG2(log2, capacity);
984 capacity = JS_BIT(log2);
987 #if JS_BITS_PER_WORD == 32
988 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
991 js_realloc(array->vector, capacity * sizeof(void *)))) {
992 JS_ReportOutOfMemory(cx);
995 array->capacity = capacity;
996 array->vector = vector;
997 for (i = array->length; i < index; i++)
1000 array->length = index + 1;
1003 array->vector[index] = elt;
1008 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1011 JSXMLArrayCursor *cursor;
1015 if (!array->setCapacity(cx, j + n))
1018 array->length = j + n;
1019 JS_ASSERT(n != (uint32)-1);
1022 array->vector[j + n] = array->vector[j];
1025 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1026 if (cursor->index > i)
1033 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1036 void **vector, *elt;
1037 JSXMLArrayCursor *cursor;
1039 length = array->length;
1040 if (index >= length)
1043 vector = array->vector;
1044 elt = vector[index];
1046 while (++index < length)
1047 vector[index-1] = vector[index];
1048 array->length = length - 1;
1049 array->capacity = JSXML_CAPACITY(array);
1051 vector[index] = NULL;
1054 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1055 if (cursor->index > index)
1062 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1066 JS_ASSERT(!array->cursors);
1067 if (length >= array->length)
1072 cx->free(array->vector);
1075 vector = (void **) js_realloc(array->vector, length * sizeof(void *));
1080 if (array->length > length)
1081 array->length = length;
1082 array->capacity = length;
1083 array->vector = vector;
1086 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1087 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1089 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1090 ? (t *) (a)->vector[i] \
1092 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1093 if ((a)->length <= (i)) \
1094 (a)->length = (i) + 1; \
1095 ((a)->vector[i] = (void *)(e)); \
1097 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1098 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1099 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1100 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1101 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1104 * Define XML setting property strings and constants early, so everyone can
1105 * use the same names.
1107 static const char js_ignoreComments_str[] = "ignoreComments";
1108 static const char js_ignoreProcessingInstructions_str[]
1109 = "ignoreProcessingInstructions";
1110 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1111 static const char js_prettyPrinting_str[] = "prettyPrinting";
1112 static const char js_prettyIndent_str[] = "prettyIndent";
1114 #define XSF_IGNORE_COMMENTS JS_BIT(0)
1115 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1116 #define XSF_IGNORE_WHITESPACE JS_BIT(2)
1117 #define XSF_PRETTY_PRINTING JS_BIT(3)
1119 static JSPropertySpec xml_static_props[] = {
1120 {js_ignoreComments_str, 0, JSPROP_PERMANENT, NULL, NULL},
1121 {js_ignoreProcessingInstructions_str, 0, JSPROP_PERMANENT, NULL, NULL},
1122 {js_ignoreWhitespace_str, 0, JSPROP_PERMANENT, NULL, NULL},
1123 {js_prettyPrinting_str, 0, JSPROP_PERMANENT, NULL, NULL},
1124 {js_prettyIndent_str, 0, JSPROP_PERMANENT, NULL, NULL},
1128 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1129 #define IS_XML(str) \
1130 (str->length() == 3 && IS_XML_CHARS(str->chars()))
1132 #define IS_XMLNS(str) \
1133 (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1135 #define IS_XML_CHARS(chars) \
1136 (JS_TOLOWER((chars)[0]) == 'x' && \
1137 JS_TOLOWER((chars)[1]) == 'm' && \
1138 JS_TOLOWER((chars)[2]) == 'l')
1140 #define HAS_NS_AFTER_XML(chars) \
1141 (JS_TOLOWER((chars)[3]) == 'n' && \
1142 JS_TOLOWER((chars)[4]) == 's')
1144 #define IS_XMLNS_CHARS(chars) \
1145 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1147 #define STARTS_WITH_XML(chars,length) \
1148 (length >= 3 && IS_XML_CHARS(chars))
1150 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1151 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1154 ParseNodeToQName(Parser *parser, JSParseNode *pn,
1155 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1157 JSContext *cx = parser->context;
1158 JSLinearString *str, *uri, *prefix, *localName;
1159 size_t length, offset;
1160 const jschar *start, *limit, *colon;
1163 JSLinearString *nsprefix;
1165 JS_ASSERT(pn->pn_arity == PN_NULLARY);
1167 start = str->chars();
1168 length = str->length();
1169 JS_ASSERT(length != 0 && *start != '@');
1170 JS_ASSERT(length != 1 || *start != '*');
1172 uri = cx->runtime->emptyString;
1173 limit = start + length;
1174 colon = js_strchr_limit(start, ':', limit);
1176 offset = colon - start;
1177 prefix = js_NewDependentString(cx, str, 0, offset);
1181 if (STARTS_WITH_XML(start, offset)) {
1183 uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str));
1186 } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1187 uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str));
1195 n = inScopeNSes->length;
1198 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1199 nsprefix = ns->getNamePrefix();
1200 if (nsprefix && EqualStrings(nsprefix, prefix)) {
1201 uri = ns->getNameURI();
1208 Value v = StringValue(prefix);
1209 JSAutoByteString bytes;
1210 if (js_ValueToPrintable(parser->context, v, &bytes)) {
1211 ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
1212 JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
1217 localName = js_NewStringCopyN(parser->context, colon + 1, length - (offset + 1));
1221 if (isAttributeName) {
1223 * An unprefixed attribute is not in any namespace, so set prefix
1224 * as well as uri to the empty string.
1229 * Loop from back to front looking for the closest declared default
1232 n = inScopeNSes->length;
1235 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1236 nsprefix = ns->getNamePrefix();
1237 if (!nsprefix || nsprefix->empty()) {
1238 uri = ns->getNameURI();
1242 prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
1247 return NewXMLQName(parser->context, uri, prefix, localName);
1251 ChompXMLWhitespace(JSContext *cx, JSString *str)
1253 size_t length, newlength, offset;
1254 const jschar *cp, *start, *end;
1257 length = str->length();
1258 start = str->getChars(cx);
1262 for (cp = start, end = cp + length; cp < end; cp++) {
1264 if (!JS_ISXMLSPACE(c))
1269 if (!JS_ISXMLSPACE(c))
1273 newlength = end - cp;
1274 if (newlength == length)
1276 offset = cp - start;
1277 return js_NewDependentString(cx, str, offset, newlength);
1281 ParseNodeToXML(Parser *parser, JSParseNode *pn,
1282 JSXMLArray *inScopeNSes, uintN flags)
1284 JSContext *cx = parser->context;
1285 JSXML *xml, *kid, *attr, *attrj;
1286 JSLinearString *str;
1287 uint32 length, n, i, j;
1288 JSParseNode *pn2, *pn3, *head, **pnp;
1290 JSObject *qn, *attrjqn;
1291 JSXMLClass xml_class;
1294 if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy)) {
1295 ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR,
1296 JSMSG_OVER_RECURSED);
1300 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1303 * Cases return early to avoid common code that gets an outermost xml's
1304 * object, which protects GC-things owned by xml and its descendants from
1305 * garbage collection.
1308 if (!js_EnterLocalRootScope(cx))
1310 switch (pn->pn_type) {
1312 length = inScopeNSes->length;
1314 xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1321 if (!xml->xml_kids.setCapacity(cx, n))
1325 while ((pn2 = pn2->pn_next) != NULL) {
1326 if (!pn2->pn_next) {
1327 /* Don't append the end tag! */
1328 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1332 if ((flags & XSF_IGNORE_WHITESPACE) &&
1333 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1338 kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1339 if (kid == PN2X_SKIP_CHILD) {
1347 /* Store kid in xml right away, to protect it from GC. */
1348 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1352 /* XXX where is this documented in an XML spec, or in E4X? */
1353 if ((flags & XSF_IGNORE_WHITESPACE) &&
1354 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1355 JSString *str = ChompXMLWhitespace(cx, kid->xml_value);
1358 kid->xml_value = str;
1363 if (n < pn->pn_count - 2)
1364 xml->xml_kids.trim();
1365 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1369 xml = js_NewXML(cx, JSXML_CLASS_LIST);
1374 if (!xml->xml_kids.setCapacity(cx, n))
1378 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1380 * Always ignore insignificant whitespace in lists -- we shouldn't
1381 * condition this on an XML.ignoreWhitespace setting when the list
1382 * constructor is XMLList (note XML/XMLList unification hazard).
1384 if (pn2->pn_type == TOK_XMLSPACE) {
1389 kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1390 if (kid == PN2X_SKIP_CHILD) {
1398 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1402 if (n < pn->pn_count)
1403 xml->xml_kids.trim();
1408 length = inScopeNSes->length;
1410 JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1411 if (pn2->pn_arity == PN_LIST)
1414 xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1418 /* First pass: check syntax and process namespace declarations. */
1419 JS_ASSERT(pn->pn_count >= 1);
1420 n = pn->pn_count - 1;
1421 pnp = &pn2->pn_next;
1423 while ((pn2 = *pnp) != NULL) {
1425 const jschar *chars;
1427 if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1430 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1431 for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1432 if (pn3->pn_atom == pn2->pn_atom) {
1433 Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom));
1434 JSAutoByteString bytes;
1435 if (js_ValueToPrintable(cx, v, &bytes)) {
1436 ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1437 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1444 JSAtom *atom = pn2->pn_atom;
1447 if (pn2->pn_type != TOK_XMLATTR)
1450 chars = atom->chars();
1451 length = atom->length();
1453 IS_XMLNS_CHARS(chars) &&
1454 (length == 5 || chars[5] == ':')) {
1455 JSLinearString *uri, *prefix;
1457 uri = ATOM_TO_STRING(pn2->pn_atom);
1459 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1460 prefix = cx->runtime->emptyString;
1462 prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1468 * Once the new ns is appended to xml->xml_namespaces, it is
1469 * protected from GC by the object that owns xml -- which is
1470 * either xml->object if outermost, or the object owning xml's
1471 * oldest ancestor if !outermost.
1473 ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1478 * Don't add a namespace that's already in scope. If someone
1479 * extracts a child property from its parent via [[Get]], then
1480 * we enforce the invariant, noted many times in ECMA-357, that
1481 * the child's namespaces form a possibly-improper superset of
1482 * its ancestors' namespaces.
1484 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1485 if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1486 !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1493 *pnp = pn2->pn_next;
1494 /* XXXbe recycle pn2 */
1498 pnp = &pn2->pn_next;
1501 xml->xml_namespaces.trim();
1503 /* Second pass: process tag name and attributes, using namespaces. */
1505 qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_FALSE);
1510 JS_ASSERT((n & 1) == 0);
1512 if (!xml->xml_attrs.setCapacity(cx, n))
1515 for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1516 qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_TRUE);
1518 xml->xml_attrs.length = i;
1523 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1524 * this time checking local name and namespace URI.
1526 for (j = 0; j < i; j++) {
1527 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1528 attrjqn = attrj->name;
1529 if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) &&
1530 EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) {
1531 Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom));
1532 JSAutoByteString bytes;
1533 if (js_ValueToPrintable(cx, v, &bytes)) {
1534 ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1535 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1544 JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1546 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1550 XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1553 attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1556 /* Point tag closes its own namespace scope. */
1557 if (pn->pn_type == TOK_XMLPTAGC)
1558 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1564 case TOK_XMLCOMMENT:
1566 str = ATOM_TO_STRING(pn->pn_atom);
1568 if (pn->pn_type == TOK_XMLCOMMENT) {
1569 if (flags & XSF_IGNORE_COMMENTS)
1571 xml_class = JSXML_CLASS_COMMENT;
1572 } else if (pn->pn_type == TOK_XMLPI) {
1574 Value v = StringValue(str);
1575 JSAutoByteString bytes;
1576 if (js_ValueToPrintable(cx, v, &bytes)) {
1577 ReportCompileErrorNumber(cx, &parser->tokenStream, pn,
1578 JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
1583 if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1586 qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE);
1591 ? ATOM_TO_STRING(pn->pn_atom2)
1592 : cx->runtime->emptyString;
1593 xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1595 /* CDATA section content, or element text. */
1596 xml_class = JSXML_CLASS_TEXT;
1599 xml = js_NewXML(cx, xml_class);
1603 if (pn->pn_type == TOK_XMLSPACE)
1604 xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1605 xml->xml_value = str;
1612 js_LeaveLocalRootScopeWithResult(cx, xml);
1616 js_LeaveLocalRootScope(cx);
1617 return PN2X_SKIP_CHILD;
1619 #undef PN2X_SKIP_CHILD
1622 ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
1624 js_LeaveLocalRootScope(cx);
1629 * XML helper, object-ops, and library functions. We start with the helpers,
1630 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1633 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1637 if (!js_FindClassObject(cx, NULL, JSProto_XML, Valueify(&v)))
1639 if (!VALUE_IS_FUNCTION(cx, v)) {
1643 return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1647 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1651 return GetXMLSetting(cx, name, &v) && JS_ValueToBoolean(cx, v, bp);
1655 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1659 return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1663 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1667 if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag[0]) ||
1668 !GetBooleanXMLSetting(cx, js_ignoreProcessingInstructions_str, &flag[1]) ||
1669 !GetBooleanXMLSetting(cx, js_ignoreWhitespace_str, &flag[2]) ||
1670 !GetBooleanXMLSetting(cx, js_prettyPrinting_str, &flag[3])) {
1675 for (size_t n = 0; n < 4; ++n)
1677 *flagsp |= JS_BIT(n);
1682 ParseXMLSource(JSContext *cx, JSString *src)
1685 JSLinearString *uri;
1686 size_t urilen, srclen, length, offset, dstlen;
1688 const jschar *srcp, *endp;
1690 const char *filename;
1694 static const char prefix[] = "<parent xmlns=\"";
1695 static const char middle[] = "\">";
1696 static const char suffix[] = "</parent>";
1698 #define constrlen(constr) (sizeof(constr) - 1)
1700 if (!js_GetDefaultXMLNamespace(cx, &nsval))
1702 uri = JSVAL_TO_OBJECT(nsval)->getNameURI();
1703 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1707 urilen = uri->length();
1708 srclen = src->length();
1709 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1712 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
1717 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1719 js_strncpy(chars + offset, uri->chars(), urilen);
1721 dstlen = length - offset + 1;
1722 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1725 srcp = src->getChars(cx);
1730 js_strncpy(chars + offset, srcp, srclen);
1732 dstlen = length - offset + 1;
1733 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1735 chars [offset + dstlen] = 0;
1739 FrameRegsIter i(cx);
1740 for (; !i.done() && !i.pc(); ++i)
1741 JS_ASSERT(!i.fp()->isScriptFrame());
1745 JSStackFrame *fp = i.fp();
1746 op = (JSOp) *i.pc();
1747 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1748 filename = fp->script()->filename;
1749 lineno = js_FramePCToLineNumber(cx, fp);
1750 for (endp = srcp + srclen; srcp < endp; srcp++) {
1759 if (parser.init(chars, length, filename, lineno, cx->findVersion())) {
1760 JSObject *scopeChain = GetScopeChain(cx);
1765 JSParseNode *pn = parser.parseXMLText(scopeChain, false);
1767 if (pn && GetXMLSettingFlags(cx, &flags)) {
1768 AutoNamespaceArray namespaces(cx);
1769 if (namespaces.array.setCapacity(cx, 1))
1770 xml = ParseNodeToXML(&parser, pn, &namespaces.array, flags);
1782 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1784 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1787 * for all x belonging to XML:
1788 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1790 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1791 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1793 * Same goes for 10.4.1 Step 7(a).
1795 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1796 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1797 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1798 * undeclared namespaces associated with x not belonging to ancestorNS.
1801 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1805 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1806 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1809 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1810 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1812 ns->setNamespaceDeclared(JSVAL_VOID);
1819 ToXML(JSContext *cx, jsval v)
1827 if (JSVAL_IS_PRIMITIVE(v)) {
1828 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1831 obj = JSVAL_TO_OBJECT(v);
1833 xml = (JSXML *) obj->getPrivate();
1834 if (xml->xml_class == JSXML_CLASS_LIST) {
1835 if (xml->xml_kids.length != 1)
1837 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1839 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1840 return js_GetXMLObject(cx, xml);
1846 clasp = obj->getClass();
1847 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1851 if (clasp != &js_StringClass &&
1852 clasp != &js_NumberClass &&
1853 clasp != &js_BooleanClass) {
1858 str = js_ValueToString(cx, Valueify(v));
1863 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1867 xml = ParseXMLSource(cx, str);
1870 length = JSXML_LENGTH(xml);
1874 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
1877 } else if (length == 1) {
1878 xml = OrphanXMLChild(cx, xml, 0);
1881 obj = js_GetXMLObject(cx, xml);
1885 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
1891 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
1892 JSDVG_IGNORE_STACK, Valueify(v), NULL);
1897 Append(JSContext *cx, JSXML *list, JSXML *kid);
1900 ToXMLList(JSContext *cx, jsval v)
1902 JSObject *obj, *listobj;
1903 JSXML *xml, *list, *kid;
1908 if (JSVAL_IS_PRIMITIVE(v)) {
1909 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1912 obj = JSVAL_TO_OBJECT(v);
1914 xml = (JSXML *) obj->getPrivate();
1915 if (xml->xml_class != JSXML_CLASS_LIST) {
1916 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1919 list = (JSXML *) listobj->getPrivate();
1920 if (!Append(cx, list, xml))
1927 clasp = obj->getClass();
1928 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1932 if (clasp != &js_StringClass &&
1933 clasp != &js_NumberClass &&
1934 clasp != &js_BooleanClass) {
1939 str = js_ValueToString(cx, Valueify(v));
1946 if (!js_EnterLocalRootScope(cx))
1948 xml = ParseXMLSource(cx, str);
1950 js_LeaveLocalRootScope(cx);
1953 length = JSXML_LENGTH(xml);
1956 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1958 list = (JSXML *) listobj->getPrivate();
1959 for (i = 0; i < length; i++) {
1960 kid = OrphanXMLChild(cx, xml, i);
1961 if (!kid || !Append(cx, list, kid)) {
1969 js_LeaveLocalRootScopeWithResult(cx, listobj);
1973 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
1974 JSDVG_IGNORE_STACK, Valueify(v), NULL);
1979 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
1980 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
1981 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
1982 * MakeXMLSpecialString subroutine.
1984 * These functions mutate sb, leaving it empty.
1986 static JSFlatString *
1987 MakeXMLSpecialString(JSContext *cx, StringBuffer &sb,
1988 JSString *str, JSString *str2,
1989 const jschar *prefix, size_t prefixlength,
1990 const jschar *suffix, size_t suffixlength)
1992 if (!sb.append(prefix, prefixlength) || !sb.append(str))
1994 if (str2 && !str2->empty()) {
1995 if (!sb.append(' ') || !sb.append(str2))
1998 if (!sb.append(suffix, suffixlength))
2001 return sb.finishString();
2004 static JSFlatString *
2005 MakeXMLCDATAString(JSContext *cx, StringBuffer &sb, JSString *str)
2007 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2008 'C', 'D', 'A', 'T', 'A',
2010 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2012 return MakeXMLSpecialString(cx, sb, str, NULL,
2013 cdata_prefix_ucNstr, 9,
2014 cdata_suffix_ucNstr, 3);
2017 static JSFlatString *
2018 MakeXMLCommentString(JSContext *cx, StringBuffer &sb, JSString *str)
2020 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2021 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2023 return MakeXMLSpecialString(cx, sb, str, NULL,
2024 comment_prefix_ucNstr, 4,
2025 comment_suffix_ucNstr, 3);
2028 static JSFlatString *
2029 MakeXMLPIString(JSContext *cx, StringBuffer &sb, JSString *name,
2032 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2033 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2035 return MakeXMLSpecialString(cx, sb, name, value,
2036 pi_prefix_ucNstr, 2,
2037 pi_suffix_ucNstr, 2);
2041 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2042 * equals, a double quote, an attribute value, and a closing double quote.
2045 AppendAttributeValue(JSContext *cx, StringBuffer &sb, JSString *valstr)
2047 if (!sb.append('='))
2049 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2050 return valstr && sb.append(valstr);
2054 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2056 * These functions mutate sb, leaving it empty.
2058 static JSFlatString *
2059 EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32 toSourceFlag)
2061 size_t length = str->length();
2062 const jschar *start = str->getChars(cx);
2066 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2070 if (!sb.append(js_lt_entity_str))
2074 if (!sb.append(js_gt_entity_str))
2078 if (!sb.append(js_amp_entity_str))
2083 * If EscapeElementValue is called by toSource/uneval, we also need
2084 * to escape '{'. See bug 463360.
2087 if (!sb.append(js_leftcurly_entity_str))
2097 return sb.finishString();
2101 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2103 * These functions mutate sb, leaving it empty.
2105 static JSFlatString *
2106 EscapeAttributeValue(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote)
2108 size_t length = str->length();
2109 const jschar *start = str->getChars(cx);
2113 if (quote && !sb.append('"'))
2116 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2120 if (!sb.append(js_quot_entity_str))
2124 if (!sb.append(js_lt_entity_str))
2128 if (!sb.append(js_amp_entity_str))
2132 if (!sb.append("
"))
2136 if (!sb.append("
"))
2140 if (!sb.append("	"))
2149 if (quote && !sb.append('"'))
2152 return sb.finishString();
2155 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2157 GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2159 JSLinearString *uri, *prefix, *nsprefix;
2160 JSObject *match, *ns;
2164 uri = qn->getNameURI();
2165 prefix = qn->getNamePrefix();
2168 JSAutoByteString bytes;
2169 const char *s = !prefix ?
2171 : js_ValueToPrintable(cx, StringValue(prefix), &bytes);
2173 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, s);
2177 /* Look for a matching namespace in inScopeNSes, if provided. */
2180 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2181 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2186 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2187 * If we preserve prefixes, we must match null prefix against
2188 * an empty prefix of ns, in order to avoid generating redundant
2189 * prefixed and default namespaces for cases such as:
2191 * x = <t xmlns="http://foo.com"/>
2192 * print(x.toXMLString());
2194 * Per 10.3.2.1, the namespace attribute in t has an empty string
2195 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2197 * 1. If the [local name] property of a is "xmlns"
2198 * a. Map ns.prefix to the empty string
2200 * But t's name has a null prefix in this implementation, meaning
2201 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2202 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2203 * saying how "no value" maps to an ECMA-357 value -- but it must
2204 * map to the *undefined* prefix value).
2206 * Since "" != undefined (or null, in the current implementation)
2207 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2208 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2209 * This spec bug leads to ToXMLString results that duplicate the
2210 * declared namespace.
2212 if (EqualStrings(ns->getNameURI(), uri)) {
2213 nsprefix = ns->getNamePrefix();
2214 if (nsprefix == prefix ||
2215 ((nsprefix && prefix)
2216 ? EqualStrings(nsprefix, prefix)
2217 : (nsprefix ? nsprefix : prefix)->empty())) {
2225 /* If we didn't match, make a new namespace from qn. */
2227 argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2228 argv[1] = STRING_TO_JSVAL(uri);
2229 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL,
2238 static JSLinearString *
2239 GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls)
2241 const jschar *cp, *start, *end;
2242 size_t length, newlength, offset;
2243 uint32 i, n, m, serial;
2247 JSLinearString *nsprefix, *prefix;
2249 JS_ASSERT(!uri->empty());
2252 * If there are no *declared* namespaces, skip all collision detection and
2253 * return a short prefix quickly; an example of such a situation:
2256 * var n = new Namespace("http://example.com/");
2257 * x.@n::att = "val";
2260 * This is necessary for various log10 uses below to be valid.
2262 if (decls->length == 0)
2263 return js_NewStringCopyZ(cx, "a");
2266 * Try peeling off the last filename suffix or pathname component till
2267 * we have a valid XML name. This heuristic will prefer "xul" given
2268 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2269 * likely URI of the form ".../xbl2/2005".
2271 start = uri->chars();
2272 end = start + uri->length();
2274 while (--cp > start) {
2275 if (*cp == '.' || *cp == '/' || *cp == ':') {
2278 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2286 * If the namespace consisted only of non-XML names or names that begin
2287 * case-insensitively with "xml", arbitrarily create a prefix consisting
2288 * of 'a's of size length (allowing dp-calculating code to work with or
2289 * without this branch executing) plus the space for storing a hyphen and
2290 * the serial number (avoiding reallocation if a collision happens).
2294 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2295 newlength = length + 2 + (size_t) log10((double) decls->length);
2297 cx->malloc((newlength + 1) * sizeof(jschar));
2302 for (i = 0; i < newlength; i++)
2307 * Now search through decls looking for a collision. If we collide with
2308 * an existing prefix, start tacking on a hyphen and a serial number.
2313 for (i = 0, n = decls->length; i < n; i++) {
2314 ns = XMLARRAY_MEMBER(decls, i, JSObject);
2315 if (ns && (nsprefix = ns->getNamePrefix()) &&
2316 nsprefix->length() == newlength &&
2317 !memcmp(nsprefix->chars(), bp,
2318 newlength * sizeof(jschar))) {
2320 newlength = length + 2 + (size_t) log10((double) n);
2322 cx->malloc((newlength + 1) * sizeof(jschar));
2325 js_strncpy(bp, cp, length);
2329 JS_ASSERT(serial <= n);
2330 dp = bp + length + 2 + (size_t) log10((double) serial);
2332 for (m = serial; m != 0; m /= 10)
2333 *--dp = (jschar)('0' + m % 10);
2335 JS_ASSERT(dp == bp + length);
2344 offset = cp - start;
2345 prefix = js_NewDependentString(cx, uri, offset, length);
2347 prefix = js_NewString(cx, bp, newlength);
2355 namespace_match(const void *a, const void *b)
2357 const JSObject *nsa = (const JSObject *) a;
2358 const JSObject *nsb = (const JSObject *) b;
2359 JSLinearString *prefixa, *prefixb = nsb->getNamePrefix();
2362 prefixa = nsa->getNamePrefix();
2363 return prefixa && EqualStrings(prefixa, prefixb);
2365 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
2368 /* ECMA-357 10.2.1 and 10.2.2 */
2369 #define TO_SOURCE_FLAG 0x80000000
2372 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2375 JSBool pretty, indentKids;
2376 StringBuffer sb(cx);
2378 JSLinearString *prefix, *nsuri;
2379 uint32 i, n, nextIndentLevel;
2381 AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx);
2383 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2387 if (!sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2393 switch (xml->xml_class) {
2394 case JSXML_CLASS_TEXT:
2397 str = ChompXMLWhitespace(cx, xml->xml_value);
2401 str = xml->xml_value;
2403 return EscapeElementValue(cx, sb, str, indentLevel & TO_SOURCE_FLAG);
2405 case JSXML_CLASS_ATTRIBUTE:
2407 return EscapeAttributeValue(cx, sb, xml->xml_value,
2408 (indentLevel & TO_SOURCE_FLAG) != 0);
2410 case JSXML_CLASS_COMMENT:
2412 return MakeXMLCommentString(cx, sb, xml->xml_value);
2414 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2416 return MakeXMLPIString(cx, sb, xml->name->getQNameLocalName(),
2419 case JSXML_CLASS_LIST:
2420 /* ECMA-357 10.2.2. */
2422 JSXMLArrayCursor cursor(&xml->xml_kids);
2424 while (JSXML *kid = (JSXML *) cursor.getNext()) {
2425 if (pretty && i != 0) {
2426 if (!sb.append('\n'))
2430 JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2431 if (!kidstr || !sb.append(kidstr))
2438 return cx->runtime->emptyString;
2439 return sb.finishString();
2444 /* After this point, control must flow through label out: to exit. */
2445 if (!js_EnterLocalRootScope(cx))
2448 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2450 ancestorNSes = &empty.array;
2452 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2454 JSXMLArrayCursor cursor(&xml->xml_namespaces);
2455 while ((ns = (JSObject *) cursor.getNext()) != NULL) {
2456 if (!IsDeclared(ns))
2458 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2459 /* NOTE: may want to exclude unused namespaces here. */
2460 ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), JS_TRUE);
2461 if (!ns2 || !XMLARRAY_APPEND(cx, &decls.array, ns2))
2468 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2469 * not own its member references. In the spec, ancdecls has no name, but
2470 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2473 if (!ancdecls.array.setCapacity(cx, ancestorNSes->length + decls.length()))
2475 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2476 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2479 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls.array, ns2, namespace_identity));
2480 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2483 for (i = 0, n = decls.length(); i < n; i++) {
2484 ns2 = XMLARRAY_MEMBER(&decls.array, i, JSObject);
2487 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls.array, ns2, namespace_identity));
2488 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2492 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2493 ns = GetNamespace(cx, xml->name, &ancdecls.array);
2497 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2498 prefix = ns->getNamePrefix();
2501 * Create a namespace prefix that isn't used by any member of decls.
2502 * Assign the new prefix to a copy of ns. Flag this namespace as if
2503 * it were declared, for assertion-testing's sake later below.
2505 * Erratum: if prefix and xml->name are both null (*undefined* in
2506 * ECMA-357), we know that xml was named using the default namespace
2507 * (proof: see GetNamespace and the Namespace constructor called with
2508 * two arguments). So we ought not generate a new prefix here, when
2509 * we can declare ns as the default namespace for xml.
2511 * This helps descendants inherit the namespace instead of redundantly
2512 * redeclaring it with generated prefixes in each descendant.
2514 nsuri = ns->getNameURI();
2515 if (!xml->name->getNamePrefix()) {
2516 prefix = cx->runtime->emptyString;
2518 prefix = GeneratePrefix(cx, nsuri, &ancdecls.array);
2522 ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2527 * If the xml->name was unprefixed, we must remove any declared default
2528 * namespace from decls before appending ns. How can you get a default
2529 * namespace in decls that doesn't match the one from name? Apparently
2530 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2531 * to fix this is to update x's in-scope namespaces when setNamespace
2532 * is called, but that's not specified by ECMA-357.
2534 * Likely Erratum here, depending on whether the lack of update to x's
2535 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2536 * erratum or not. Note that changing setNamespace to update the list
2537 * of in-scope namespaces will change x.namespaceDeclarations().
2539 if (prefix->empty()) {
2540 i = XMLArrayFindMember(&decls.array, ns, namespace_match);
2541 if (i != XML_NOT_FOUND)
2542 XMLArrayDelete(cx, &decls.array, i, JS_TRUE);
2546 * In the spec, ancdecls has no name, but is always written out as
2547 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2548 * that union in ancdecls, any time we append a namespace strong
2549 * ref to decls, we must also append a weak ref to ancdecls. Order
2550 * matters here: code at label out: releases strong refs in decls.
2552 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns) ||
2553 !XMLARRAY_APPEND(cx, &decls.array, ns)) {
2558 /* Format the element or point-tag into sb. */
2559 if (!sb.append('<'))
2562 if (!prefix->empty()) {
2563 if (!sb.append(prefix) || !sb.append(':'))
2566 if (!sb.append(xml->name->getQNameLocalName()))
2570 * Step 16 makes a union to avoid writing two loops in step 17, to share
2571 * common attribute value appending spec-code. We prefer two loops for
2572 * faster code and less data overhead.
2575 /* Step 17(b): append attributes. */
2577 JSXMLArrayCursor cursor(&xml->xml_attrs);
2578 while (JSXML *attr = (JSXML *) cursor.getNext()) {
2579 if (!sb.append(' '))
2581 ns2 = GetNamespace(cx, attr->name, &ancdecls.array);
2585 /* 17(b)(ii): NULL means *undefined* here. */
2586 prefix = ns2->getNamePrefix();
2588 prefix = GeneratePrefix(cx, ns2->getNameURI(), &ancdecls.array);
2592 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2593 ns2 = NewXMLNamespace(cx, prefix, ns2->getNameURI(), JS_TRUE);
2598 * In the spec, ancdecls has no name, but is always written out as
2599 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2600 * that union in ancdecls, any time we append a namespace strong
2601 * ref to decls, we must also append a weak ref to ancdecls. Order
2602 * matters here: code at label out: releases strong refs in decls.
2604 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2) ||
2605 !XMLARRAY_APPEND(cx, &decls.array, ns2)) {
2611 if (!prefix->empty()) {
2612 if (!sb.append(prefix) || !sb.append(':'))
2617 if (!sb.append(attr->name->getQNameLocalName()))
2621 if (!AppendAttributeValue(cx, sb, attr->xml_value))
2626 /* Step 17(c): append XML namespace declarations. */
2628 JSXMLArrayCursor cursor(&decls.array);
2629 while (JSObject *ns3 = (JSObject *) cursor.getNext()) {
2630 JS_ASSERT(IsDeclared(ns3));
2632 if (!sb.append(" xmlns"))
2635 /* 17(c)(ii): NULL means *undefined* here. */
2636 prefix = ns3->getNamePrefix();
2638 prefix = GeneratePrefix(cx, ns3->getNameURI(), &ancdecls.array);
2641 ns3->setNamePrefix(prefix);
2645 if (!prefix->empty()) {
2646 if (!sb.append(':') || !sb.append(prefix))
2651 if (!AppendAttributeValue(cx, sb, ns3->getNameURI()))
2656 /* Step 18: handle point tags. */
2657 n = xml->xml_kids.length;
2659 if (!sb.append("/>"))
2662 /* Steps 19 through 25: handle element content, and open the end-tag. */
2663 if (!sb.append('>'))
2667 indentKids = n > 1 ||
2669 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2670 kid->xml_class != JSXML_CLASS_TEXT);
2673 if (pretty && indentKids) {
2674 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2676 nextIndentLevel = indentLevel + i;
2678 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2682 JSXMLArrayCursor cursor(&xml->xml_kids);
2683 while (JSXML *kid = (JSXML *) cursor.getNext()) {
2684 if (pretty && indentKids) {
2685 if (!sb.append('\n'))
2689 JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls.array, nextIndentLevel);
2693 if (!sb.append(kidstr))
2698 if (pretty && indentKids) {
2699 if (!sb.append('\n') ||
2700 !sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2703 if (!sb.append("</"))
2707 prefix = ns->getNamePrefix();
2708 if (prefix && !prefix->empty()) {
2709 if (!sb.append(prefix) || !sb.append(':'))
2714 if (!sb.append(xml->name->getQNameLocalName()) || !sb.append('>'))
2718 str = sb.finishString();
2720 js_LeaveLocalRootScopeWithResult(cx, str);
2726 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
2732 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2733 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2734 JSMSG_BAD_XML_CONVERSION,
2735 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2739 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2740 return js_ValueToString(cx, Valueify(v));
2742 if (JSVAL_IS_STRING(v)) {
2743 StringBuffer sb(cx);
2744 return EscapeElementValue(cx, sb, JSVAL_TO_STRING(v), toSourceFlag);
2747 obj = JSVAL_TO_OBJECT(v);
2748 if (!obj->isXML()) {
2749 if (!DefaultValue(cx, obj, JSTYPE_STRING, Valueify(&v)))
2751 str = js_ValueToString(cx, Valueify(v));
2754 StringBuffer sb(cx);
2755 return EscapeElementValue(cx, sb, str, toSourceFlag);
2758 /* Handle non-element cases in this switch, returning from each case. */
2759 xml = (JSXML *) obj->getPrivate();
2760 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
2764 ToAttributeName(JSContext *cx, jsval v)
2766 JSLinearString *name, *uri, *prefix;
2771 if (JSVAL_IS_STRING(v)) {
2772 name = JSVAL_TO_STRING(v)->ensureLinear(cx);
2775 uri = prefix = cx->runtime->emptyString;
2777 if (JSVAL_IS_PRIMITIVE(v)) {
2778 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2779 JSDVG_IGNORE_STACK, Valueify(v), NULL);
2783 obj = JSVAL_TO_OBJECT(v);
2784 clasp = obj->getClass();
2785 if (clasp == &js_AttributeNameClass)
2788 if (clasp == &js_QNameClass) {
2790 uri = qn->getNameURI();
2791 prefix = qn->getNamePrefix();
2792 name = qn->getQNameLocalName();
2794 if (clasp == &js_AnyNameClass) {
2795 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2797 JSString *str = js_ValueToString(cx, Valueify(v));
2800 name = str->ensureLinear(cx);
2804 uri = prefix = cx->runtime->emptyString;
2808 qn = NewXMLAttributeName(cx, uri, prefix, name);
2815 ReportBadXMLName(JSContext *cx, const Value &idval)
2817 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL);
2821 IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
2824 JSLinearString *uri;
2826 atom = cx->runtime->atomState.functionNamespaceURIAtom;
2827 uri = qn->getNameURI();
2829 (uri == ATOM_TO_STRING(atom) ||
2830 EqualStrings(uri, ATOM_TO_STRING(atom)))) {
2831 return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp);
2833 *funidp = JSID_VOID;
2838 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
2840 if (obj->getClass() == &js_QNameClass)
2841 return IsFunctionQName(cx, obj, funidp);
2842 *funidp = JSID_VOID;
2847 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
2849 JSAtom *atomizedName;
2855 if (JSVAL_IS_STRING(v)) {
2856 name = JSVAL_TO_STRING(v);
2858 if (JSVAL_IS_PRIMITIVE(v)) {
2859 ReportBadXMLName(cx, Valueify(v));
2863 obj = JSVAL_TO_OBJECT(v);
2864 clasp = obj->getClass();
2865 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass)
2867 if (clasp == &js_AnyNameClass) {
2868 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2871 name = js_ValueToString(cx, Valueify(v));
2876 atomizedName = js_AtomizeString(cx, name, 0);
2881 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
2883 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
2885 * First, _P_ should be _s_, to refer to the given string.
2887 * Second, why does ToXMLName applied to the string type throw TypeError
2888 * only for numeric literals without any leading or trailing whitespace?
2890 * If the idea is to reject uint32 property names, then the check needs to
2891 * be stricter, to exclude hexadecimal and floating point literals.
2893 if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index))
2896 if (*atomizedName->chars() == '@') {
2897 name = js_NewDependentString(cx, name, 1, name->length() - 1);
2900 *funidp = JSID_VOID;
2901 return ToAttributeName(cx, STRING_TO_JSVAL(name));
2905 v = STRING_TO_JSVAL(name);
2906 obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v));
2911 if (!IsFunctionQName(cx, obj, funidp))
2916 JSAutoByteString bytes;
2917 if (js_ValueToPrintable(cx, StringValue(name), &bytes))
2918 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, bytes.ptr());
2922 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
2924 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
2926 JSLinearString *prefix, *prefix2;
2927 JSObject *match, *ns2;
2930 if (xml->xml_class != JSXML_CLASS_ELEMENT)
2933 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
2934 prefix = ns->getNamePrefix();
2937 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2938 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2939 if (ns2 && EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
2944 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
2947 if (prefix->empty() && xml->name->getNameURI()->empty())
2950 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2953 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2954 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2955 if (ns2 && (prefix2 = ns2->getNamePrefix()) &&
2956 EqualStrings(prefix2, prefix)) {
2962 if (match && !EqualStrings(match->getNameURI(), ns->getNameURI())) {
2963 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
2965 JS_ASSERT(ns2 == match);
2966 match->clearNamePrefix();
2967 if (!AddInScopeNamespace(cx, xml, match))
2970 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
2974 /* OPTION: enforce that descendants have superset namespaces. */
2978 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
2980 Append(JSContext *cx, JSXML *list, JSXML *xml)
2985 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
2986 i = list->xml_kids.length;
2988 if (xml->xml_class == JSXML_CLASS_LIST) {
2989 list->xml_target = xml->xml_target;
2990 list->xml_targetprop = xml->xml_targetprop;
2991 n = JSXML_LENGTH(xml);
2993 if (!list->xml_kids.setCapacity(cx, k))
2995 for (j = 0; j < n; j++) {
2996 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
2998 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3003 list->xml_target = xml->parent;
3004 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3005 list->xml_targetprop = NULL;
3007 list->xml_targetprop = xml->name;
3008 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3013 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3015 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3018 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3022 /* Our caller may not be protecting newborns with a local root scope. */
3023 if (!js_EnterLocalRootScope(cx))
3025 copy = DeepCopyInLRS(cx, xml, flags);
3028 /* Caller provided the object for this copy, hook 'em up. */
3029 obj->setPrivate(copy);
3031 } else if (!js_GetXMLObject(cx, copy)) {
3035 js_LeaveLocalRootScopeWithResult(cx, copy);
3040 * (i) We must be in a local root scope (InLRS).
3041 * (ii) parent must have a rooted object.
3042 * (iii) from's owning object must be locked if not thread-local.
3045 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3053 if (!to->setCapacity(cx, n))
3056 JSXMLArrayCursor cursor(from);
3058 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3059 if ((flags & XSF_IGNORE_COMMENTS) &&
3060 kid->xml_class == JSXML_CLASS_COMMENT) {
3063 if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3064 kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3067 if ((flags & XSF_IGNORE_WHITESPACE) &&
3068 (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3071 kid2 = DeepCopyInLRS(cx, kid, flags);
3077 if ((flags & XSF_IGNORE_WHITESPACE) &&
3078 n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3079 str = ChompXMLWhitespace(cx, kid2->xml_value);
3084 kid2->xml_value = str;
3087 XMLARRAY_SET_MEMBER(to, j, kid2);
3089 if (parent->xml_class != JSXML_CLASS_LIST)
3090 kid2->parent = parent;
3099 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
3107 JS_CHECK_RECURSION(cx, return NULL);
3109 copy = js_NewXML(cx, JSXMLClass(xml->xml_class));
3114 qn = NewXMLQName(cx, qn->getNameURI(), qn->getNamePrefix(), qn->getQNameLocalName());
3121 copy->xml_flags = xml->xml_flags;
3123 if (JSXML_HAS_VALUE(xml)) {
3124 copy->xml_value = xml->xml_value;
3127 ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags);
3131 if (xml->xml_class == JSXML_CLASS_LIST) {
3132 copy->xml_target = xml->xml_target;
3133 copy->xml_targetprop = xml->xml_targetprop;
3135 n = xml->xml_namespaces.length;
3136 ok = copy->xml_namespaces.setCapacity(cx, n);
3139 for (i = 0; i < n; i++) {
3140 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3143 ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(),
3146 copy->xml_namespaces.length = i;
3150 XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2);
3153 ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy,
3166 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3168 DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index)
3172 if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3173 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3176 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3180 typedef JSBool (*JSXMLNameMatcher)(JSObject *nameqn, JSXML *xml);
3183 MatchAttrName(JSObject *nameqn, JSXML *attr)
3185 JSObject *attrqn = attr->name;
3186 JSLinearString *localName = nameqn->getQNameLocalName();
3187 JSLinearString *uri;
3189 return (IS_STAR(localName) ||
3190 EqualStrings(attrqn->getQNameLocalName(), localName)) &&
3191 (!(uri = nameqn->getNameURI()) ||
3192 EqualStrings(attrqn->getNameURI(), uri));
3196 MatchElemName(JSObject *nameqn, JSXML *elem)
3198 JSLinearString *localName = nameqn->getQNameLocalName();
3199 JSLinearString *uri;
3201 return (IS_STAR(localName) ||
3202 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3203 EqualStrings(elem->name->getQNameLocalName(), localName))) &&
3204 (!(uri = nameqn->getNameURI()) ||
3205 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3206 EqualStrings(elem->name->getNameURI(), uri)));
3209 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3211 DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list)
3216 JS_CHECK_RECURSION(cx, return JS_FALSE);
3218 if (xml->xml_class == JSXML_CLASS_ELEMENT &&
3219 nameqn->getClass() == &js_AttributeNameClass) {
3220 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
3221 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3222 if (attr && MatchAttrName(nameqn, attr)) {
3223 if (!Append(cx, list, attr))
3229 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3230 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3233 if (nameqn->getClass() != &js_AttributeNameClass &&
3234 MatchElemName(nameqn, kid)) {
3235 if (!Append(cx, list, kid))
3238 if (!DescendantsHelper(cx, kid, nameqn, list))
3245 Descendants(JSContext *cx, JSXML *xml, jsval id)
3254 nameqn = ToXMLName(cx, id, &funid);
3258 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3261 list = (JSXML *) listobj->getPrivate();
3262 if (!JSID_IS_VOID(funid))
3266 * Protect nameqn's object and strings from GC by linking list to it
3267 * temporarily. The newborn GC root for the last allocated object
3268 * protects listobj, which protects list. Any other object allocations
3269 * occurring beneath DescendantsHelper use local roots.
3271 list->name = nameqn;
3272 if (!js_EnterLocalRootScope(cx))
3274 if (xml->xml_class == JSXML_CLASS_LIST) {
3276 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
3277 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3278 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
3279 ok = DescendantsHelper(cx, kid, nameqn, list);
3285 ok = DescendantsHelper(cx, xml, nameqn, list);
3287 js_LeaveLocalRootScopeWithResult(cx, list);
3294 /* Recursive (JSXML *) parameterized version of Equals. */
3296 XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3300 JSXML *kid, *vkid, *attr, *vattr;
3301 JSObject *xobj, *vobj;
3304 if (xml->xml_class != vxml->xml_class) {
3305 if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
3306 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3310 if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3311 vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3323 EqualStrings(qn->getQNameLocalName(), vqn->getQNameLocalName()) &&
3324 EqualStrings(qn->getNameURI(), vqn->getNameURI());
3331 if (JSXML_HAS_VALUE(xml)) {
3332 if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, bp))
3334 } else if (xml->xml_kids.length != vxml->xml_kids.length) {
3338 JSXMLArrayCursor cursor(&xml->xml_kids);
3339 JSXMLArrayCursor vcursor(&vxml->xml_kids);
3341 kid = (JSXML *) cursor.getNext();
3342 vkid = (JSXML *) vcursor.getNext();
3343 if (!kid || !vkid) {
3344 *bp = !kid && !vkid;
3347 xobj = js_GetXMLObject(cx, kid);
3348 vobj = js_GetXMLObject(cx, vkid);
3349 if (!xobj || !vobj ||
3350 !js_TestXMLEquality(cx, ObjectValue(*xobj), ObjectValue(*vobj), bp))
3357 if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3358 n = xml->xml_attrs.length;
3359 if (n != vxml->xml_attrs.length)
3361 for (i = 0; *bp && i < n; i++) {
3362 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3365 j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3366 if (j == XML_NOT_FOUND) {
3370 vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3373 if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, bp))
3382 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3384 Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3389 if (JSVAL_IS_PRIMITIVE(v)) {
3391 if (xml->xml_class == JSXML_CLASS_LIST) {
3392 if (xml->xml_kids.length == 1) {
3393 vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3396 vobj = js_GetXMLObject(cx, vxml);
3399 return js_TestXMLEquality(cx, ObjectValue(*vobj), Valueify(v), bp);
3401 if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3405 vobj = JSVAL_TO_OBJECT(v);
3406 if (!vobj->isXML()) {
3409 vxml = (JSXML *) vobj->getPrivate();
3410 if (!XMLEquals(cx, xml, vxml, bp))
3418 CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3420 JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3424 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3425 JSMSG_CYCLIC_VALUE, js_XML_str);
3428 } while ((xml = xml->parent) != NULL);
3433 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3435 Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3442 if (!JSXML_HAS_KIDS(xml))
3447 if (!JSVAL_IS_PRIMITIVE(v)) {
3448 vobj = JSVAL_TO_OBJECT(v);
3449 if (vobj->isXML()) {
3450 vxml = (JSXML *) vobj->getPrivate();
3451 if (vxml->xml_class == JSXML_CLASS_LIST) {
3452 n = vxml->xml_kids.length;
3455 for (j = 0; j < n; j++) {
3456 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3459 if (!CheckCycle(cx, xml, kid))
3462 } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3463 /* OPTION: enforce that descendants have superset namespaces. */
3464 if (!CheckCycle(cx, xml, vxml))
3470 str = js_ValueToString(cx, Valueify(v));
3474 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3477 vxml->xml_value = str;
3480 if (i > xml->xml_kids.length)
3481 i = xml->xml_kids.length;
3483 if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3486 if (vxml->xml_class == JSXML_CLASS_LIST) {
3487 for (j = 0; j < n; j++) {
3488 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3492 XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3494 /* OPTION: enforce that descendants have superset namespaces. */
3498 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3504 IndexToId(JSContext *cx, uint32 index, jsid *idp)
3509 if (index <= JSID_INT_MAX) {
3510 *idp = INT_TO_JSID(index);
3512 str = js_NumberToString(cx, (jsdouble) index);
3515 atom = js_AtomizeString(cx, str, 0);
3518 *idp = ATOM_TO_JSID(atom);
3523 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3525 Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3532 if (!JSXML_HAS_KIDS(xml))
3537 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3538 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3540 n = xml->xml_kids.length;
3545 if (!JSVAL_IS_PRIMITIVE(v)) {
3546 vobj = JSVAL_TO_OBJECT(v);
3548 vxml = (JSXML *) vobj->getPrivate();
3551 switch (vxml ? JSXMLClass(vxml->xml_class) : JSXML_CLASS_LIMIT) {
3552 case JSXML_CLASS_ELEMENT:
3553 /* OPTION: enforce that descendants have superset namespaces. */
3554 if (!CheckCycle(cx, xml, vxml))
3556 case JSXML_CLASS_COMMENT:
3557 case JSXML_CLASS_PROCESSING_INSTRUCTION:
3558 case JSXML_CLASS_TEXT:
3561 case JSXML_CLASS_LIST:
3563 DeleteByIndex(cx, xml, i);
3564 if (!Insert(cx, xml, i, v))
3569 str = js_ValueToString(cx, Valueify(v));
3573 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3576 vxml->xml_value = str;
3581 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3585 if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3593 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3595 DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn,
3599 uint32 index, deleteCount;
3601 JSXMLNameMatcher matcher;
3603 if (xml->xml_class == JSXML_CLASS_LIST) {
3604 array = &xml->xml_kids;
3605 for (index = 0; index < array->length; index++) {
3606 kid = XMLARRAY_MEMBER(array, index, JSXML);
3607 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT)
3608 DeleteNamedProperty(cx, kid, nameqn, attributes);
3610 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3612 array = &xml->xml_attrs;
3613 matcher = MatchAttrName;
3615 array = &xml->xml_kids;
3616 matcher = MatchElemName;
3619 for (index = 0; index < array->length; index++) {
3620 kid = XMLARRAY_MEMBER(array, index, JSXML);
3621 if (kid && matcher(nameqn, kid)) {
3623 XMLArrayDelete(cx, array, index, JS_FALSE);
3625 } else if (deleteCount != 0) {
3626 XMLARRAY_SET_MEMBER(array,
3627 index - deleteCount,
3628 array->vector[index]);
3631 array->length -= deleteCount;
3635 /* ECMA-357 9.2.1.3 index case. */
3637 DeleteListElement(JSContext *cx, JSXML *xml, uint32 index)
3639 JSXML *kid, *parent;
3642 JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST);
3644 if (index < xml->xml_kids.length) {
3645 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3647 parent = kid->parent;
3649 JS_ASSERT(parent != xml);
3650 JS_ASSERT(JSXML_HAS_KIDS(parent));
3652 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
3653 DeleteNamedProperty(cx, parent, kid->name, JS_TRUE);
3655 kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
3657 JS_ASSERT(kidIndex != XML_NOT_FOUND);
3658 DeleteByIndex(cx, parent, kidIndex);
3661 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3667 SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
3669 JSXMLArray *nsarray;
3673 nsarray = &xml->xml_namespaces;
3674 while ((xml = xml->parent) != NULL) {
3675 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3676 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3677 if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
3678 if (!XMLARRAY_APPEND(cx, nsarray, ns))
3687 GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list)
3690 JSXMLNameMatcher matcher;
3693 if (xml->xml_class == JSXML_CLASS_LIST) {
3694 JSXMLArrayCursor cursor(&xml->xml_kids);
3695 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3696 if (kid->xml_class == JSXML_CLASS_ELEMENT &&
3697 !GetNamedProperty(cx, kid, nameqn, list)) {
3701 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3702 attrs = (nameqn->getClass() == &js_AttributeNameClass);
3704 array = &xml->xml_attrs;
3705 matcher = MatchAttrName;
3707 array = &xml->xml_kids;
3708 matcher = MatchElemName;
3711 JSXMLArrayCursor cursor(array);
3712 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3713 if (matcher(nameqn, kid)) {
3715 kid->xml_class == JSXML_CLASS_ELEMENT &&
3716 !SyncInScopeNamespaces(cx, kid)) {
3719 if (!Append(cx, list, kid))
3728 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3730 GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3732 JSXML *xml, *list, *kid;
3734 JSObject *kidobj, *listobj;
3738 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3742 if (js_IdIsIndex(id, &index)) {
3743 if (!JSXML_HAS_KIDS(xml)) {
3744 *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
3747 * ECMA-357 9.2.1.1 starts here.
3749 * Erratum: 9.2 is not completely clear that indexed properties
3750 * correspond to kids, but that's what it seems to say, and it's
3751 * what any sane user would want.
3753 if (index < xml->xml_kids.length) {
3754 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3759 kidobj = js_GetXMLObject(cx, kid);
3763 *vp = OBJECT_TO_JSVAL(kidobj);
3772 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3774 nameqn = ToXMLName(cx, IdToJsval(id), &funid);
3777 if (!JSID_IS_VOID(funid))
3778 return GetXMLFunction(cx, obj, funid, vp);
3780 jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL };
3781 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
3783 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3787 roots[1] = OBJECT_TO_JSVAL(listobj);
3789 list = (JSXML *) listobj->getPrivate();
3790 if (!GetNamedProperty(cx, xml, nameqn, list))
3794 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
3795 * given list's [[TargetProperty]] to the property that is being
3796 * appended. This means that any use of the internal [[Get]]
3797 * property returns a list which, when used by e.g. [[Insert]]
3798 * duplicates the last element matched by id. See bug 336921.
3800 list->xml_target = xml;
3801 list->xml_targetprop = nameqn;
3802 *vp = OBJECT_TO_JSVAL(listobj);
3807 CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
3809 JS_ASSERT(xml->object != obj);
3811 xml = DeepCopy(cx, xml, obj, 0);
3815 JS_ASSERT(xml->object == obj);
3819 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
3820 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
3823 KidToString(JSContext *cx, JSXML *xml, uint32 index)
3828 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3830 return cx->runtime->emptyString;
3831 kidobj = js_GetXMLObject(cx, kid);
3834 return js_ValueToString(cx, ObjectValue(*kidobj));
3837 /* Forward declared -- its implementation uses other statics that call it. */
3839 ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
3841 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
3843 PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
3845 JSBool ok, primitiveAssign;
3846 enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
3847 JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
3848 JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
3849 JSObject *targetprop, *nameqn, *attrqn;
3850 uint32 index, i, j, k, n, q, matchIndex;
3851 jsval attrval, nsval;
3855 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3859 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
3863 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
3865 if (!JSVAL_IS_PRIMITIVE(*vp)) {
3866 vobj = JSVAL_TO_OBJECT(*vp);
3868 vxml = (JSXML *) vobj->getPrivate();
3871 ok = js_EnterLocalRootScope(cx);
3875 MUST_FLOW_THROUGH("out");
3877 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
3878 roots[ID_ROOT] = IdToJsval(id);
3879 roots[VAL_ROOT] = *vp;
3880 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
3882 if (js_IdIsIndex(id, &index)) {
3883 if (xml->xml_class != JSXML_CLASS_LIST) {
3884 /* See NOTE in spec: this variation is reserved for future use. */
3885 ReportBadXMLName(cx, IdToValue(id));
3890 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
3895 if (xml->xml_target) {
3896 ok = ResolveValue(cx, xml->xml_target, &rxml);
3901 JS_ASSERT(rxml->object);
3907 if (index >= xml->xml_kids.length) {
3910 if (rxml->xml_class == JSXML_CLASS_LIST) {
3911 if (rxml->xml_kids.length != 1)
3913 rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
3916 ok = js_GetXMLObject(cx, rxml) != NULL;
3922 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
3923 * _y.[[Parent]] = r_ where _r_ is the result of
3924 * [[ResolveValue]] called on _x.[[TargetObject]] in
3925 * 2(a)(i). This can result in text parenting text:
3927 * var MYXML = new XML();
3928 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
3930 * (testcase from Werner Sharp <wsharp@macromedia.com>).
3932 * To match insertChildAfter, insertChildBefore,
3933 * prependChild, and setChildren, we should silently
3934 * do nothing in this case.
3936 if (!JSXML_HAS_KIDS(rxml))
3940 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
3941 targetprop = xml->xml_targetprop;
3942 if (!targetprop || IS_STAR(targetprop->getQNameLocalName())) {
3943 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
3944 kid = js_NewXML(cx, JSXML_CLASS_TEXT);
3948 nameobj = targetprop;
3949 if (nameobj->getClass() == &js_AttributeNameClass) {
3952 * Note that rxml can't be null here, because target
3953 * and targetprop are non-null.
3955 ok = GetProperty(cx, rxml->object, id, &attrval);
3958 if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
3960 attrobj = JSVAL_TO_OBJECT(attrval);
3961 attr = (JSXML *) attrobj->getPrivate();
3962 if (JSXML_LENGTH(attr) != 0)
3965 kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
3968 kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
3973 /* An important bit of 2(c)(ii). */
3974 kid->name = targetprop;
3977 /* Final important bit of 2(c)(ii). */
3981 i = xml->xml_kids.length;
3982 if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
3984 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
3985 * y.[[Parent]] is here called kid->parent, which we know
3986 * from 2(c)(ii) is _r_, here called rxml. So let's just
3987 * test that! Erratum, the spec should be simpler here.
3990 JS_ASSERT(JSXML_HAS_KIDS(rxml));
3991 n = rxml->xml_kids.length;
3993 if (n != 0 && i != 0) {
3994 for (n = j, j = 0; j < n; j++) {
3995 if (rxml->xml_kids.vector[j] ==
3996 xml->xml_kids.vector[i-1]) {
4002 kidobj = js_GetXMLObject(cx, kid);
4005 ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4012 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4013 * typo for [[TargetProperty]].
4016 kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4017 ? vxml->xml_targetprop
4023 ok = Append(cx, xml, kid);
4030 vxml->xml_class == JSXML_CLASS_TEXT ||
4031 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4032 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4035 roots[VAL_ROOT] = *vp;
4039 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4042 parent = kid->parent;
4043 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4044 nameobj = kid->name;
4045 if (nameobj->getClass() != &js_AttributeNameClass) {
4046 nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(),
4047 nameobj->getQNameLocalName());
4051 id = OBJECT_TO_JSID(nameobj);
4055 parentobj = js_GetXMLObject(cx, parent);
4058 ok = PutProperty(cx, parentobj, id, strict, vp);
4063 ok = GetProperty(cx, parentobj, id, vp);
4066 attr = (JSXML *) JSVAL_TO_OBJECT(*vp)->getPrivate();
4068 /* 2(e)(iii) - the length check comes from the bug 375406. */
4069 if (attr->xml_kids.length != 0)
4070 xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
4075 else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4079 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4080 * if we do that we never change the parent of each child in the
4081 * list. Since [[Put]] when called on an XML object deeply copies
4082 * the provided list _V_, we also do so here. Perhaps the shallow
4083 * copy was a misguided optimization?
4085 copy = DeepCopyInLRS(cx, vxml, 0);
4088 copyobj = js_GetXMLObject(cx, copy);
4092 JS_ASSERT(parent != xml);
4094 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4095 JS_ASSERT(q != XML_NOT_FOUND);
4096 ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj));
4101 /* Erratum: this loop in the spec is useless. */
4102 for (j = 0, n = copy->xml_kids.length; j < n; j++) {
4103 kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
4104 JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML)
4112 * Erratum: notice the unhandled zero-length V basis case and
4113 * the off-by-one errors for the n != 0 cases in the spec.
4115 n = copy->xml_kids.length;
4117 XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4119 ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4123 for (j = 0; j < n; j++)
4124 xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4129 else if (vxml || JSXML_HAS_VALUE(kid)) {
4131 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4132 JS_ASSERT(q != XML_NOT_FOUND);
4133 ok = Replace(cx, parent, q, *vp);
4137 vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4140 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
4145 * Erratum: _V_ may not be of type XML, but all index-named
4146 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4147 * according to 9.2.1.1 Overview and other places in the spec.
4149 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4150 * or an XML/XMLList object. If *vp is a string, call ToXML
4151 * on it to satisfy the constraint.
4154 JS_ASSERT(JSVAL_IS_STRING(*vp));
4155 vobj = ToXML(cx, *vp);
4158 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4159 vxml = (JSXML *) vobj->getPrivate();
4161 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4166 kidobj = js_GetXMLObject(cx, kid);
4169 id = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
4170 ok = PutProperty(cx, kidobj, id, strict, vp);
4176 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4178 nameqn = ToXMLName(cx, IdToJsval(id), &funid);
4181 if (!JSID_IS_VOID(funid)) {
4182 ok = js_SetProperty(cx, obj, funid, Valueify(vp), false);
4186 roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
4188 if (xml->xml_class == JSXML_CLASS_LIST) {
4190 * Step 3 of 9.2.1.2.
4191 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4192 * or an r with r.[[Length]] != 1, throw TypeError.
4194 n = JSXML_LENGTH(xml);
4198 ok = ResolveValue(cx, xml, &rxml);
4201 if (!rxml || JSXML_LENGTH(rxml) != 1)
4203 ok = Append(cx, xml, rxml);
4207 JS_ASSERT(JSXML_LENGTH(xml) == 1);
4208 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4211 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
4212 obj = js_GetXMLObject(cx, xml);
4215 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4217 /* FALL THROUGH to non-list case */
4222 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4223 * effort in ToString or [[DeepCopy]].
4226 if (JSXML_HAS_VALUE(xml))
4230 vxml->xml_class == JSXML_CLASS_TEXT ||
4231 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4232 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4236 rxml = DeepCopyInLRS(cx, vxml, 0);
4237 if (!rxml || !js_GetXMLObject(cx, rxml))
4240 *vp = OBJECT_TO_JSVAL(vxml->object);
4242 roots[VAL_ROOT] = *vp;
4246 * Erratum: why is this done here, so early? use is way later....
4248 ok = js_GetDefaultXMLNamespace(cx, &nsval);
4252 if (nameobj->getClass() == &js_AttributeNameClass) {
4254 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4258 if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4259 n = vxml->xml_kids.length;
4261 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4263 JSString *left = KidToString(cx, vxml, 0);
4267 JSString *space = cx->runtime->atomState.spaceAtom;
4268 for (i = 1; i < n; i++) {
4269 left = js_ConcatStrings(cx, left, space);
4272 JSString *right = KidToString(cx, vxml, i);
4275 left = js_ConcatStrings(cx, left, right);
4280 roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4283 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4286 roots[VAL_ROOT] = *vp;
4291 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4292 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4295 attrqn = attr->name;
4296 if (EqualStrings(attrqn->getQNameLocalName(), nameqn->getQNameLocalName())) {
4297 JSLinearString *uri = nameqn->getNameURI();
4298 if (!uri || EqualStrings(attrqn->getNameURI(), uri)) {
4302 DeleteNamedProperty(cx, xml, attrqn, JS_TRUE);
4313 JSLinearString *uri = nameqn->getNameURI();
4314 JSLinearString *left, *right;
4316 left = right = cx->runtime->emptyString;
4319 right = nameqn->getNamePrefix();
4321 nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4326 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4330 attr->name = nameqn;
4333 ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4338 ns = GetNamespace(cx, nameqn, NULL);
4341 ok = AddInScopeNamespace(cx, xml, ns);
4347 attr->xml_value = JSVAL_TO_STRING(*vp);
4352 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4353 !IS_STAR(nameqn->getQNameLocalName())) {
4359 primitiveAssign = !vxml && !IS_STAR(nameqn->getQNameLocalName());
4362 k = n = xml->xml_kids.length;
4363 matchIndex = XML_NOT_FOUND;
4367 kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
4368 if (kid && MatchElemName(nameqn, kid)) {
4369 if (matchIndex != XML_NOT_FOUND)
4370 DeleteByIndex(cx, xml, matchIndex);
4377 * Erratum: ECMA-357 specified child insertion inconsistently:
4378 * insertChildBefore and insertChildAfter insert an arbitrary XML
4379 * instance, and therefore can create cycles, but appendChild as
4380 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4381 * its argument. But the "Semantics" in 13.4.4.3 do not include
4382 * any [[DeepCopy]] call.
4384 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4385 * required adding cycle detection, and allowing duplicate kids to
4386 * be created (see comment 6 in the bug). Allowing duplicate kid
4387 * references means the loop above will delete all but the lowest
4388 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4389 * parent. Thus the need to restore parent here. This is covered
4390 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4393 JS_ASSERT(kid2->parent == xml || !kid2->parent);
4399 if (matchIndex == XML_NOT_FOUND) {
4404 if (primitiveAssign) {
4405 JSLinearString *uri = nameqn->getNameURI();
4406 JSLinearString *left, *right;
4408 ns = JSVAL_TO_OBJECT(nsval);
4409 left = ns->getNameURI();
4410 right = ns->getNamePrefix();
4413 right = nameqn->getNamePrefix();
4415 nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4420 vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4423 vxml = (JSXML *) vobj->getPrivate();
4425 vxml->name = nameqn;
4428 ns = GetNamespace(cx, nameqn, NULL);
4431 ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj));
4434 ok = AddInScopeNamespace(cx, vxml, ns);
4441 if (primitiveAssign) {
4442 JSXMLArrayCursor cursor(&xml->xml_kids);
4443 cursor.index = matchIndex;
4444 kid = (JSXML *) cursor.getCurrent();
4445 if (JSXML_HAS_KIDS(kid)) {
4446 kid->xml_kids.finish(cx);
4447 kid->xml_kids.init();
4448 ok = kid->xml_kids.setCapacity(cx, 1);
4452 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4454 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4455 if (ok && !JSVAL_TO_STRING(*vp)->empty()) {
4456 roots[VAL_ROOT] = *vp;
4457 if ((JSXML *) cursor.getCurrent() == kid)
4458 ok = Replace(cx, kid, 0, *vp);
4463 ok = Replace(cx, xml, matchIndex, *vp);
4468 js_LeaveLocalRootScope(cx);
4473 JSAutoByteString bytes;
4474 if (js_ValueToPrintable(cx, IdToValue(id), &bytes))
4475 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XMLLIST_PUT, bytes.ptr());
4482 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4484 ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4486 JSXML *target, *base;
4487 JSObject *targetprop;
4491 if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4492 if (!js_GetXMLObject(cx, list))
4498 target = list->xml_target;
4499 targetprop = list->xml_targetprop;
4500 if (!target || !targetprop || IS_STAR(targetprop->getQNameLocalName())) {
4505 if (targetprop->getClass() == &js_AttributeNameClass) {
4510 if (!ResolveValue(cx, target, &base))
4516 if (!js_GetXMLObject(cx, base))
4519 id = OBJECT_TO_JSID(targetprop);
4520 if (!GetProperty(cx, base->object, id, &tv))
4522 target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4524 if (JSXML_LENGTH(target) == 0) {
4525 if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4529 tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4530 if (!PutProperty(cx, base->object, id, false, &tv))
4532 if (!GetProperty(cx, base->object, id, &tv))
4534 target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4542 HasNamedProperty(JSXML *xml, JSObject *nameqn)
4546 JSXMLNameMatcher matcher;
4549 if (xml->xml_class == JSXML_CLASS_LIST) {
4551 JSXMLArrayCursor cursor(&xml->xml_kids);
4552 while (JSXML *kid = (JSXML *) cursor.getNext()) {
4553 found = HasNamedProperty(kid, nameqn);
4560 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4561 if (nameqn->getClass() == &js_AttributeNameClass) {
4562 array = &xml->xml_attrs;
4563 matcher = MatchAttrName;
4565 array = &xml->xml_kids;
4566 matcher = MatchElemName;
4568 for (i = 0, n = array->length; i < n; i++) {
4569 JSXML *kid = XMLARRAY_MEMBER(array, i, JSXML);
4570 if (kid && matcher(nameqn, kid))
4579 HasIndexedProperty(JSXML *xml, uint32 i)
4581 if (xml->xml_class == JSXML_CLASS_LIST)
4582 return i < JSXML_LENGTH(xml);
4584 if (xml->xml_class == JSXML_CLASS_ELEMENT)
4591 HasSimpleContent(JSXML *xml);
4594 HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
4600 JS_ASSERT(obj->getClass() == &js_XMLClass);
4602 if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
4605 xml = (JSXML *) obj->getPrivate();
4606 if (HasSimpleContent(xml)) {
4607 AutoObjectRooter tvr(cx);
4610 * Search in String.prototype to set found whenever
4611 * GetXMLFunction returns existing function.
4613 if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
4616 JS_ASSERT(tvr.object());
4617 if (!js_LookupProperty(cx, tvr.object(), funid, &pobj, &prop))
4621 *found = (prop != NULL);
4625 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4627 HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found)
4635 xml = (JSXML *) obj->getPrivate();
4636 if (!js_IdValIsIndex(cx, id, &i, &isIndex))
4640 *found = HasIndexedProperty(xml, i);
4642 qn = ToXMLName(cx, id, &funid);
4645 if (!JSID_IS_VOID(funid)) {
4646 if (!HasFunctionProperty(cx, obj, funid, found))
4649 *found = HasNamedProperty(xml, qn);
4656 xml_finalize(JSContext *cx, JSObject *obj)
4658 JSXML *xml = (JSXML *) obj->getPrivate();
4661 if (xml->object == obj)
4666 xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
4671 for (i = 0; i < len; i++) {
4674 JS_SET_TRACING_INDEX(trc, "xml_vector", i);
4681 * XML objects are native. Thus xml_lookupProperty must return a valid
4682 * Shape pointer parameter via *propp to signify "property found". Since the
4683 * only call to xml_lookupProperty is via JSObject::lookupProperty, and then
4684 * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
4685 * JSOP_IN case in the interpreter, the only time we add a Shape here is when
4686 * an unqualified name is being accessed or when "name in xml" is called.
4688 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4689 * giving it an shape with (getter, setter) == (GetProperty, PutProperty).
4691 * NB: xml_deleteProperty must take care to remove any property added here.
4693 * FIXME This clashes with the function namespace implementation which also
4694 * uses native properties. Effectively after xml_lookupProperty any property
4695 * stored previously using assignments to xml.function::name will be removed.
4696 * We partially workaround the problem in GetXMLFunction. There we take
4697 * advantage of the fact that typically function:: is used to access the
4698 * functions from XML.prototype. So when js_GetProperty returns a non-function
4699 * property, we assume that it represents the result of GetProperty setter
4700 * hiding the function and use an extra prototype chain lookup to recover it.
4701 * For a proper solution see bug 355257.
4704 xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4713 xml = (JSXML *) obj->getPrivate();
4714 if (js_IdIsIndex(id, &i)) {
4715 found = HasIndexedProperty(xml, i);
4717 qn = ToXMLName(cx, IdToJsval(id), &funid);
4720 if (!JSID_IS_VOID(funid))
4721 return js_LookupProperty(cx, obj, funid, objp, propp);
4722 found = HasNamedProperty(xml, qn);
4728 const Shape *shape =
4729 js_AddNativeProperty(cx, obj, id,
4730 Valueify(GetProperty), Valueify(PutProperty),
4731 SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
4737 *propp = (JSProperty *) shape;
4743 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
4744 PropertyOp getter, StrictPropertyOp setter, uintN attrs)
4746 if (IsFunctionObject(*v) || getter || setter ||
4747 (attrs & JSPROP_ENUMERATE) == 0 ||
4748 (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
4749 return js_DefineProperty(cx, obj, id, v, getter, setter, attrs);
4752 jsval tmp = Jsvalify(*v);
4753 return PutProperty(cx, obj, id, false, &tmp);
4757 xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
4759 if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) {
4764 return GetProperty(cx, obj, id, Jsvalify(vp));
4768 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
4770 return PutProperty(cx, obj, id, strict, Jsvalify(vp));
4774 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
4777 if (!HasProperty(cx, obj, IdToJsval(id), &found))
4780 *attrsp = found ? JSPROP_ENUMERATE : 0;
4785 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
4788 if (!HasProperty(cx, obj, IdToJsval(id), &found))
4792 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4793 JSMSG_CANT_SET_XML_ATTRS);
4800 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
4808 idval = IdToJsval(id);
4809 xml = (JSXML *) obj->getPrivate();
4810 if (js_IdIsIndex(id, &index)) {
4811 if (xml->xml_class != JSXML_CLASS_LIST) {
4812 /* See NOTE in spec: this variation is reserved for future use. */
4813 ReportBadXMLName(cx, IdToValue(id));
4817 /* ECMA-357 9.2.1.3. */
4818 DeleteListElement(cx, xml, index);
4820 nameqn = ToXMLName(cx, idval, &funid);
4823 if (!JSID_IS_VOID(funid))
4824 return js_DeleteProperty(cx, obj, funid, rval, false);
4826 DeleteNamedProperty(cx, xml, nameqn,
4827 nameqn->getClass() == &js_AttributeNameClass);
4831 * If this object has its own (mutable) scope, then we may have added a
4832 * property to the scope in xml_lookupProperty for it to return to mean
4833 * "found" and to provide a handle for access operations to call the
4834 * property's getter or setter. But now it's time to remove any such
4835 * property, to purge the property cache and remove the scope entry.
4837 if (!obj->nativeEmpty() && !js_DeleteProperty(cx, obj, id, rval, false))
4840 rval->setBoolean(true);
4845 xml_convert(JSContext *cx, JSObject *obj, JSType type, Value *rval)
4847 return js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, rval);
4851 xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
4854 uint32 length, index;
4855 JSXMLArrayCursor *cursor;
4857 xml = (JSXML *)obj->getPrivate();
4858 length = JSXML_LENGTH(xml);
4861 case JSENUMERATE_INIT:
4862 case JSENUMERATE_INIT_ALL:
4864 statep->setInt32(0);
4866 cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
4869 statep->setPrivate(cursor);
4872 *idp = INT_TO_JSID(length);
4875 case JSENUMERATE_NEXT:
4876 if (statep->isInt32(0)) {
4880 cursor = (JSXMLArrayCursor *) statep->toPrivate();
4881 if (cursor && cursor->array && (index = cursor->index) < length) {
4882 *idp = INT_TO_JSID(index);
4883 cursor->index = index + 1;
4888 case JSENUMERATE_DESTROY:
4889 if (!statep->isInt32(0)) {
4890 cursor = (JSXMLArrayCursor *) statep->toPrivate();
4892 cx->destroy(cursor);
4901 xml_typeOf(JSContext *cx, JSObject *obj)
4907 xml_hasInstance(JSContext *cx, JSObject *obj, const Value *, JSBool *bp)
4913 xml_trace(JSTracer *trc, JSObject *obj)
4915 JSXML *xml = (JSXML *) obj->getPrivate();
4917 JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private");
4921 xml_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
4923 JS_ASSERT(obj->isExtensible());
4929 xml_clear(JSContext *cx, JSObject *obj)
4934 HasSimpleContent(JSXML *xml)
4941 switch (xml->xml_class) {
4942 case JSXML_CLASS_COMMENT:
4943 case JSXML_CLASS_PROCESSING_INSTRUCTION:
4945 case JSXML_CLASS_LIST:
4946 if (xml->xml_kids.length == 0)
4948 if (xml->xml_kids.length == 1) {
4949 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4958 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
4959 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4960 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
4970 * 11.2.2.1 Step 3(d) onward.
4973 js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
4975 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
4977 if (JSID_IS_OBJECT(id)) {
4980 if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid))
4982 if (!JSID_IS_VOID(funid))
4987 * As our callers have a bad habit of passing a pointer to an unrooted
4988 * local value as vp, we use a proper root here.
4990 AutoValueRooter tvr(cx);
4991 JSBool ok = GetXMLFunction(cx, obj, id, Jsvalify(tvr.addr()));
4997 js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp)
5002 JSString *str, *vstr;
5007 if (v1.isObject() && v1.toObject().isXML()) {
5008 obj = &v1.toObject();
5012 obj = &v2.toObject();
5015 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
5017 xml = (JSXML *) obj->getPrivate();
5019 if (!JSVAL_IS_PRIMITIVE(v)) {
5020 vobj = JSVAL_TO_OBJECT(v);
5022 vxml = (JSXML *) vobj->getPrivate();
5025 if (xml->xml_class == JSXML_CLASS_LIST) {
5026 ok = Equals(cx, xml, v, bp);
5028 if (vxml->xml_class == JSXML_CLASS_LIST) {
5029 ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
5031 if (((xml->xml_class == JSXML_CLASS_TEXT ||
5032 xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5033 HasSimpleContent(vxml)) ||
5034 ((vxml->xml_class == JSXML_CLASS_TEXT ||
5035 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5036 HasSimpleContent(xml))) {
5037 ok = js_EnterLocalRootScope(cx);
5039 ok = (str = js_ValueToString(cx, ObjectValue(*obj))) &&
5040 (vstr = js_ValueToString(cx, Valueify(v)));
5042 ok = EqualStrings(cx, str, vstr, bp);
5043 js_LeaveLocalRootScope(cx);
5046 ok = XMLEquals(cx, xml, vxml, bp);
5050 ok = js_EnterLocalRootScope(cx);
5052 if (HasSimpleContent(xml)) {
5053 ok = (str = js_ValueToString(cx, ObjectValue(*obj))) &&
5054 (vstr = js_ValueToString(cx, Valueify(v)));
5056 ok = EqualStrings(cx, str, vstr, bp);
5057 } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
5058 str = js_ValueToString(cx, ObjectValue(*obj));
5061 } else if (JSVAL_IS_STRING(v)) {
5062 ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), bp);
5064 ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5066 d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5067 : JSVAL_TO_DOUBLE(v);
5068 *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
5074 js_LeaveLocalRootScope(cx);
5081 js_ConcatenateXML(JSContext *cx, JSObject *obj, JSObject *robj, Value *vp)
5085 JSXML *list, *lxml, *rxml;
5087 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
5088 ok = js_EnterLocalRootScope(cx);
5092 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5098 list = (JSXML *) listobj->getPrivate();
5099 lxml = (JSXML *) obj->getPrivate();
5100 ok = Append(cx, list, lxml);
5104 JS_ASSERT(robj->isXML());
5105 rxml = (JSXML *) robj->getPrivate();
5106 ok = Append(cx, list, rxml);
5110 vp->setObject(*listobj);
5112 js_LeaveLocalRootScopeWithResult(cx, *vp);
5116 JS_FRIEND_DATA(Class) js_XMLClass = {
5118 JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
5119 JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
5120 PropertyStub, /* addProperty */
5121 PropertyStub, /* delProperty */
5122 PropertyStub, /* getProperty */
5123 StrictPropertyStub, /* setProperty */
5128 NULL, /* reserved0 */
5129 NULL, /* checkAccess */
5131 NULL, /* construct */
5132 NULL, /* xdrObject */
5134 JS_CLASS_TRACE(xml_trace),
5148 NULL, /* thisObject */
5154 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
5160 JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
5162 *objp = ToObject(cx, Valueify(&vp[1]));
5165 xml = (JSXML *) GetInstancePrivate(cx, *objp, &js_XMLClass, Valueify(vp + 2));
5166 if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5169 if (xml->xml_kids.length == 1) {
5170 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5172 *objp = js_GetXMLObject(cx, xml);
5175 vp[1] = OBJECT_TO_JSVAL(*objp);
5180 fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp));
5181 JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
5182 JSAutoByteString funNameBytes;
5183 if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
5184 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_LIST_XML_METHOD,
5190 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5191 #define XML_METHOD_PROLOG \
5192 JSObject *obj = ToObject(cx, Valueify(&vp[1])); \
5195 JSXML *xml = (JSXML *)GetInstancePrivate(cx, obj, &js_XMLClass, Valueify(vp+2)); \
5199 #define NON_LIST_XML_METHOD_PROLOG \
5201 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5204 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5207 xml_addNamespace(JSContext *cx, uintN argc, jsval *vp)
5211 NON_LIST_XML_METHOD_PROLOG;
5212 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5214 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5218 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
5220 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5222 ns = JSVAL_TO_OBJECT(*vp);
5223 if (!AddInScopeNamespace(cx, xml, ns))
5225 ns->setNamespaceDeclared(JSVAL_TRUE);
5228 *vp = OBJECT_TO_JSVAL(obj);
5233 xml_appendChild(JSContext *cx, uintN argc, jsval *vp)
5239 NON_LIST_XML_METHOD_PROLOG;
5240 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5245 if (!js_GetAnyName(cx, &name))
5248 if (!GetProperty(cx, obj, name, &v))
5251 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5252 vobj = JSVAL_TO_OBJECT(v);
5253 JS_ASSERT(vobj->isXML());
5254 vxml = (JSXML *) vobj->getPrivate();
5255 JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
5257 if (!IndexToId(cx, vxml->xml_kids.length, &name))
5259 *vp = (argc != 0) ? vp[2] : JSVAL_VOID;
5261 if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, false, vp))
5264 *vp = OBJECT_TO_JSVAL(obj);
5268 /* XML and XMLList */
5270 xml_attribute(JSContext *cx, uintN argc, jsval *vp)
5275 js_ReportMissingArg(cx, Valueify(*vp), 0);
5279 qn = ToAttributeName(cx, vp[2]);
5282 vp[2] = OBJECT_TO_JSVAL(qn); /* local root */
5284 jsid id = OBJECT_TO_JSID(qn);
5285 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5288 return GetProperty(cx, obj, id, vp);
5291 /* XML and XMLList */
5293 xml_attributes(JSContext *cx, uintN argc, jsval *vp)
5295 jsval name = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
5296 JSObject *qn = ToAttributeName(cx, name);
5300 AutoObjectRooter tvr(cx, qn);
5301 jsid id = OBJECT_TO_JSID(qn);
5302 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5305 return GetProperty(cx, obj, id, vp);
5309 xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5314 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5318 *rval = OBJECT_TO_JSVAL(listobj);
5319 list = (JSXML *) listobj->getPrivate();
5320 list->xml_target = xml;
5325 ValueToId(JSContext *cx, jsval v, AutoIdRooter *idr)
5327 if (JSVAL_IS_INT(v)) {
5328 jsint i = JSVAL_TO_INT(v);
5329 if (INT_FITS_IN_JSID(i))
5330 *idr->addr() = INT_TO_JSID(i);
5331 else if (!js_ValueToStringId(cx, Valueify(v), idr->addr()))
5333 } else if (JSVAL_IS_STRING(v)) {
5334 JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
5337 *idr->addr() = ATOM_TO_JSID(atom);
5338 } else if (!JSVAL_IS_PRIMITIVE(v)) {
5339 *idr->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v));
5341 ReportBadXMLName(cx, Valueify(v));
5348 xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5356 /* ECMA-357 13.4.4.6 */
5357 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5359 if (!js_IdValIsIndex(cx, name, &index, &isIndex))
5363 if (index >= JSXML_LENGTH(xml)) {
5366 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5370 kidobj = js_GetXMLObject(cx, kid);
5373 *rval = OBJECT_TO_JSVAL(kidobj);
5379 AutoIdRooter idr(cx);
5380 if (!ValueToId(cx, name, &idr))
5383 return GetProperty(cx, obj, idr.id(), rval);
5386 /* XML and XMLList */
5388 xml_child(JSContext *cx, uintN argc, jsval *vp)
5395 jsval name = argc != 0 ? vp[2] : JSVAL_VOID;
5396 if (xml->xml_class == JSXML_CLASS_LIST) {
5397 /* ECMA-357 13.5.4.4 */
5398 list = xml_list_helper(cx, xml, vp);
5402 JSXMLArrayCursor cursor(&xml->xml_kids);
5403 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5404 kidobj = js_GetXMLObject(cx, kid);
5407 if (!xml_child_helper(cx, kidobj, kid, name, &v))
5409 if (JSVAL_IS_VOID(v)) {
5410 /* The property didn't exist in this kid. */
5414 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5415 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5416 if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
5417 !Append(cx, list, vxml)) {
5424 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5425 if (!xml_child_helper(cx, obj, xml, name, vp))
5427 if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp))
5433 xml_childIndex(JSContext *cx, uintN argc, jsval *vp)
5438 NON_LIST_XML_METHOD_PROLOG;
5439 parent = xml->parent;
5440 if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
5441 *vp = DOUBLE_TO_JSVAL(js_NaN);
5444 for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5445 if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5449 if (i <= JSVAL_INT_MAX)
5450 *vp = INT_TO_JSVAL(i);
5452 *vp = DOUBLE_TO_JSVAL(i);
5456 /* XML and XMLList */
5458 xml_children(JSContext *cx, uintN argc, jsval *vp)
5460 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5463 jsid name = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
5464 return GetProperty(cx, obj, name, vp);
5467 /* XML and XMLList */
5469 xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
5471 JSXML *list, *kid, *vxml;
5477 list = xml_list_helper(cx, xml, vp);
5483 if (xml->xml_class == JSXML_CLASS_LIST) {
5484 /* 13.5.4.6 Step 2. */
5485 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5486 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5487 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5488 ok = js_EnterLocalRootScope(cx);
5491 kidobj = js_GetXMLObject(cx, kid);
5493 ok = xml_comments_helper(cx, kidobj, kid, &v);
5498 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
5501 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5502 if (JSXML_LENGTH(vxml) != 0) {
5503 ok = Append(cx, list, vxml);
5510 /* 13.4.4.9 Step 2. */
5511 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5512 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5513 if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
5514 ok = Append(cx, list, kid);
5525 xml_comments(JSContext *cx, uintN argc, jsval *vp)
5528 return xml_comments_helper(cx, obj, xml, vp);
5531 /* XML and XMLList */
5533 xml_contains(JSContext *cx, uintN argc, jsval *vp)
5540 value = argc != 0 ? vp[2] : JSVAL_VOID;
5541 if (xml->xml_class == JSXML_CLASS_LIST) {
5543 JSXMLArrayCursor cursor(&xml->xml_kids);
5544 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5545 kidobj = js_GetXMLObject(cx, kid);
5546 if (!kidobj || !js_TestXMLEquality(cx, ObjectValue(*kidobj), Valueify(value), &eq))
5552 if (!js_TestXMLEquality(cx, ObjectValue(*obj), Valueify(value), &eq))
5555 *vp = BOOLEAN_TO_JSVAL(eq);
5559 /* XML and XMLList */
5561 xml_copy(JSContext *cx, uintN argc, jsval *vp)
5566 copy = DeepCopy(cx, xml, NULL, 0);
5569 *vp = OBJECT_TO_JSVAL(copy->object);
5573 /* XML and XMLList */
5575 xml_descendants(JSContext *cx, uintN argc, jsval *vp)
5581 name = argc == 0 ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5582 list = Descendants(cx, xml, name);
5585 *vp = OBJECT_TO_JSVAL(list->object);
5589 /* XML and XMLList */
5591 xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml,
5592 JSObject *nameqn, jsval *vp)
5600 list = xml_list_helper(cx, xml, vp);
5604 list->xml_targetprop = nameqn;
5607 if (xml->xml_class == JSXML_CLASS_LIST) {
5609 JSXMLArrayCursor cursor(&xml->xml_kids);
5610 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5611 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
5612 ok = js_EnterLocalRootScope(cx);
5615 kidobj = js_GetXMLObject(cx, kid);
5617 ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v);
5622 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
5625 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5626 if (JSXML_LENGTH(vxml) != 0) {
5627 ok = Append(cx, list, vxml);
5634 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5635 JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5636 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
5637 MatchElemName(nameqn, kid)) {
5638 ok = Append(cx, list, kid);
5649 xml_elements(JSContext *cx, uintN argc, jsval *vp)
5657 name = (argc == 0) ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5658 nameqn = ToXMLName(cx, name, &funid);
5662 if (!JSID_IS_VOID(funid))
5663 return xml_list_helper(cx, xml, vp) != NULL;
5665 return xml_elements_helper(cx, obj, xml, nameqn, vp);
5668 /* XML and XMLList */
5670 xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
5675 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5678 if (!InstanceOf(cx, obj, &js_XMLClass, Valueify(vp + 2)))
5681 name = argc != 0 ? vp[2] : JSVAL_VOID;
5682 if (!HasProperty(cx, obj, name, &found))
5688 return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, Valueify(vp));
5691 /* XML and XMLList */
5693 xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp)
5701 switch (xml->xml_class) {
5702 case JSXML_CLASS_ATTRIBUTE:
5703 case JSXML_CLASS_COMMENT:
5704 case JSXML_CLASS_PROCESSING_INSTRUCTION:
5705 case JSXML_CLASS_TEXT:
5708 case JSXML_CLASS_LIST:
5709 if (xml->xml_kids.length == 0) {
5711 } else if (xml->xml_kids.length == 1) {
5712 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5714 kidobj = js_GetXMLObject(cx, kid);
5718 xml = (JSXML *) obj->getPrivate();
5725 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
5726 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5727 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5737 /* XML and XMLList */
5739 xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp)
5742 *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
5747 FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
5749 uint32 length, i, j, n;
5751 JSLinearString *prefix, *prefix2;
5753 length = nsarray->length;
5755 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5757 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
5758 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
5762 prefix = ns->getNamePrefix();
5763 for (j = 0; j < length; j++) {
5764 ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject);
5766 prefix2 = ns2->getNamePrefix();
5767 if ((prefix2 && prefix)
5768 ? EqualStrings(prefix2, prefix)
5769 : EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
5776 if (!XMLARRAY_APPEND(cx, nsarray, ns))
5781 } while ((xml = xml->parent) != NULL);
5782 JS_ASSERT(length == nsarray->length);
5788 * Populate a new JS array with elements of array and place the result into
5789 * rval. rval must point to a rooted location.
5792 NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
5794 JSObject *arrayobj = NewDenseEmptyArray(cx);
5797 *rval = OBJECT_TO_JSVAL(arrayobj);
5799 AutoValueRooter tvr(cx);
5800 for (uint32 i = 0, n = array->length; i < n; i++) {
5801 JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
5804 tvr.set(ObjectValue(*ns));
5805 if (!arrayobj->setProperty(cx, INT_TO_JSID(i), tvr.addr(), false))
5812 xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp)
5814 NON_LIST_XML_METHOD_PROLOG;
5816 AutoNamespaceArray namespaces(cx);
5817 return FindInScopeNamespaces(cx, xml, &namespaces.array) &&
5818 NamespacesToJSArray(cx, &namespaces.array, vp);
5822 xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp)
5828 NON_LIST_XML_METHOD_PROLOG;
5829 *vp = OBJECT_TO_JSVAL(obj);
5830 if (!JSXML_HAS_KIDS(xml) || argc == 0)
5834 if (JSVAL_IS_NULL(arg)) {
5838 if (!VALUE_IS_XML(arg))
5840 kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
5841 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
5842 if (i == XML_NOT_FOUND)
5847 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5850 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
5854 xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp)
5860 NON_LIST_XML_METHOD_PROLOG;
5861 *vp = OBJECT_TO_JSVAL(obj);
5862 if (!JSXML_HAS_KIDS(xml) || argc == 0)
5866 if (JSVAL_IS_NULL(arg)) {
5868 i = xml->xml_kids.length;
5870 if (!VALUE_IS_XML(arg))
5872 kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
5873 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
5874 if (i == XML_NOT_FOUND)
5878 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5881 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
5884 /* XML and XMLList */
5886 xml_length(JSContext *cx, uintN argc, jsval *vp)
5889 if (xml->xml_class != JSXML_CLASS_LIST) {
5892 uint32 l = xml->xml_kids.length;
5893 if (l <= JSVAL_INT_MAX)
5894 *vp = INT_TO_JSVAL(l);
5896 *vp = DOUBLE_TO_JSVAL(l);
5902 xml_localName(JSContext *cx, uintN argc, jsval *vp)
5904 NON_LIST_XML_METHOD_PROLOG;
5905 *vp = xml->name ? xml->name->getQNameLocalNameVal() : JSVAL_NULL;
5910 xml_name(JSContext *cx, uintN argc, jsval *vp)
5912 NON_LIST_XML_METHOD_PROLOG;
5913 *vp = OBJECT_TO_JSVAL(xml->name);
5918 xml_namespace(JSContext *cx, uintN argc, jsval *vp)
5920 JSLinearString *prefix, *nsprefix;
5924 NON_LIST_XML_METHOD_PROLOG;
5925 if (argc == 0 && !JSXML_HAS_NAME(xml)) {
5933 JSString *str = js_ValueToString(cx, Valueify(vp[2]));
5936 prefix = str->ensureLinear(cx);
5939 vp[2] = STRING_TO_JSVAL(prefix); /* local root */
5942 AutoNamespaceArray inScopeNSes(cx);
5943 if (!FindInScopeNamespaces(cx, xml, &inScopeNSes.array))
5947 ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
5952 for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
5953 ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject);
5955 nsprefix = ns->getNamePrefix();
5956 if (nsprefix && EqualStrings(nsprefix, prefix))
5963 *vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns);
5968 xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp)
5970 NON_LIST_XML_METHOD_PROLOG;
5971 if (JSXML_HAS_VALUE(xml))
5974 AutoNamespaceArray ancestors(cx);
5975 AutoNamespaceArray declared(cx);
5978 while ((yml = yml->parent) != NULL) {
5979 JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
5980 for (uint32 i = 0, n = yml->xml_namespaces.length; i < n; i++) {
5981 JSObject *ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
5982 if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
5983 if (!XMLARRAY_APPEND(cx, &ancestors.array, ns))
5989 for (uint32 i = 0, n = xml->xml_namespaces.length; i < n; i++) {
5990 JSObject *ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
5993 if (!IsDeclared(ns))
5995 if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
5996 if (!XMLARRAY_APPEND(cx, &declared.array, ns))
6001 return NamespacesToJSArray(cx, &declared.array, vp);
6004 static const char js_attribute_str[] = "attribute";
6005 static const char js_text_str[] = "text";
6007 /* Exported to jsgc.c #ifdef DEBUG. */
6008 const char *js_xml_class_str[] = {
6012 "processing-instruction",
6018 xml_nodeKind(JSContext *cx, uintN argc, jsval *vp)
6022 NON_LIST_XML_METHOD_PROLOG;
6023 str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6026 *vp = STRING_TO_JSVAL(str);
6031 NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index)
6033 if (xml->xml_class == JSXML_CLASS_LIST)
6034 DeleteListElement(cx, xml, index);
6036 DeleteByIndex(cx, xml, index);
6039 /* XML and XMLList */
6041 xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml)
6048 if (!JSXML_HAS_KIDS(xml))
6051 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6055 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6056 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6059 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6060 kidobj = js_GetXMLObject(cx, kid);
6061 if (!kidobj || !xml_normalize_helper(cx, kidobj, kid))
6063 } else if (kid->xml_class == JSXML_CLASS_TEXT) {
6065 (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
6066 kid2->xml_class == JSXML_CLASS_TEXT) {
6067 str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
6070 NormalizingDelete(cx, xml, i + 1);
6071 n = xml->xml_kids.length;
6072 kid->xml_value = str;
6074 if (kid->xml_value->empty()) {
6075 NormalizingDelete(cx, xml, i);
6076 n = xml->xml_kids.length;
6086 xml_normalize(JSContext *cx, uintN argc, jsval *vp)
6089 *vp = OBJECT_TO_JSVAL(obj);
6090 return xml_normalize_helper(cx, obj, xml);
6093 /* XML and XMLList */
6095 xml_parent(JSContext *cx, uintN argc, jsval *vp)
6097 JSXML *parent, *kid;
6099 JSObject *parentobj;
6102 parent = xml->parent;
6103 if (xml->xml_class == JSXML_CLASS_LIST) {
6105 n = xml->xml_kids.length;
6109 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6112 parent = kid->parent;
6113 for (i = 1; i < n; i++) {
6114 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6115 if (kid && kid->parent != parent)
6125 parentobj = js_GetXMLObject(cx, parent);
6128 *vp = OBJECT_TO_JSVAL(parentobj);
6132 /* XML and XMLList */
6134 xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6135 JSObject *nameqn, jsval *vp)
6143 list = xml_list_helper(cx, xml, vp);
6147 list->xml_targetprop = nameqn;
6150 if (xml->xml_class == JSXML_CLASS_LIST) {
6151 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6152 JSXMLArrayCursor cursor(&xml->xml_kids);
6153 while (JSXML *kid = (JSXML *) cursor.getNext()) {
6154 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6155 ok = js_EnterLocalRootScope(cx);
6158 kidobj = js_GetXMLObject(cx, kid);
6160 ok = xml_processingInstructions_helper(cx, kidobj, kid,
6166 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
6169 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6170 if (JSXML_LENGTH(vxml) != 0) {
6171 ok = Append(cx, list, vxml);
6178 /* 13.4.4.28 Step 4. */
6179 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6180 JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6181 if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
6182 JSLinearString *localName = nameqn->getQNameLocalName();
6183 if (IS_STAR(localName) ||
6184 EqualStrings(localName, kid->name->getQNameLocalName())) {
6185 ok = Append(cx, list, kid);
6197 xml_processingInstructions(JSContext *cx, uintN argc, jsval *vp)
6205 name = (argc == 0) ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
6206 nameqn = ToXMLName(cx, name, &funid);
6209 vp[2] = OBJECT_TO_JSVAL(nameqn);
6211 if (!JSID_IS_VOID(funid))
6212 return xml_list_helper(cx, xml, vp) != NULL;
6214 return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp);
6218 xml_prependChild(JSContext *cx, uintN argc, jsval *vp)
6220 NON_LIST_XML_METHOD_PROLOG;
6221 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6224 *vp = OBJECT_TO_JSVAL(obj);
6225 return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID);
6228 /* XML and XMLList */
6230 xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
6238 if (!js_IdValIsIndex(cx, vp[2], &index, &isIndex))
6242 if (xml->xml_class == JSXML_CLASS_LIST) {
6244 *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6247 *vp = BOOLEAN_TO_JSVAL(index == 0);
6255 namespace_full_match(const void *a, const void *b)
6257 const JSObject *nsa = (const JSObject *) a;
6258 const JSObject *nsb = (const JSObject *) b;
6259 JSLinearString *prefixa = nsa->getNamePrefix();
6260 JSLinearString *prefixb;
6263 prefixb = nsb->getNamePrefix();
6264 if (prefixb && !EqualStrings(prefixa, prefixb))
6267 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
6271 xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns)
6273 JSObject *thisns, *attrns;
6277 thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6282 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6283 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6286 attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
6292 i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
6293 if (i != XML_NOT_FOUND)
6294 XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
6296 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6297 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6298 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6299 if (!xml_removeNamespace_helper(cx, kid, ns))
6307 xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp)
6311 NON_LIST_XML_METHOD_PROLOG;
6312 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6314 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6318 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6320 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6321 ns = JSVAL_TO_OBJECT(*vp);
6323 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6324 if (!xml_removeNamespace_helper(cx, xml, ns))
6327 *vp = OBJECT_TO_JSVAL(obj);
6332 xml_replace(JSContext *cx, uintN argc, jsval *vp)
6339 NON_LIST_XML_METHOD_PROLOG;
6340 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6344 value = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6347 vxml = VALUE_IS_XML(value)
6348 ? (JSXML *) JSVAL_TO_OBJECT(value)->getPrivate()
6351 if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
6355 vxml = DeepCopy(cx, vxml, NULL, 0);
6358 value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
6362 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6370 if (!js_IdValIsIndex(cx, vp[2], &index, &haveIndex))
6376 * Call function QName per spec, not ToXMLName, to avoid attribute
6379 if (!QNameHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6381 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6382 nameqn = JSVAL_TO_OBJECT(*vp);
6384 i = xml->xml_kids.length;
6385 index = XML_NOT_FOUND;
6388 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6389 if (kid && MatchElemName(nameqn, kid)) {
6390 if (i != XML_NOT_FOUND)
6391 DeleteByIndex(cx, xml, i);
6396 if (index == XML_NOT_FOUND)
6400 if (!Replace(cx, xml, index, value))
6404 *vp = OBJECT_TO_JSVAL(obj);
6409 xml_setChildren(JSContext *cx, uintN argc, jsval *vp)
6413 if (!StartNonListXMLMethod(cx, vp, &obj))
6416 *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */
6417 if (!PutProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.starAtom), false, vp))
6420 *vp = OBJECT_TO_JSVAL(obj);
6425 xml_setLocalName(JSContext *cx, uintN argc, jsval *vp)
6429 JSLinearString *namestr;
6431 NON_LIST_XML_METHOD_PROLOG;
6432 if (!JSXML_HAS_NAME(xml))
6436 namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6439 if (!JSVAL_IS_PRIMITIVE(name) &&
6440 JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) {
6441 nameqn = JSVAL_TO_OBJECT(name);
6442 namestr = nameqn->getQNameLocalName();
6444 if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
6447 namestr = JSVAL_TO_STRING(name)->ensureLinear(cx);
6453 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6457 xml->name->setQNameLocalName(namestr);
6462 xml_setName(JSContext *cx, uintN argc, jsval *vp)
6467 JSXMLArray *nsarray;
6471 NON_LIST_XML_METHOD_PROLOG;
6472 if (!JSXML_HAS_NAME(xml))
6476 name = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6479 if (!JSVAL_IS_PRIMITIVE(name) &&
6480 JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass &&
6481 !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) {
6482 name = vp[2] = nameqn->getQNameLocalNameVal();
6486 nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&name));
6490 /* ECMA-357 13.4.4.35 Step 4. */
6491 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6492 nameqn->setNameURI(cx->runtime->emptyString);
6494 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6500 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6501 * in-scope namespaces, either by finding an in-scope namespace with a
6502 * matching uri and setting the new name's prefix to that namespace's
6503 * prefix, or by extending the in-scope namespaces for xml (which are in
6504 * xml->parent if xml is an attribute or a PI).
6506 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6509 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6511 nsowner = xml->parent;
6514 if (nameqn->getNamePrefix()) {
6516 * The name being set has a prefix, which originally came from some
6517 * namespace object (which may be the null namespace, where both the
6518 * prefix and uri are the empty string). We must go through a full
6519 * GetNamespace in case that namespace is in-scope in nsowner.
6521 * If we find such an in-scope namespace, we return true right away,
6522 * in this block. Otherwise, we fall through to the final return of
6523 * AddInScopeNamespace(cx, nsowner, ns).
6525 ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
6529 /* XXXbe have to test membership to see whether GetNamespace added */
6530 if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
6534 * At this point, we know prefix of nameqn is null, so its uri can't
6535 * be the empty string (the null namespace always uses the empty string
6536 * for both prefix and uri).
6538 * This means we must inline GetNamespace and specialize it to match
6539 * uri only, never prefix. If we find a namespace with nameqn's uri
6540 * already in nsowner->xml_namespaces, then all that we need do is set
6541 * prefix of nameqn to that namespace's prefix.
6543 * If no such namespace exists, we can create one without going through
6544 * the constructor, because we know uri of nameqn is non-empty (so
6545 * prefix does not need to be converted from null to empty by QName).
6547 JS_ASSERT(!nameqn->getNameURI()->empty());
6549 nsarray = &nsowner->xml_namespaces;
6550 for (i = 0, n = nsarray->length; i < n; i++) {
6551 ns = XMLARRAY_MEMBER(nsarray, i, JSObject);
6552 if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) {
6553 nameqn->setNamePrefix(ns->getNamePrefix());
6558 ns = NewXMLNamespace(cx, NULL, nameqn->getNameURI(), JS_TRUE);
6563 if (!AddInScopeNamespace(cx, nsowner, ns))
6570 xml_setNamespace(JSContext *cx, uintN argc, jsval *vp)
6577 NON_LIST_XML_METHOD_PROLOG;
6578 if (!JSXML_HAS_NAME(xml))
6581 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6585 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj,
6586 argc == 0 ? 0 : 1, Valueify(vp + 2));
6589 vp[0] = OBJECT_TO_JSVAL(ns);
6590 ns->setNamespaceDeclared(JSVAL_TRUE);
6592 qnargv[0] = OBJECT_TO_JSVAL(ns);
6593 qnargv[1] = OBJECT_TO_JSVAL(xml->name);
6594 qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv));
6601 * Erratum: the spec fails to update the governing in-scope namespaces.
6602 * See the erratum noted in xml_setName, above.
6604 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6607 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6609 nsowner = xml->parent;
6611 if (!AddInScopeNamespace(cx, nsowner, ns))
6617 /* XML and XMLList */
6619 xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
6621 JSXML *list, *kid, *vxml;
6627 list = xml_list_helper(cx, xml, vp);
6631 if (xml->xml_class == JSXML_CLASS_LIST) {
6633 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6634 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6635 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6636 ok = js_EnterLocalRootScope(cx);
6639 kidobj = js_GetXMLObject(cx, kid);
6641 ok = xml_text_helper(cx, kidobj, kid, &v);
6646 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
6649 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6650 if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
6655 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6656 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6657 if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
6658 if (!Append(cx, list, kid))
6667 xml_text(JSContext *cx, uintN argc, jsval *vp)
6670 return xml_text_helper(cx, obj, xml, vp);
6673 /* XML and XMLList */
6675 xml_toString_helper(JSContext *cx, JSXML *xml)
6677 JSString *str, *kidstr;
6679 if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
6680 xml->xml_class == JSXML_CLASS_TEXT) {
6681 return xml->xml_value;
6684 if (!HasSimpleContent(xml))
6685 return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0);
6687 str = cx->runtime->emptyString;
6688 if (!js_EnterLocalRootScope(cx))
6690 JSXMLArrayCursor cursor(&xml->xml_kids);
6691 while (JSXML *kid = (JSXML *) cursor.getNext()) {
6692 if (kid->xml_class != JSXML_CLASS_COMMENT &&
6693 kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
6694 kidstr = xml_toString_helper(cx, kid);
6699 str = js_ConcatStrings(cx, str, kidstr);
6704 js_LeaveLocalRootScopeWithResult(cx, str);
6709 xml_toSource(JSContext *cx, uintN argc, jsval *vp)
6711 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6714 JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), TO_SOURCE_FLAG);
6717 *vp = STRING_TO_JSVAL(str);
6722 xml_toString(JSContext *cx, uintN argc, jsval *vp)
6727 str = xml_toString_helper(cx, xml);
6730 *vp = STRING_TO_JSVAL(str);
6734 /* XML and XMLList */
6736 xml_toXMLString(JSContext *cx, uintN argc, jsval *vp)
6738 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6741 JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), 0);
6744 *vp = STRING_TO_JSVAL(str);
6748 /* XML and XMLList */
6750 xml_valueOf(JSContext *cx, uintN argc, jsval *vp)
6752 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6755 *vp = OBJECT_TO_JSVAL(obj);
6759 static JSFunctionSpec xml_methods[] = {
6760 JS_FN("addNamespace", xml_addNamespace, 1,0),
6761 JS_FN("appendChild", xml_appendChild, 1,0),
6762 JS_FN(js_attribute_str, xml_attribute, 1,0),
6763 JS_FN("attributes", xml_attributes, 0,0),
6764 JS_FN("child", xml_child, 1,0),
6765 JS_FN("childIndex", xml_childIndex, 0,0),
6766 JS_FN("children", xml_children, 0,0),
6767 JS_FN("comments", xml_comments, 0,0),
6768 JS_FN("contains", xml_contains, 1,0),
6769 JS_FN("copy", xml_copy, 0,0),
6770 JS_FN("descendants", xml_descendants, 1,0),
6771 JS_FN("elements", xml_elements, 1,0),
6772 JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0),
6773 JS_FN("hasComplexContent", xml_hasComplexContent, 1,0),
6774 JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0),
6775 JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0),
6776 JS_FN("insertChildAfter", xml_insertChildAfter, 2,0),
6777 JS_FN("insertChildBefore", xml_insertChildBefore, 2,0),
6778 JS_FN(js_length_str, xml_length, 0,0),
6779 JS_FN(js_localName_str, xml_localName, 0,0),
6780 JS_FN(js_name_str, xml_name, 0,0),
6781 JS_FN(js_namespace_str, xml_namespace, 1,0),
6782 JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0),
6783 JS_FN("nodeKind", xml_nodeKind, 0,0),
6784 JS_FN("normalize", xml_normalize, 0,0),
6785 JS_FN(js_xml_parent_str, xml_parent, 0,0),
6786 JS_FN("processingInstructions",xml_processingInstructions,1,0),
6787 JS_FN("prependChild", xml_prependChild, 1,0),
6788 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0),
6789 JS_FN("removeNamespace", xml_removeNamespace, 1,0),
6790 JS_FN("replace", xml_replace, 2,0),
6791 JS_FN("setChildren", xml_setChildren, 1,0),
6792 JS_FN("setLocalName", xml_setLocalName, 1,0),
6793 JS_FN("setName", xml_setName, 1,0),
6794 JS_FN("setNamespace", xml_setNamespace, 1,0),
6795 JS_FN(js_text_str, xml_text, 0,0),
6796 JS_FN(js_toSource_str, xml_toSource, 0,0),
6797 JS_FN(js_toString_str, xml_toString, 0,0),
6798 JS_FN(js_toXMLString_str, xml_toXMLString, 0,0),
6799 JS_FN(js_valueOf_str, xml_valueOf, 0,0),
6804 CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
6810 /* Note: PRETTY_INDENT is not a boolean setting. */
6811 for (i = 0; xml_static_props[i].name; i++) {
6812 name = xml_static_props[i].name;
6813 if (!JS_GetProperty(cx, from, name, &v))
6815 if (name == js_prettyIndent_str) {
6816 if (!JSVAL_IS_NUMBER(v))
6819 if (!JSVAL_IS_BOOLEAN(v))
6822 if (!JS_SetProperty(cx, to, name, &v))
6830 SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
6835 /* Note: PRETTY_INDENT is not a boolean setting. */
6836 for (i = 0; xml_static_props[i].name; i++) {
6837 v = (xml_static_props[i].name != js_prettyIndent_str)
6838 ? JSVAL_TRUE : INT_TO_JSVAL(2);
6839 if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
6846 xml_settings(JSContext *cx, uintN argc, jsval *vp)
6848 JSObject *settings = JS_NewObject(cx, NULL, NULL, NULL);
6851 *vp = OBJECT_TO_JSVAL(settings);
6852 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6855 return CopyXMLSettings(cx, obj, settings);
6859 xml_setSettings(JSContext *cx, uintN argc, jsval *vp)
6865 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6868 v = (argc == 0) ? JSVAL_VOID : vp[2];
6869 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
6870 ok = SetDefaultXMLSettings(cx, obj);
6872 if (JSVAL_IS_PRIMITIVE(v))
6874 settings = JSVAL_TO_OBJECT(v);
6875 ok = CopyXMLSettings(cx, settings, obj);
6881 xml_defaultSettings(JSContext *cx, uintN argc, jsval *vp)
6885 settings = JS_NewObject(cx, NULL, NULL, NULL);
6888 *vp = OBJECT_TO_JSVAL(settings);
6889 return SetDefaultXMLSettings(cx, settings);
6892 static JSFunctionSpec xml_static_methods[] = {
6893 JS_FN("settings", xml_settings, 0,0),
6894 JS_FN("setSettings", xml_setSettings, 1,0),
6895 JS_FN("defaultSettings", xml_defaultSettings, 0,0),
6900 XML(JSContext *cx, uintN argc, Value *vp)
6903 JSObject *xobj, *vobj;
6906 jsval v = argc ? Jsvalify(vp[2]) : JSVAL_VOID;
6908 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
6909 v = STRING_TO_JSVAL(cx->runtime->emptyString);
6911 xobj = ToXML(cx, v);
6914 xml = (JSXML *) xobj->getPrivate();
6916 if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) {
6917 vobj = JSVAL_TO_OBJECT(v);
6918 clasp = vobj->getClass();
6919 if (clasp == &js_XMLClass ||
6920 (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
6921 copy = DeepCopy(cx, xml, NULL, 0);
6924 vp->setObject(*copy->object);
6929 vp->setObject(*xobj);
6934 XMLList(JSContext *cx, uintN argc, jsval *vp)
6936 JSObject *vobj, *listobj;
6939 jsval v = argc ? vp[2] : JSVAL_VOID;
6941 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
6942 v = STRING_TO_JSVAL(cx->runtime->emptyString);
6944 if (IsConstructing(Valueify(vp)) && !JSVAL_IS_PRIMITIVE(v)) {
6945 vobj = JSVAL_TO_OBJECT(v);
6946 if (vobj->isXML()) {
6947 xml = (JSXML *) vobj->getPrivate();
6948 if (xml->xml_class == JSXML_CLASS_LIST) {
6949 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
6952 *vp = OBJECT_TO_JSVAL(listobj);
6954 list = (JSXML *) listobj->getPrivate();
6955 if (!Append(cx, list, xml))
6962 /* Toggle on XML support since the script has explicitly requested it. */
6963 listobj = ToXMLList(cx, v);
6967 *vp = OBJECT_TO_JSVAL(listobj);
6972 JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
6977 js_NewXML(JSContext *cx, JSXMLClass xml_class)
6979 JSXML *xml = js_NewGCXML(cx);
6984 xml->domnode = NULL;
6987 xml->xml_class = xml_class;
6989 if (JSXML_CLASS_HAS_VALUE(xml_class)) {
6990 xml->xml_value = cx->runtime->emptyString;
6992 xml->xml_kids.init();
6993 if (xml_class == JSXML_CLASS_LIST) {
6994 xml->xml_target = NULL;
6995 xml->xml_targetprop = NULL;
6997 xml->xml_namespaces.init();
6998 xml->xml_attrs.init();
7003 JS_APPEND_LINK(&xml->links, &xml_leaks);
7004 xml->serial = xml_serial++;
7006 METER(xml_stats.xml);
7011 js_TraceXML(JSTracer *trc, JSXML *xml)
7014 MarkObject(trc, *xml->object, "object");
7016 MarkObject(trc, *xml->name, "name");
7018 JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent");
7020 if (JSXML_HAS_VALUE(xml)) {
7022 MarkString(trc, xml->xml_value, "value");
7026 xml_trace_vector(trc,
7027 (JSXML **) xml->xml_kids.vector,
7028 xml->xml_kids.length);
7029 XMLArrayCursorTrace(trc, xml->xml_kids.cursors);
7030 if (IS_GC_MARKING_TRACER(trc))
7031 xml->xml_kids.trim();
7033 if (xml->xml_class == JSXML_CLASS_LIST) {
7034 if (xml->xml_target)
7035 JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target");
7036 if (xml->xml_targetprop)
7037 MarkObject(trc, *xml->xml_targetprop, "targetprop");
7039 MarkObjectRange(trc, xml->xml_namespaces.length,
7040 (JSObject **) xml->xml_namespaces.vector,
7042 XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
7043 if (IS_GC_MARKING_TRACER(trc))
7044 xml->xml_namespaces.trim();
7046 xml_trace_vector(trc,
7047 (JSXML **) xml->xml_attrs.vector,
7048 xml->xml_attrs.length);
7049 XMLArrayCursorTrace(trc, xml->xml_attrs.cursors);
7050 if (IS_GC_MARKING_TRACER(trc))
7051 xml->xml_attrs.trim();
7056 js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7058 JSXML *xml = js_NewXML(cx, xml_class);
7062 AutoXMLRooter root(cx, xml);
7063 return js_GetXMLObject(cx, xml);
7067 NewXMLObject(JSContext *cx, JSXML *xml)
7071 obj = NewNonFunction<WithProto::Class>(cx, &js_XMLClass, NULL, NULL);
7074 obj->setPrivate(xml);
7075 METER(xml_stats.xmlobj);
7080 js_GetXMLObject(JSContext *cx, JSXML *xml)
7086 JS_ASSERT(obj->getPrivate() == xml);
7090 obj = NewXMLObject(cx, xml);
7098 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7100 return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2,
7101 namespace_props, namespace_methods, NULL, NULL);
7105 js_InitQNameClass(JSContext *cx, JSObject *obj)
7107 return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
7108 qname_props, qname_methods, NULL, NULL);
7112 js_InitXMLClass(JSContext *cx, JSObject *obj)
7114 JSObject *proto, *pobj;
7121 /* Define the isXMLName function. */
7122 if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7125 /* Define the XML class constructor and prototype. */
7126 proto = js_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
7128 xml_static_props, xml_static_methods);
7132 xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7135 proto->setPrivate(xml);
7136 xml->object = proto;
7137 METER(xml_stats.xmlobj);
7140 * Prepare to set default settings on the XML constructor we just made.
7141 * NB: We can't use JS_GetConstructor, because it calls
7142 * JSObject::getProperty, which is xml_getProperty, which creates a new
7143 * XMLList every time! We must instead call js_LookupProperty directly.
7145 if (!js_LookupProperty(cx, proto,
7146 ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
7151 shape = (Shape *) prop;
7152 cval = Jsvalify(pobj->nativeGetSlot(shape->slot));
7153 JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
7155 /* Set default settings. */
7159 if (!xml_setSettings(cx, 1, vp))
7162 /* Define the XMLList function and give it the same prototype as XML. */
7163 fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR);
7166 if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
7167 JSPROP_READONLY | JSPROP_PERMANENT)) {
7174 js_InitXMLClasses(JSContext *cx, JSObject *obj)
7176 if (!js_InitNamespaceClass(cx, obj))
7178 if (!js_InitQNameClass(cx, obj))
7180 return js_InitXMLClass(cx, obj);
7184 js_GetFunctionNamespace(JSContext *cx, Value *vp)
7186 JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
7188 *vp = global->getReservedSlot(JSRESERVED_GLOBAL_FUNCTION_NS);
7189 if (vp->isUndefined()) {
7190 JSRuntime *rt = cx->runtime;
7191 JSLinearString *prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION];
7192 JSLinearString *uri = rt->atomState.functionNamespaceURIAtom;
7193 JSObject *obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE);
7198 * Avoid entraining any in-scope Object.prototype. The loss of
7199 * Namespace.prototype is not detectable, as there is no way to
7200 * refer to this instance in scripts. When used to qualify method
7201 * names, its prefix and uri references are copied to the QName.
7202 * The parent remains set and links back to global.
7206 vp->setObject(*obj);
7207 if (!js_SetReservedSlot(cx, global, JSRESERVED_GLOBAL_FUNCTION_NS, *vp))
7215 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7216 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7217 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
7218 * requirement that fp->varobj lie directly on fp->scopeChain, although
7219 * it should be reachable using the prototype chain from a scope object (cf.
7220 * JSOPTION_VAROBJFIX in jsapi.h).
7222 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7223 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7224 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7225 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7226 * the default XML namespace will be set to ("", n.uri). So the uri string
7227 * is really the only usefully stored value of the default namespace.
7230 js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7232 JSObject *ns, *obj, *tmp;
7235 JSObject *scopeChain = GetScopeChain(cx);
7238 for (tmp = scopeChain; tmp; tmp = tmp->getParent()) {
7239 Class *clasp = tmp->getClass();
7240 if (clasp == &js_BlockClass || clasp == &js_WithClass)
7242 if (!tmp->getProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(&v)))
7244 if (!JSVAL_IS_PRIMITIVE(v)) {
7251 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, 0, NULL);
7254 v = OBJECT_TO_JSVAL(ns);
7255 if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(v),
7256 PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) {
7264 js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
7267 argv[0].setString(cx->runtime->emptyString);
7269 JSObject *ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, 2, argv);
7273 JSStackFrame *fp = js_GetTopStackFrame(cx);
7274 JSObject &varobj = fp->varobj(cx);
7275 if (!varobj.defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns),
7276 PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) {
7283 js_ToAttributeName(JSContext *cx, Value *vp)
7287 qn = ToAttributeName(cx, Jsvalify(*vp));
7295 js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote)
7297 StringBuffer sb(cx);
7298 return EscapeAttributeValue(cx, sb, str, quote);
7302 js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7304 size_t len = str->length();
7305 const jschar *chars = str->getChars(cx);
7309 size_t len2 = str2->length();
7310 const jschar *chars2 = str2->getChars(cx);
7314 size_t newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
7315 jschar *newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar));
7319 js_strncpy(newchars, chars, len);
7323 js_strncpy(newchars, chars2, len2);
7328 js_strncpy(newchars, chars2, len2);
7333 return js_NewString(cx, newchars - newlen, newlen);
7337 js_EscapeElementValue(JSContext *cx, JSString *str)
7339 StringBuffer sb(cx);
7340 return EscapeElementValue(cx, sb, str, 0);
7344 js_ValueToXMLString(JSContext *cx, const Value &v)
7346 return ToXMLString(cx, Jsvalify(v), 0);
7350 js_GetAnyName(JSContext *cx, jsid *idp)
7352 JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
7353 Value v = global->getReservedSlot(JSProto_AnyName);
7354 if (v.isUndefined()) {
7355 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AnyNameClass, NULL, global);
7359 JS_ASSERT(!obj->getProto());
7361 JSRuntime *rt = cx->runtime;
7362 InitXMLQName(obj, rt->emptyString, rt->emptyString,
7363 ATOM_TO_STRING(rt->atomState.starAtom));
7364 METER(xml_stats.qname);
7367 if (!js_SetReservedSlot(cx, global, JSProto_AnyName, v))
7370 *idp = OBJECT_TO_JSID(&v.toObject());
7375 js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *idp)
7381 JSObject *obj, *target, *proto, *pobj;
7386 JS_ASSERT(nameval.isObject());
7387 nameobj = &nameval.toObject();
7388 if (nameobj->getClass() == &js_AnyNameClass) {
7389 v = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
7390 nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1,
7395 JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
7396 nameobj->getClass() == &js_QNameClass);
7400 if (!IsFunctionQName(cx, qn, &funid))
7403 obj = &js_GetTopStackFrame(cx)->scopeChain();
7405 /* Skip any With object that can wrap XML. */
7407 while (target->getClass() == &js_WithClass) {
7408 proto = target->getProto();
7414 if (target->isXML()) {
7415 if (JSID_IS_VOID(funid)) {
7416 xml = (JSXML *) target->getPrivate();
7417 found = HasNamedProperty(xml, qn);
7419 if (!HasFunctionProperty(cx, target, funid, &found))
7423 *idp = OBJECT_TO_JSID(nameobj);
7427 } else if (!JSID_IS_VOID(funid)) {
7428 if (!target->lookupProperty(cx, funid, &pobj, &prop))
7436 } while ((obj = obj->getParent()) != NULL);
7438 JSAutoByteString printable;
7439 JSString *str = ConvertQNameToString(cx, nameobj);
7440 if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
7441 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
7442 JSMSG_UNDEFINED_XML_NAME, printable.ptr());
7448 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
7450 JS_ASSERT(obj->isXML());
7453 * See comments before xml_lookupProperty about the need for the proto
7456 JSObject *target = obj;
7457 AutoObjectRooter tvr(cx);
7459 if (!js_GetProperty(cx, target, id, Valueify(vp)))
7461 if (VALUE_IS_FUNCTION(cx, *vp))
7463 target = target->getProto();
7464 if (target == NULL || !target->isNative())
7466 tvr.setObject(target);
7469 JSXML *xml = (JSXML *) obj->getPrivate();
7470 if (!HasSimpleContent(xml))
7473 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
7474 if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
7477 JS_ASSERT(tvr.object());
7478 return tvr.object()->getProperty(cx, id, Valueify(vp));
7482 GetPrivate(JSContext *cx, JSObject *obj, const char *method)
7486 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
7488 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7489 JSMSG_INCOMPATIBLE_METHOD,
7490 js_XML_str, method, obj->getClass()->name);
7496 js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
7500 xml = GetPrivate(cx, obj, "descendants internal method");
7504 list = Descendants(cx, xml, id);
7507 *vp = OBJECT_TO_JSVAL(list->object);
7512 js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
7517 list = (JSXML *) listobj->getPrivate();
7518 for (n = list->xml_kids.length; n != 0; --n)
7519 DeleteListElement(cx, list, 0);
7529 JSXMLArrayCursor cursor;
7531 JSXMLFilter(JSXML *list, JSXMLArray *array)
7532 : list(list), result(NULL), kid(NULL), cursor(array) {}
7538 xmlfilter_trace(JSTracer *trc, JSObject *obj)
7540 JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7544 JS_ASSERT(filter->list);
7545 JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list");
7547 JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result");
7549 JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid");
7552 * We do not need to trace the cursor as that would be done when
7553 * tracing the filter->list.
7558 xmlfilter_finalize(JSContext *cx, JSObject *obj)
7560 JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7564 cx->destroy(filter);
7567 Class js_XMLFilterClass = {
7569 JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
7570 PropertyStub, /* addProperty */
7571 PropertyStub, /* delProperty */
7572 PropertyStub, /* getProperty */
7573 StrictPropertyStub, /* setProperty */
7578 NULL, /* reserved0 */
7579 NULL, /* checkAccess */
7581 NULL, /* construct */
7582 NULL, /* xdrObject */
7583 NULL, /* hasInstance */
7584 JS_CLASS_TRACE(xmlfilter_trace)
7588 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
7591 JSObject *obj, *filterobj, *resobj, *kidobj;
7593 JSXMLFilter *filter;
7596 sp = Jsvalify(cx->regs->sp);
7599 * We haven't iterated yet, so initialize the filter based on the
7600 * value stored in sp[-2].
7602 if (!VALUE_IS_XML(sp[-2])) {
7603 js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, Valueify(sp[-2]), NULL);
7606 obj = JSVAL_TO_OBJECT(sp[-2]);
7607 xml = (JSXML *) obj->getPrivate();
7609 if (xml->xml_class == JSXML_CLASS_LIST) {
7612 obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7617 * Root just-created obj. sp[-2] cannot be used yet for rooting
7618 * as it may be the only root holding xml.
7620 sp[-1] = OBJECT_TO_JSVAL(obj);
7621 list = (JSXML *) obj->getPrivate();
7622 if (!Append(cx, list, xml))
7626 filterobj = NewNonFunction<WithProto::Given>(cx, &js_XMLFilterClass, NULL, NULL);
7631 * Init all filter fields before setPrivate exposes it to
7632 * xmlfilter_trace or xmlfilter_finalize.
7634 filter = cx->create<JSXMLFilter>(list, &list->xml_kids);
7637 filterobj->setPrivate(filter);
7639 /* Store filterobj to use in the later iterations. */
7640 sp[-2] = OBJECT_TO_JSVAL(filterobj);
7642 resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7646 /* This also roots resobj. */
7647 filter->result = (JSXML *) resobj->getPrivate();
7649 /* We have iterated at least once. */
7650 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
7651 JS_ASSERT(JSVAL_TO_OBJECT(sp[-2])->getClass() == &js_XMLFilterClass);
7652 filter = (JSXMLFilter *) JSVAL_TO_OBJECT(sp[-2])->getPrivate();
7653 JS_ASSERT(filter->kid);
7655 /* Check if the filter expression wants to append the element. */
7656 if (js_ValueToBoolean(Valueify(sp[-1])) &&
7657 !Append(cx, filter->result, filter->kid)) {
7662 /* Do the iteration. */
7663 filter->kid = (JSXML *) filter->cursor.getNext();
7666 * Do not defer finishing the cursor until the next GC cycle to avoid
7667 * accumulation of dead cursors associated with filter->list.
7669 filter->cursor.disconnect();
7670 JS_ASSERT(filter->result->object);
7671 sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
7674 kidobj = js_GetXMLObject(cx, filter->kid);
7679 /* Null as kidobj at sp[-1] signals filter termination. */
7680 sp[-1] = OBJECT_TO_JSVAL(kidobj);
7685 js_ValueToXMLObject(JSContext *cx, const Value &v)
7687 return ToXML(cx, Jsvalify(v));
7691 js_ValueToXMLListObject(JSContext *cx, const Value &v)
7693 return ToXMLList(cx, Jsvalify(v));
7697 js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
7705 if (!GetXMLSettingFlags(cx, &flags))
7708 if ((xml_class == JSXML_CLASS_COMMENT &&
7709 (flags & XSF_IGNORE_COMMENTS)) ||
7710 (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
7711 (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
7712 return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
7715 obj = js_NewXMLObject(cx, xml_class);
7718 xml = (JSXML *) obj->getPrivate();
7720 JSLinearString *linearName = name->ensureLinear(cx);
7723 qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName);
7728 xml->xml_value = value;
7733 js_MakeXMLCDATAString(JSContext *cx, JSString *str)
7735 StringBuffer sb(cx);
7736 return MakeXMLCDATAString(cx, sb, str);
7740 js_MakeXMLCommentString(JSContext *cx, JSString *str)
7742 StringBuffer sb(cx);
7743 return MakeXMLCommentString(cx, sb, str);
7747 js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
7749 StringBuffer sb(cx);
7750 return MakeXMLPIString(cx, sb, name, str);
7753 #endif /* JS_HAS_XML_SUPPORT */