Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jsxml.cpp
blobb432efec3c246040e2d1af35ae1525b0b1044e38
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
15 * License.
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.
24 * Contributor(s):
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
44 #include <math.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsstdint.h"
49 #include "jsbit.h"
50 #include "jsprf.h"
51 #include "jsutil.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsfun.h"
58 #include "jsgc.h"
59 #include "jsinterp.h"
60 #include "jslock.h"
61 #include "jsnum.h"
62 #include "jsobj.h"
63 #include "jsopcode.h"
64 #include "jsparse.h"
65 #include "jsscan.h"
66 #include "jsscope.h"
67 #include "jsscript.h"
68 #include "jsstr.h"
69 #include "jsxml.h"
70 #include "jsstaticcheck.h"
71 #include "jsvector.h"
73 #include "jsatominlines.h"
74 #include "jscntxtinlines.h"
75 #include "jsinterpinlines.h"
76 #include "jsobjinlines.h"
77 #include "jsstrinlines.h"
79 #ifdef DEBUG
80 #include <string.h> /* for #ifdef DEBUG memset calls */
81 #endif
83 using namespace js;
84 using namespace js::gc;
87 * NOTES
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
91 * TODO
92 * - XXXbe patrol
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!
98 static inline bool
99 js_EnterLocalRootScope(JSContext *cx)
101 return true;
104 static inline void
105 js_LeaveLocalRootScope(JSContext *cx)
109 static inline void
110 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
114 static inline void
115 js_LeaveLocalRootScopeWithResult(JSContext *cx, Value rval)
119 static inline void
120 js_LeaveLocalRootScopeWithResult(JSContext *cx, void *rval)
124 #ifdef XML_METERING
125 static struct {
126 jsrefcount qname;
127 jsrefcount xmlnamespace;
128 jsrefcount xml;
129 jsrefcount xmlobj;
130 } xml_stats;
132 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
133 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
134 #else
135 #define METER(x) /* nothing */
136 #define UNMETER(x) /* nothing */
137 #endif
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[] = "&amp;";
152 const char js_gt_entity_str[] = "&gt;";
153 const char js_lt_entity_str[] = "&lt;";
154 const char js_quot_entity_str[] = "&quot;";
155 const char js_leftcurly_entity_str[] = "&#123;";
157 #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*')
159 static JSBool
160 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
162 static JSBool
163 IsDeclared(const JSObject *obj)
165 jsval v;
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;
173 static JSBool
174 xml_isXMLName(JSContext *cx, uintN argc, jsval *vp)
176 *vp = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argc ? vp[2] : JSVAL_VOID));
177 return JS_TRUE;
181 * This wrapper is needed because NewBuiltinClassInstance doesn't
182 * call the constructor, and we need a place to set the
183 * HAS_EQUALITY bit.
185 static inline JSObject *
186 NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
188 JSObject *obj = NewBuiltinClassInstance(cx, clasp);
189 if (obj)
190 obj->syncSpecialEquality();
191 return obj;
194 #define DEFINE_GETTER(name,code) \
195 static JSBool \
196 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
198 code; \
199 return true; \
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())
210 static JSBool
211 namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
213 JSObject *obj2;
215 JS_ASSERT(v->isObjectOrNull());
216 obj2 = v->toObjectOrNull();
217 *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass)
218 ? JS_FALSE
219 : EqualStrings(obj->getNameURI(), obj2->getNameURI());
220 return JS_TRUE;
223 JS_FRIEND_DATA(Class) js_NamespaceClass = {
224 "Namespace",
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 */
232 EnumerateStub,
233 ResolveStub,
234 ConvertStub,
235 FinalizeStub,
236 NULL, /* reserved0 */
237 NULL, /* checkAccess */
238 NULL, /* call */
239 NULL, /* construct */
240 NULL, /* xdrObject */
241 NULL, /* hasInstance */
242 NULL, /* mark */
244 namespace_equality,
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},
258 {0,0,0,0,0}
261 static JSBool
262 namespace_toString(JSContext *cx, uintN argc, Value *vp)
264 JSObject *obj = ToObject(cx, &vp[1]);
265 if (!obj)
266 return JS_FALSE;
267 if (!JS_InstanceOf(cx, obj, Jsvalify(&js_NamespaceClass), Jsvalify(vp + 2)))
268 return JS_FALSE;
269 *vp = Valueify(obj->getNameURIVal());
270 return JS_TRUE;
273 static JSFunctionSpec namespace_methods[] = {
274 JS_FN(js_toString_str, namespace_toString, 0,0),
275 JS_FS_END
278 static JSObject *
279 NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared)
281 JSObject *obj;
283 obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
284 if (!obj)
285 return JS_FALSE;
286 JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
287 JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
288 JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
289 if (prefix)
290 obj->setNamePrefix(prefix);
291 if (uri)
292 obj->setNameURI(uri);
293 if (declared)
294 obj->setNamespaceDeclared(JSVAL_TRUE);
295 METER(xml_stats.xmlnamespace);
296 return obj;
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())
309 static JSBool
310 qname_identity(JSObject *qna, JSObject *qnb)
312 JSLinearString *uri1 = qna->getNameURI();
313 JSLinearString *uri2 = qnb->getNameURI();
315 if (!uri1 ^ !uri2)
316 return JS_FALSE;
317 if (uri1 && !EqualStrings(uri1, uri2))
318 return JS_FALSE;
319 return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName());
322 static JSBool
323 qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp)
325 JSObject *obj2;
327 obj2 = v->toObjectOrNull();
328 *bp = (!obj2 || obj2->getClass() != &js_QNameClass)
329 ? JS_FALSE
330 : qname_identity(qn, obj2);
331 return JS_TRUE;
334 JS_FRIEND_DATA(Class) js_QNameClass = {
335 "QName",
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 */
343 EnumerateStub,
344 ResolveStub,
345 ConvertStub,
346 FinalizeStub,
347 NULL, /* reserved0 */
348 NULL, /* checkAccess */
349 NULL, /* call */
350 NULL, /* construct */
351 NULL, /* xdrObject */
352 NULL, /* hasInstance */
353 NULL, /* mark */
355 qname_equality,
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 */
378 EnumerateStub,
379 ResolveStub,
380 ConvertStub,
381 FinalizeStub
384 JS_FRIEND_DATA(Class) js_AnyNameClass = {
385 js_AnyName_str,
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 */
393 EnumerateStub,
394 ResolveStub,
395 ConvertStub,
396 FinalizeStub
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},
404 {0,0,0,0,0}
407 static JSString *
408 ConvertQNameToString(JSContext *cx, JSObject *obj)
410 JS_ASSERT(obj->isQName());
411 JSString *uri = obj->getNameURI();
412 JSString *str;
413 if (!uri) {
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;
419 } else {
420 JSString *qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
421 str = js_ConcatStrings(cx, uri, qualstr);
422 if (!str)
423 return NULL;
425 str = js_ConcatStrings(cx, str, obj->getQNameLocalName());
426 if (!str)
427 return NULL;
429 if (obj->getClass() == &js_AttributeNameClass) {
430 size_t length = str->length();
431 jschar *chars = (jschar *) cx->malloc((length + 2) * sizeof(jschar));
432 if (!chars)
433 return JS_FALSE;
434 *chars = '@';
435 const jschar *strChars = str->getChars(cx);
436 if (!strChars) {
437 cx->free(chars);
438 return NULL;
440 js_strncpy(chars + 1, strChars, length);
441 chars[++length] = 0;
442 str = js_NewString(cx, chars, length);
443 if (!str) {
444 cx->free(chars);
445 return NULL;
448 return str;
451 static JSBool
452 qname_toString(JSContext *cx, uintN argc, Value *vp)
454 JSObject *obj = ToObject(cx, &vp[1]);
455 if (!obj)
456 return false;
458 if (!InstanceOf(cx, obj, &js_QNameClass, vp + 2))
459 return false;
461 JSString *str = ConvertQNameToString(cx, obj);
462 if (!str)
463 return false;
465 vp->setString(str);
466 return true;
469 static JSFunctionSpec qname_methods[] = {
470 JS_FN(js_toString_str, qname_toString, 0,0),
471 JS_FS_END
475 static void
476 InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
477 JSLinearString *localName)
479 JS_ASSERT(obj->isQName());
480 JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
481 JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
482 JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
483 if (uri)
484 obj->setNameURI(uri);
485 if (prefix)
486 obj->setNamePrefix(prefix);
487 if (localName)
488 obj->setQNameLocalName(localName);
491 static JSObject *
492 NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
493 JSLinearString *localName)
495 JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
496 if (!obj)
497 return NULL;
498 InitXMLQName(obj, uri, prefix, localName);
499 METER(xml_stats.qname);
500 return obj;
503 static JSObject *
504 NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
505 JSLinearString *localName)
508 * AttributeName is an internal anonymous class which instances are not
509 * exposed to scripts.
511 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AttributeNameClass, NULL, NULL);
512 if (!obj)
513 return NULL;
514 JS_ASSERT(obj->isQName());
515 InitXMLQName(obj, uri, prefix, localName);
516 METER(xml_stats.qname);
517 return obj;
520 JSObject *
521 js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval)
523 Value argv[2];
526 * ECMA-357 11.1.2,
527 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
528 * production, step 2.
530 if (nsval.isObject() &&
531 nsval.toObject().getClass() == &js_AnyNameClass) {
532 argv[0].setNull();
533 } else {
534 argv[0] = nsval;
536 argv[1] = lnval;
537 return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, argv);
540 static JSBool
541 IsXMLName(const jschar *cp, size_t n)
543 JSBool rv;
544 jschar c;
546 rv = JS_FALSE;
547 if (n != 0 && JS_ISXMLNSSTART(*cp)) {
548 while (--n != 0) {
549 c = *++cp;
550 if (!JS_ISXMLNS(c))
551 return rv;
553 rv = JS_TRUE;
555 return rv;
558 JSBool
559 js_IsXMLName(JSContext *cx, jsval v)
561 JSLinearString *name = NULL;
562 JSErrorReporter older;
565 * Inline specialization of the QName constructor called with v passed as
566 * the only argument, to compute the localName for the constructed qname,
567 * without actually allocating the object or computing its uri and prefix.
568 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
570 if (!JSVAL_IS_PRIMITIVE(v) &&
571 JSVAL_TO_OBJECT(v)->isQName()) {
572 name = JSVAL_TO_OBJECT(v)->getQNameLocalName();
573 } else {
574 older = JS_SetErrorReporter(cx, NULL);
575 JSString *str = js_ValueToString(cx, Valueify(v));
576 if (str)
577 name = str->ensureLinear(cx);
578 JS_SetErrorReporter(cx, older);
579 if (!name) {
580 JS_ClearPendingException(cx);
581 return JS_FALSE;
585 return IsXMLName(name->chars(), name->length());
589 * When argc is -1, it indicates argv is empty but the code should behave as
590 * if argc is 1 and argv[0] is JSVAL_VOID.
592 static JSBool
593 NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
594 jsval *rval)
596 jsval urival, prefixval;
597 JSObject *uriobj;
598 JSBool isNamespace, isQName;
599 Class *clasp;
600 JSLinearString *empty, *prefix, *uri;
602 isNamespace = isQName = JS_FALSE;
603 #ifdef __GNUC__ /* suppress bogus gcc warnings */
604 uriobj = NULL;
605 #endif
606 if (argc <= 0) {
607 urival = JSVAL_VOID;
608 } else {
609 urival = argv[argc > 1];
610 if (!JSVAL_IS_PRIMITIVE(urival)) {
611 uriobj = JSVAL_TO_OBJECT(urival);
612 clasp = uriobj->getClass();
613 isNamespace = (clasp == &js_NamespaceClass);
614 isQName = (clasp == &js_QNameClass);
618 if (!obj) {
619 /* Namespace called as function. */
620 if (argc == 1 && isNamespace) {
621 /* Namespace called with one Namespace argument is identity. */
622 *rval = urival;
623 return JS_TRUE;
626 obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
627 if (!obj)
628 return JS_FALSE;
630 *rval = OBJECT_TO_JSVAL(obj);
631 METER(xml_stats.xmlnamespace);
633 empty = cx->runtime->emptyString;
634 obj->setNamePrefix(empty);
635 obj->setNameURI(empty);
637 if (argc == 1 || argc == -1) {
638 if (isNamespace) {
639 obj->setNameURI(uriobj->getNameURI());
640 obj->setNamePrefix(uriobj->getNamePrefix());
641 } else if (isQName && (uri = uriobj->getNameURI())) {
642 obj->setNameURI(uri);
643 obj->setNamePrefix(uriobj->getNamePrefix());
644 } else {
645 JSString *str = js_ValueToString(cx, Valueify(urival));
646 if (!str)
647 return JS_FALSE;
648 uri = str->ensureLinear(cx);
649 if (!uri)
650 return JS_FALSE;
651 obj->setNameURI(uri);
652 if (!uri->empty())
653 obj->clearNamePrefix();
655 } else if (argc == 2) {
656 if (!isQName || !(uri = uriobj->getNameURI())) {
657 JSString *str = js_ValueToString(cx, Valueify(urival));
658 if (!str)
659 return JS_FALSE;
660 uri = str->ensureLinear(cx);
661 if (!uri)
662 return JS_FALSE;
664 obj->setNameURI(uri);
666 prefixval = argv[0];
667 if (uri->empty()) {
668 if (!JSVAL_IS_VOID(prefixval)) {
669 JSString *str = js_ValueToString(cx, Valueify(prefixval));
670 if (!str)
671 return JS_FALSE;
672 if (!str->empty()) {
673 JSAutoByteString bytes;
674 if (js_ValueToPrintable(cx, StringValue(str), &bytes)) {
675 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
676 JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
678 return JS_FALSE;
681 } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
682 obj->clearNamePrefix();
683 } else {
684 JSString *str = js_ValueToString(cx, Valueify(prefixval));
685 if (!str)
686 return JS_FALSE;
687 prefix = str->ensureLinear(cx);
688 if (!prefix)
689 return JS_FALSE;
690 obj->setNamePrefix(prefix);
693 return JS_TRUE;
696 static JSBool
697 Namespace(JSContext *cx, uintN argc, Value *vp)
699 JSObject *thisobj = NULL;
700 (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
701 return NamespaceHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
705 * When argc is -1, it indicates argv is empty but the code should behave as
706 * if argc is 1 and argv[0] is JSVAL_VOID.
708 static JSBool
709 QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval)
711 jsval nameval, nsval;
712 JSBool isQName, isNamespace;
713 JSObject *qn;
714 JSLinearString *uri, *prefix, *name;
715 JSObject *obj2;
717 if (argc <= 0) {
718 nameval = JSVAL_VOID;
719 isQName = JS_FALSE;
720 } else {
721 nameval = argv[argc > 1];
722 isQName =
723 !JSVAL_IS_PRIMITIVE(nameval) &&
724 JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass;
727 if (!obj) {
728 /* QName called as function. */
729 if (argc == 1 && isQName) {
730 /* QName called with one QName argument is identity. */
731 *rval = nameval;
732 return JS_TRUE;
735 /* Create and return a new QName object exactly as if constructed. */
736 obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
737 if (!obj)
738 return JS_FALSE;
740 *rval = OBJECT_TO_JSVAL(obj);
741 METER(xml_stats.qname);
743 if (isQName) {
744 /* If namespace is not specified and name is a QName, clone it. */
745 qn = JSVAL_TO_OBJECT(nameval);
746 if (argc == 1) {
747 uri = qn->getNameURI();
748 prefix = qn->getNamePrefix();
749 name = qn->getQNameLocalName();
750 goto out;
753 /* Namespace and qname were passed -- use the qname's localName. */
754 nameval = qn->getQNameLocalNameVal();
757 if (argc == 0) {
758 name = cx->runtime->emptyString;
759 } else if (argc < 0) {
760 name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
761 } else {
762 JSString *str = js_ValueToString(cx, Valueify(nameval));
763 if (!str)
764 return JS_FALSE;
765 name = str->ensureLinear(cx);
766 if (!name)
767 return JS_FALSE;
768 argv[argc > 1] = STRING_TO_JSVAL(name);
771 if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
772 nsval = argv[0];
773 } else if (IS_STAR(name)) {
774 nsval = JSVAL_NULL;
775 } else {
776 if (!js_GetDefaultXMLNamespace(cx, &nsval))
777 return JS_FALSE;
778 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
779 JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
780 &js_NamespaceClass);
783 if (JSVAL_IS_NULL(nsval)) {
784 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
785 prefix = uri = NULL;
786 } else {
788 * Inline specialization of the Namespace constructor called with
789 * nsval passed as the only argument, to compute the uri and prefix
790 * for the constructed namespace, without actually allocating the
791 * object or computing other members. See ECMA-357 13.3.2 6(a) and
792 * 13.2.2.
794 isNamespace = isQName = JS_FALSE;
795 if (!JSVAL_IS_PRIMITIVE(nsval)) {
796 obj2 = JSVAL_TO_OBJECT(nsval);
797 isNamespace = (obj2->getClass() == &js_NamespaceClass);
798 isQName = (obj2->getClass() == &js_QNameClass);
800 #ifdef __GNUC__ /* suppress bogus gcc warnings */
801 else obj2 = NULL;
802 #endif
804 if (isNamespace) {
805 uri = obj2->getNameURI();
806 prefix = obj2->getNamePrefix();
807 } else if (isQName && (uri = obj2->getNameURI())) {
808 JS_ASSERT(argc > 1);
809 prefix = obj2->getNamePrefix();
810 } else {
811 JS_ASSERT(argc > 1);
812 JSString *str = js_ValueToString(cx, Valueify(nsval));
813 if (!str)
814 return JS_FALSE;
815 uri = str->ensureLinear(cx);
816 if (!uri)
817 return JS_FALSE;
818 argv[0] = STRING_TO_JSVAL(uri); /* local root */
820 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
821 prefix = uri->empty() ? cx->runtime->emptyString : NULL;
825 out:
826 InitXMLQName(obj, uri, prefix, name);
827 return JS_TRUE;
830 static JSBool
831 QName(JSContext *cx, uintN argc, Value *vp)
833 JSObject *thisobj = NULL;
834 (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
835 return QNameHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
839 * XMLArray library functions.
841 static JSBool
842 namespace_identity(const void *a, const void *b)
844 const JSObject *nsa = (const JSObject *) a;
845 const JSObject *nsb = (const JSObject *) b;
846 JSLinearString *prefixa = nsa->getNamePrefix();
847 JSLinearString *prefixb = nsb->getNamePrefix();
849 if (prefixa && prefixb) {
850 if (!EqualStrings(prefixa, prefixb))
851 return JS_FALSE;
852 } else {
853 if (prefixa || prefixb)
854 return JS_FALSE;
856 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
859 static JSBool
860 attr_identity(const void *a, const void *b)
862 const JSXML *xmla = (const JSXML *) a;
863 const JSXML *xmlb = (const JSXML *) b;
865 return qname_identity(xmla->name, xmlb->name);
868 void
869 JSXMLArrayCursor::trace(JSTracer *trc) {
870 #ifdef DEBUG
871 size_t index = 0;
872 #endif
873 for (JSXMLArrayCursor *cursor = this; cursor; cursor = cursor->next)
874 js::gc::MarkGCThing(trc, cursor->root, "cursor_root", index++);
877 static void
878 XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
880 cursor->trace(trc);
883 /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
884 bool
885 JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity)
887 if (newCapacity == 0) {
888 /* We could let realloc(p, 0) free this, but purify gets confused. */
889 if (vector) {
890 if (cx)
891 cx->free(vector);
892 else
893 js_free(vector);
895 vector = NULL;
896 } else {
897 void **tmp;
899 if (
900 #if JS_BITS_PER_WORD == 32
901 (size_t)newCapacity > ~(size_t)0 / sizeof(void *) ||
902 #endif
903 !(tmp = (void **) js_realloc(vector, newCapacity * sizeof(void *)))) {
904 if (cx)
905 JS_ReportOutOfMemory(cx);
906 return false;
908 vector = tmp;
910 capacity = JSXML_PRESET_CAPACITY | newCapacity;
911 return true;
914 void
915 JSXMLArray::trim()
917 if (capacity & JSXML_PRESET_CAPACITY)
918 return;
919 if (length < capacity)
920 setCapacity(NULL, length);
923 void
924 JSXMLArray::finish(JSContext *cx)
926 cx->free(vector);
928 while (JSXMLArrayCursor *cursor = cursors)
929 cursor->disconnect();
931 #ifdef DEBUG
932 memset(this, 0xd5, sizeof *this);
933 #endif
936 #define XML_NOT_FOUND ((uint32) -1)
938 static uint32
939 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
941 void **vector;
942 uint32 i, n;
944 /* The identity op must not reallocate array->vector. */
945 vector = array->vector;
946 if (identity) {
947 for (i = 0, n = array->length; i < n; i++) {
948 if (identity(vector[i], elt))
949 return i;
951 } else {
952 for (i = 0, n = array->length; i < n; i++) {
953 if (vector[i] == elt)
954 return i;
957 return XML_NOT_FOUND;
961 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
962 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
963 * should be greater than increment.
965 #define LINEAR_THRESHOLD 256
966 #define LINEAR_INCREMENT 32
968 static JSBool
969 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
971 uint32 capacity, i;
972 int log2;
973 void **vector;
975 if (index >= array->length) {
976 if (index >= JSXML_CAPACITY(array)) {
977 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
978 capacity = index + 1;
979 if (index >= LINEAR_THRESHOLD) {
980 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
981 } else {
982 JS_CEILING_LOG2(log2, capacity);
983 capacity = JS_BIT(log2);
985 if (
986 #if JS_BITS_PER_WORD == 32
987 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
988 #endif
989 !(vector = (void **)
990 js_realloc(array->vector, capacity * sizeof(void *)))) {
991 JS_ReportOutOfMemory(cx);
992 return JS_FALSE;
994 array->capacity = capacity;
995 array->vector = vector;
996 for (i = array->length; i < index; i++)
997 vector[i] = NULL;
999 array->length = index + 1;
1002 array->vector[index] = elt;
1003 return JS_TRUE;
1006 static JSBool
1007 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1009 uint32 j;
1010 JSXMLArrayCursor *cursor;
1012 j = array->length;
1013 JS_ASSERT(i <= j);
1014 if (!array->setCapacity(cx, j + n))
1015 return JS_FALSE;
1017 array->length = j + n;
1018 JS_ASSERT(n != (uint32)-1);
1019 while (j != i) {
1020 --j;
1021 array->vector[j + n] = array->vector[j];
1024 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1025 if (cursor->index > i)
1026 cursor->index += n;
1028 return JS_TRUE;
1031 static void *
1032 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1034 uint32 length;
1035 void **vector, *elt;
1036 JSXMLArrayCursor *cursor;
1038 length = array->length;
1039 if (index >= length)
1040 return NULL;
1042 vector = array->vector;
1043 elt = vector[index];
1044 if (compress) {
1045 while (++index < length)
1046 vector[index-1] = vector[index];
1047 array->length = length - 1;
1048 array->capacity = JSXML_CAPACITY(array);
1049 } else {
1050 vector[index] = NULL;
1053 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1054 if (cursor->index > index)
1055 --cursor->index;
1057 return elt;
1060 static void
1061 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1063 void **vector;
1065 JS_ASSERT(!array->cursors);
1066 if (length >= array->length)
1067 return;
1069 if (length == 0) {
1070 if (array->vector)
1071 cx->free(array->vector);
1072 vector = NULL;
1073 } else {
1074 vector = (void **) js_realloc(array->vector, length * sizeof(void *));
1075 if (!vector)
1076 return;
1079 if (array->length > length)
1080 array->length = length;
1081 array->capacity = length;
1082 array->vector = vector;
1085 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1086 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1087 XML_NOT_FOUND)
1088 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1089 ? (t *) (a)->vector[i] \
1090 : NULL)
1091 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1092 if ((a)->length <= (i)) \
1093 (a)->length = (i) + 1; \
1094 ((a)->vector[i] = (void *)(e)); \
1095 JS_END_MACRO
1096 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1097 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1098 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1099 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1100 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1103 * Define XML setting property strings and constants early, so everyone can
1104 * use the same names.
1106 static const char js_ignoreComments_str[] = "ignoreComments";
1107 static const char js_ignoreProcessingInstructions_str[]
1108 = "ignoreProcessingInstructions";
1109 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1110 static const char js_prettyPrinting_str[] = "prettyPrinting";
1111 static const char js_prettyIndent_str[] = "prettyIndent";
1113 #define XSF_IGNORE_COMMENTS JS_BIT(0)
1114 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1115 #define XSF_IGNORE_WHITESPACE JS_BIT(2)
1116 #define XSF_PRETTY_PRINTING JS_BIT(3)
1118 static JSPropertySpec xml_static_props[] = {
1119 {js_ignoreComments_str, 0, JSPROP_PERMANENT, NULL, NULL},
1120 {js_ignoreProcessingInstructions_str, 0, JSPROP_PERMANENT, NULL, NULL},
1121 {js_ignoreWhitespace_str, 0, JSPROP_PERMANENT, NULL, NULL},
1122 {js_prettyPrinting_str, 0, JSPROP_PERMANENT, NULL, NULL},
1123 {js_prettyIndent_str, 0, JSPROP_PERMANENT, NULL, NULL},
1124 {0,0,0,0,0}
1127 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1128 #define IS_XML(str) \
1129 (str->length() == 3 && IS_XML_CHARS(str->chars()))
1131 #define IS_XMLNS(str) \
1132 (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1134 #define IS_XML_CHARS(chars) \
1135 (JS_TOLOWER((chars)[0]) == 'x' && \
1136 JS_TOLOWER((chars)[1]) == 'm' && \
1137 JS_TOLOWER((chars)[2]) == 'l')
1139 #define HAS_NS_AFTER_XML(chars) \
1140 (JS_TOLOWER((chars)[3]) == 'n' && \
1141 JS_TOLOWER((chars)[4]) == 's')
1143 #define IS_XMLNS_CHARS(chars) \
1144 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1146 #define STARTS_WITH_XML(chars,length) \
1147 (length >= 3 && IS_XML_CHARS(chars))
1149 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1150 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1152 static JSObject *
1153 ParseNodeToQName(Parser *parser, JSParseNode *pn,
1154 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1156 JSContext *cx = parser->context;
1157 JSLinearString *str, *uri, *prefix, *localName;
1158 size_t length, offset;
1159 const jschar *start, *limit, *colon;
1160 uint32 n;
1161 JSObject *ns;
1162 JSLinearString *nsprefix;
1164 JS_ASSERT(pn->pn_arity == PN_NULLARY);
1165 str = pn->pn_atom;
1166 start = str->chars();
1167 length = str->length();
1168 JS_ASSERT(length != 0 && *start != '@');
1169 JS_ASSERT(length != 1 || *start != '*');
1171 uri = cx->runtime->emptyString;
1172 limit = start + length;
1173 colon = js_strchr_limit(start, ':', limit);
1174 if (colon) {
1175 offset = colon - start;
1176 prefix = js_NewDependentString(cx, str, 0, offset);
1177 if (!prefix)
1178 return NULL;
1180 if (STARTS_WITH_XML(start, offset)) {
1181 if (offset == 3) {
1182 uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str));
1183 if (!uri)
1184 return NULL;
1185 } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1186 uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str));
1187 if (!uri)
1188 return NULL;
1189 } else {
1190 uri = NULL;
1192 } else {
1193 uri = NULL;
1194 n = inScopeNSes->length;
1195 while (n != 0) {
1196 --n;
1197 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1198 nsprefix = ns->getNamePrefix();
1199 if (nsprefix && EqualStrings(nsprefix, prefix)) {
1200 uri = ns->getNameURI();
1201 break;
1206 if (!uri) {
1207 Value v = StringValue(prefix);
1208 JSAutoByteString bytes;
1209 if (js_ValueToPrintable(parser->context, v, &bytes)) {
1210 ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
1211 JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
1213 return NULL;
1216 localName = js_NewStringCopyN(parser->context, colon + 1, length - (offset + 1));
1217 if (!localName)
1218 return NULL;
1219 } else {
1220 if (isAttributeName) {
1222 * An unprefixed attribute is not in any namespace, so set prefix
1223 * as well as uri to the empty string.
1225 prefix = uri;
1226 } else {
1228 * Loop from back to front looking for the closest declared default
1229 * namespace.
1231 n = inScopeNSes->length;
1232 while (n != 0) {
1233 --n;
1234 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1235 nsprefix = ns->getNamePrefix();
1236 if (!nsprefix || nsprefix->empty()) {
1237 uri = ns->getNameURI();
1238 break;
1241 prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
1243 localName = str;
1246 return NewXMLQName(parser->context, uri, prefix, localName);
1249 static JSString *
1250 ChompXMLWhitespace(JSContext *cx, JSString *str)
1252 size_t length, newlength, offset;
1253 const jschar *cp, *start, *end;
1254 jschar c;
1256 length = str->length();
1257 start = str->getChars(cx);
1258 if (!start)
1259 return NULL;
1261 for (cp = start, end = cp + length; cp < end; cp++) {
1262 c = *cp;
1263 if (!JS_ISXMLSPACE(c))
1264 break;
1266 while (end > cp) {
1267 c = end[-1];
1268 if (!JS_ISXMLSPACE(c))
1269 break;
1270 --end;
1272 newlength = end - cp;
1273 if (newlength == length)
1274 return str;
1275 offset = cp - start;
1276 return js_NewDependentString(cx, str, offset, newlength);
1279 static JSXML *
1280 ParseNodeToXML(Parser *parser, JSParseNode *pn,
1281 JSXMLArray *inScopeNSes, uintN flags)
1283 JSContext *cx = parser->context;
1284 JSXML *xml, *kid, *attr, *attrj;
1285 JSLinearString *str;
1286 uint32 length, n, i, j;
1287 JSParseNode *pn2, *pn3, *head, **pnp;
1288 JSObject *ns;
1289 JSObject *qn, *attrjqn;
1290 JSXMLClass xml_class;
1291 int stackDummy;
1293 if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy)) {
1294 ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR,
1295 JSMSG_OVER_RECURSED);
1296 return NULL;
1299 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1302 * Cases return early to avoid common code that gets an outermost xml's
1303 * object, which protects GC-things owned by xml and its descendants from
1304 * garbage collection.
1306 xml = NULL;
1307 if (!js_EnterLocalRootScope(cx))
1308 return NULL;
1309 switch (pn->pn_type) {
1310 case TOK_XMLELEM:
1311 length = inScopeNSes->length;
1312 pn2 = pn->pn_head;
1313 xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1314 if (!xml)
1315 goto fail;
1317 n = pn->pn_count;
1318 JS_ASSERT(n >= 2);
1319 n -= 2;
1320 if (!xml->xml_kids.setCapacity(cx, n))
1321 goto fail;
1323 i = 0;
1324 while ((pn2 = pn2->pn_next) != NULL) {
1325 if (!pn2->pn_next) {
1326 /* Don't append the end tag! */
1327 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1328 break;
1331 if ((flags & XSF_IGNORE_WHITESPACE) &&
1332 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1333 --n;
1334 continue;
1337 kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1338 if (kid == PN2X_SKIP_CHILD) {
1339 --n;
1340 continue;
1343 if (!kid)
1344 goto fail;
1346 /* Store kid in xml right away, to protect it from GC. */
1347 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1348 kid->parent = xml;
1349 ++i;
1351 /* XXX where is this documented in an XML spec, or in E4X? */
1352 if ((flags & XSF_IGNORE_WHITESPACE) &&
1353 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1354 JSString *str = ChompXMLWhitespace(cx, kid->xml_value);
1355 if (!str)
1356 goto fail;
1357 kid->xml_value = str;
1361 JS_ASSERT(i == n);
1362 if (n < pn->pn_count - 2)
1363 xml->xml_kids.trim();
1364 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1365 break;
1367 case TOK_XMLLIST:
1368 xml = js_NewXML(cx, JSXML_CLASS_LIST);
1369 if (!xml)
1370 goto fail;
1372 n = pn->pn_count;
1373 if (!xml->xml_kids.setCapacity(cx, n))
1374 goto fail;
1376 i = 0;
1377 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1379 * Always ignore insignificant whitespace in lists -- we shouldn't
1380 * condition this on an XML.ignoreWhitespace setting when the list
1381 * constructor is XMLList (note XML/XMLList unification hazard).
1383 if (pn2->pn_type == TOK_XMLSPACE) {
1384 --n;
1385 continue;
1388 kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1389 if (kid == PN2X_SKIP_CHILD) {
1390 --n;
1391 continue;
1394 if (!kid)
1395 goto fail;
1397 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1398 ++i;
1401 if (n < pn->pn_count)
1402 xml->xml_kids.trim();
1403 break;
1405 case TOK_XMLSTAGO:
1406 case TOK_XMLPTAGC:
1407 length = inScopeNSes->length;
1408 pn2 = pn->pn_head;
1409 JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1410 if (pn2->pn_arity == PN_LIST)
1411 goto syntax;
1413 xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1414 if (!xml)
1415 goto fail;
1417 /* First pass: check syntax and process namespace declarations. */
1418 JS_ASSERT(pn->pn_count >= 1);
1419 n = pn->pn_count - 1;
1420 pnp = &pn2->pn_next;
1421 head = *pnp;
1422 while ((pn2 = *pnp) != NULL) {
1423 size_t length;
1424 const jschar *chars;
1426 if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1427 goto syntax;
1429 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1430 for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1431 if (pn3->pn_atom == pn2->pn_atom) {
1432 Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom));
1433 JSAutoByteString bytes;
1434 if (js_ValueToPrintable(cx, v, &bytes)) {
1435 ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1436 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1437 bytes.ptr());
1439 goto fail;
1443 JSAtom *atom = pn2->pn_atom;
1444 pn2 = pn2->pn_next;
1445 JS_ASSERT(pn2);
1446 if (pn2->pn_type != TOK_XMLATTR)
1447 goto syntax;
1449 chars = atom->chars();
1450 length = atom->length();
1451 if (length >= 5 &&
1452 IS_XMLNS_CHARS(chars) &&
1453 (length == 5 || chars[5] == ':')) {
1454 JSLinearString *uri, *prefix;
1456 uri = ATOM_TO_STRING(pn2->pn_atom);
1457 if (length == 5) {
1458 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1459 prefix = cx->runtime->emptyString;
1460 } else {
1461 prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1462 if (!prefix)
1463 goto fail;
1467 * Once the new ns is appended to xml->xml_namespaces, it is
1468 * protected from GC by the object that owns xml -- which is
1469 * either xml->object if outermost, or the object owning xml's
1470 * oldest ancestor if !outermost.
1472 ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1473 if (!ns)
1474 goto fail;
1477 * Don't add a namespace that's already in scope. If someone
1478 * extracts a child property from its parent via [[Get]], then
1479 * we enforce the invariant, noted many times in ECMA-357, that
1480 * the child's namespaces form a possibly-improper superset of
1481 * its ancestors' namespaces.
1483 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1484 if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1485 !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1486 goto fail;
1490 JS_ASSERT(n >= 2);
1491 n -= 2;
1492 *pnp = pn2->pn_next;
1493 /* XXXbe recycle pn2 */
1494 continue;
1497 pnp = &pn2->pn_next;
1500 xml->xml_namespaces.trim();
1502 /* Second pass: process tag name and attributes, using namespaces. */
1503 pn2 = pn->pn_head;
1504 qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_FALSE);
1505 if (!qn)
1506 goto fail;
1507 xml->name = qn;
1509 JS_ASSERT((n & 1) == 0);
1510 n >>= 1;
1511 if (!xml->xml_attrs.setCapacity(cx, n))
1512 goto fail;
1514 for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1515 qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_TRUE);
1516 if (!qn) {
1517 xml->xml_attrs.length = i;
1518 goto fail;
1522 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1523 * this time checking local name and namespace URI.
1525 for (j = 0; j < i; j++) {
1526 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1527 attrjqn = attrj->name;
1528 if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) &&
1529 EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) {
1530 Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom));
1531 JSAutoByteString bytes;
1532 if (js_ValueToPrintable(cx, v, &bytes)) {
1533 ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1534 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1535 bytes.ptr());
1537 goto fail;
1541 pn2 = pn2->pn_next;
1542 JS_ASSERT(pn2);
1543 JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1545 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1546 if (!attr)
1547 goto fail;
1549 XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1550 attr->parent = xml;
1551 attr->name = qn;
1552 attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1555 /* Point tag closes its own namespace scope. */
1556 if (pn->pn_type == TOK_XMLPTAGC)
1557 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1558 break;
1560 case TOK_XMLSPACE:
1561 case TOK_XMLTEXT:
1562 case TOK_XMLCDATA:
1563 case TOK_XMLCOMMENT:
1564 case TOK_XMLPI:
1565 str = ATOM_TO_STRING(pn->pn_atom);
1566 qn = NULL;
1567 if (pn->pn_type == TOK_XMLCOMMENT) {
1568 if (flags & XSF_IGNORE_COMMENTS)
1569 goto skip_child;
1570 xml_class = JSXML_CLASS_COMMENT;
1571 } else if (pn->pn_type == TOK_XMLPI) {
1572 if (IS_XML(str)) {
1573 Value v = StringValue(str);
1574 JSAutoByteString bytes;
1575 if (js_ValueToPrintable(cx, v, &bytes)) {
1576 ReportCompileErrorNumber(cx, &parser->tokenStream, pn,
1577 JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
1579 goto fail;
1582 if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1583 goto skip_child;
1585 qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE);
1586 if (!qn)
1587 goto fail;
1589 str = pn->pn_atom2
1590 ? ATOM_TO_STRING(pn->pn_atom2)
1591 : cx->runtime->emptyString;
1592 xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1593 } else {
1594 /* CDATA section content, or element text. */
1595 xml_class = JSXML_CLASS_TEXT;
1598 xml = js_NewXML(cx, xml_class);
1599 if (!xml)
1600 goto fail;
1601 xml->name = qn;
1602 if (pn->pn_type == TOK_XMLSPACE)
1603 xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1604 xml->xml_value = str;
1605 break;
1607 default:
1608 goto syntax;
1611 js_LeaveLocalRootScopeWithResult(cx, xml);
1612 return xml;
1614 skip_child:
1615 js_LeaveLocalRootScope(cx);
1616 return PN2X_SKIP_CHILD;
1618 #undef PN2X_SKIP_CHILD
1620 syntax:
1621 ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
1622 fail:
1623 js_LeaveLocalRootScope(cx);
1624 return NULL;
1628 * XML helper, object-ops, and library functions. We start with the helpers,
1629 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1631 static JSBool
1632 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1634 jsval v;
1636 if (!js_FindClassObject(cx, NULL, JSProto_XML, Valueify(&v)))
1637 return JS_FALSE;
1638 if (!VALUE_IS_FUNCTION(cx, v)) {
1639 *vp = JSVAL_VOID;
1640 return JS_TRUE;
1642 return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1645 static JSBool
1646 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1648 jsval v;
1650 return GetXMLSetting(cx, name, &v) && JS_ValueToBoolean(cx, v, bp);
1653 static JSBool
1654 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1656 jsval v;
1658 return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1661 static JSBool
1662 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1664 JSBool flag[4];
1666 if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag[0]) ||
1667 !GetBooleanXMLSetting(cx, js_ignoreProcessingInstructions_str, &flag[1]) ||
1668 !GetBooleanXMLSetting(cx, js_ignoreWhitespace_str, &flag[2]) ||
1669 !GetBooleanXMLSetting(cx, js_prettyPrinting_str, &flag[3])) {
1670 return false;
1673 *flagsp = 0;
1674 for (size_t n = 0; n < 4; ++n)
1675 if (flag[n])
1676 *flagsp |= JS_BIT(n);
1677 return true;
1680 static JSXML *
1681 ParseXMLSource(JSContext *cx, JSString *src)
1683 jsval nsval;
1684 JSLinearString *uri;
1685 size_t urilen, srclen, length, offset, dstlen;
1686 jschar *chars;
1687 const jschar *srcp, *endp;
1688 JSXML *xml;
1689 const char *filename;
1690 uintN lineno;
1691 JSOp op;
1693 static const char prefix[] = "<parent xmlns=\"";
1694 static const char middle[] = "\">";
1695 static const char suffix[] = "</parent>";
1697 #define constrlen(constr) (sizeof(constr) - 1)
1699 if (!js_GetDefaultXMLNamespace(cx, &nsval))
1700 return NULL;
1701 uri = JSVAL_TO_OBJECT(nsval)->getNameURI();
1702 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1703 if (!uri)
1704 return NULL;
1706 urilen = uri->length();
1707 srclen = src->length();
1708 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1709 constrlen(suffix);
1711 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
1712 if (!chars)
1713 return NULL;
1715 dstlen = length;
1716 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1717 offset = dstlen;
1718 js_strncpy(chars + offset, uri->chars(), urilen);
1719 offset += urilen;
1720 dstlen = length - offset + 1;
1721 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1722 &dstlen);
1723 offset += dstlen;
1724 srcp = src->getChars(cx);
1725 if (!srcp) {
1726 cx->free(chars);
1727 return NULL;
1729 js_strncpy(chars + offset, srcp, srclen);
1730 offset += srclen;
1731 dstlen = length - offset + 1;
1732 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1733 &dstlen);
1734 chars [offset + dstlen] = 0;
1736 LeaveTrace(cx);
1737 xml = NULL;
1738 FrameRegsIter i(cx);
1739 for (; !i.done() && !i.pc(); ++i)
1740 JS_ASSERT(!i.fp()->isScriptFrame());
1741 filename = NULL;
1742 lineno = 1;
1743 if (!i.done()) {
1744 JSStackFrame *fp = i.fp();
1745 op = (JSOp) *i.pc();
1746 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1747 filename = fp->script()->filename;
1748 lineno = js_FramePCToLineNumber(cx, fp);
1749 for (endp = srcp + srclen; srcp < endp; srcp++) {
1750 if (*srcp == '\n')
1751 --lineno;
1757 Parser parser(cx);
1758 if (parser.init(chars, length, filename, lineno, cx->findVersion())) {
1759 JSObject *scopeChain = GetScopeChain(cx);
1760 if (!scopeChain) {
1761 cx->free(chars);
1762 return NULL;
1764 JSParseNode *pn = parser.parseXMLText(scopeChain, false);
1765 uintN flags;
1766 if (pn && GetXMLSettingFlags(cx, &flags)) {
1767 AutoNamespaceArray namespaces(cx);
1768 if (namespaces.array.setCapacity(cx, 1))
1769 xml = ParseNodeToXML(&parser, pn, &namespaces.array, flags);
1774 cx->free(chars);
1775 return xml;
1777 #undef constrlen
1781 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1783 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1784 * the constraint:
1786 * for all x belonging to XML:
1787 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1789 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1790 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1792 * Same goes for 10.4.1 Step 7(a).
1794 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1795 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1796 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1797 * undeclared namespaces associated with x not belonging to ancestorNS.
1799 static JSXML *
1800 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1802 JSObject *ns;
1804 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1805 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1806 if (!ns || !xml)
1807 return xml;
1808 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1809 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1810 return NULL;
1811 ns->setNamespaceDeclared(JSVAL_VOID);
1813 xml->parent = NULL;
1814 return xml;
1817 static JSObject *
1818 ToXML(JSContext *cx, jsval v)
1820 JSObject *obj;
1821 JSXML *xml;
1822 Class *clasp;
1823 JSString *str;
1824 uint32 length;
1826 if (JSVAL_IS_PRIMITIVE(v)) {
1827 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1828 goto bad;
1829 } else {
1830 obj = JSVAL_TO_OBJECT(v);
1831 if (obj->isXML()) {
1832 xml = (JSXML *) obj->getPrivate();
1833 if (xml->xml_class == JSXML_CLASS_LIST) {
1834 if (xml->xml_kids.length != 1)
1835 goto bad;
1836 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1837 if (xml) {
1838 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1839 return js_GetXMLObject(cx, xml);
1842 return obj;
1845 clasp = obj->getClass();
1846 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1847 JS_ASSERT(0);
1850 if (clasp != &js_StringClass &&
1851 clasp != &js_NumberClass &&
1852 clasp != &js_BooleanClass) {
1853 goto bad;
1857 str = js_ValueToString(cx, Valueify(v));
1858 if (!str)
1859 return NULL;
1860 if (str->empty()) {
1861 length = 0;
1862 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1863 xml = NULL;
1864 #endif
1865 } else {
1866 xml = ParseXMLSource(cx, str);
1867 if (!xml)
1868 return NULL;
1869 length = JSXML_LENGTH(xml);
1872 if (length == 0) {
1873 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
1874 if (!obj)
1875 return NULL;
1876 } else if (length == 1) {
1877 xml = OrphanXMLChild(cx, xml, 0);
1878 if (!xml)
1879 return NULL;
1880 obj = js_GetXMLObject(cx, xml);
1881 if (!obj)
1882 return NULL;
1883 } else {
1884 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
1885 return NULL;
1887 return obj;
1889 bad:
1890 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
1891 JSDVG_IGNORE_STACK, Valueify(v), NULL);
1892 return NULL;
1895 static JSBool
1896 Append(JSContext *cx, JSXML *list, JSXML *kid);
1898 static JSObject *
1899 ToXMLList(JSContext *cx, jsval v)
1901 JSObject *obj, *listobj;
1902 JSXML *xml, *list, *kid;
1903 Class *clasp;
1904 JSString *str;
1905 uint32 i, length;
1907 if (JSVAL_IS_PRIMITIVE(v)) {
1908 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1909 goto bad;
1910 } else {
1911 obj = JSVAL_TO_OBJECT(v);
1912 if (obj->isXML()) {
1913 xml = (JSXML *) obj->getPrivate();
1914 if (xml->xml_class != JSXML_CLASS_LIST) {
1915 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1916 if (!listobj)
1917 return NULL;
1918 list = (JSXML *) listobj->getPrivate();
1919 if (!Append(cx, list, xml))
1920 return NULL;
1921 return listobj;
1923 return obj;
1926 clasp = obj->getClass();
1927 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1928 JS_ASSERT(0);
1931 if (clasp != &js_StringClass &&
1932 clasp != &js_NumberClass &&
1933 clasp != &js_BooleanClass) {
1934 goto bad;
1938 str = js_ValueToString(cx, Valueify(v));
1939 if (!str)
1940 return NULL;
1941 if (str->empty()) {
1942 xml = NULL;
1943 length = 0;
1944 } else {
1945 if (!js_EnterLocalRootScope(cx))
1946 return NULL;
1947 xml = ParseXMLSource(cx, str);
1948 if (!xml) {
1949 js_LeaveLocalRootScope(cx);
1950 return NULL;
1952 length = JSXML_LENGTH(xml);
1955 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1956 if (listobj) {
1957 list = (JSXML *) listobj->getPrivate();
1958 for (i = 0; i < length; i++) {
1959 kid = OrphanXMLChild(cx, xml, i);
1960 if (!kid || !Append(cx, list, kid)) {
1961 listobj = NULL;
1962 break;
1967 if (xml)
1968 js_LeaveLocalRootScopeWithResult(cx, listobj);
1969 return listobj;
1971 bad:
1972 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
1973 JSDVG_IGNORE_STACK, Valueify(v), NULL);
1974 return NULL;
1978 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
1979 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
1980 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
1981 * MakeXMLSpecialString subroutine.
1983 * These functions mutate sb, leaving it empty.
1985 static JSFlatString *
1986 MakeXMLSpecialString(JSContext *cx, StringBuffer &sb,
1987 JSString *str, JSString *str2,
1988 const jschar *prefix, size_t prefixlength,
1989 const jschar *suffix, size_t suffixlength)
1991 if (!sb.append(prefix, prefixlength) || !sb.append(str))
1992 return NULL;
1993 if (str2 && !str2->empty()) {
1994 if (!sb.append(' ') || !sb.append(str2))
1995 return NULL;
1997 if (!sb.append(suffix, suffixlength))
1998 return NULL;
2000 return sb.finishString();
2003 static JSFlatString *
2004 MakeXMLCDATAString(JSContext *cx, StringBuffer &sb, JSString *str)
2006 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2007 'C', 'D', 'A', 'T', 'A',
2008 '['};
2009 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2011 return MakeXMLSpecialString(cx, sb, str, NULL,
2012 cdata_prefix_ucNstr, 9,
2013 cdata_suffix_ucNstr, 3);
2016 static JSFlatString *
2017 MakeXMLCommentString(JSContext *cx, StringBuffer &sb, JSString *str)
2019 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2020 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2022 return MakeXMLSpecialString(cx, sb, str, NULL,
2023 comment_prefix_ucNstr, 4,
2024 comment_suffix_ucNstr, 3);
2027 static JSFlatString *
2028 MakeXMLPIString(JSContext *cx, StringBuffer &sb, JSString *name,
2029 JSString *value)
2031 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2032 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2034 return MakeXMLSpecialString(cx, sb, name, value,
2035 pi_prefix_ucNstr, 2,
2036 pi_suffix_ucNstr, 2);
2040 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2041 * equals, a double quote, an attribute value, and a closing double quote.
2043 static bool
2044 AppendAttributeValue(JSContext *cx, StringBuffer &sb, JSString *valstr)
2046 if (!sb.append('='))
2047 return false;
2048 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2049 return valstr && sb.append(valstr);
2053 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2055 * These functions mutate sb, leaving it empty.
2057 static JSFlatString *
2058 EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32 toSourceFlag)
2060 size_t length = str->length();
2061 const jschar *start = str->getChars(cx);
2062 if (!start)
2063 return NULL;
2065 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2066 jschar c = *cp;
2067 switch (*cp) {
2068 case '<':
2069 if (!sb.append(js_lt_entity_str))
2070 return NULL;
2071 break;
2072 case '>':
2073 if (!sb.append(js_gt_entity_str))
2074 return NULL;
2075 break;
2076 case '&':
2077 if (!sb.append(js_amp_entity_str))
2078 return NULL;
2079 break;
2080 case '{':
2082 * If EscapeElementValue is called by toSource/uneval, we also need
2083 * to escape '{'. See bug 463360.
2085 if (toSourceFlag) {
2086 if (!sb.append(js_leftcurly_entity_str))
2087 return NULL;
2088 break;
2090 /* FALL THROUGH */
2091 default:
2092 if (!sb.append(c))
2093 return NULL;
2096 return sb.finishString();
2100 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2102 * These functions mutate sb, leaving it empty.
2104 static JSFlatString *
2105 EscapeAttributeValue(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote)
2107 size_t length = str->length();
2108 const jschar *start = str->getChars(cx);
2109 if (!start)
2110 return NULL;
2112 if (quote && !sb.append('"'))
2113 return NULL;
2115 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2116 jschar c = *cp;
2117 switch (c) {
2118 case '"':
2119 if (!sb.append(js_quot_entity_str))
2120 return NULL;
2121 break;
2122 case '<':
2123 if (!sb.append(js_lt_entity_str))
2124 return NULL;
2125 break;
2126 case '&':
2127 if (!sb.append(js_amp_entity_str))
2128 return NULL;
2129 break;
2130 case '\n':
2131 if (!sb.append("&#xA;"))
2132 return NULL;
2133 break;
2134 case '\r':
2135 if (!sb.append("&#xD;"))
2136 return NULL;
2137 break;
2138 case '\t':
2139 if (!sb.append("&#x9;"))
2140 return NULL;
2141 break;
2142 default:
2143 if (!sb.append(c))
2144 return NULL;
2148 if (quote && !sb.append('"'))
2149 return NULL;
2151 return sb.finishString();
2154 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2155 static JSObject *
2156 GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2158 JSLinearString *uri, *prefix, *nsprefix;
2159 JSObject *match, *ns;
2160 uint32 i, n;
2161 jsval argv[2];
2163 uri = qn->getNameURI();
2164 prefix = qn->getNamePrefix();
2165 JS_ASSERT(uri);
2166 if (!uri) {
2167 JSAutoByteString bytes;
2168 const char *s = !prefix ?
2169 js_undefined_str
2170 : js_ValueToPrintable(cx, StringValue(prefix), &bytes);
2171 if (s)
2172 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, s);
2173 return NULL;
2176 /* Look for a matching namespace in inScopeNSes, if provided. */
2177 match = NULL;
2178 if (inScopeNSes) {
2179 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2180 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2181 if (!ns)
2182 continue;
2185 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2186 * If we preserve prefixes, we must match null prefix against
2187 * an empty prefix of ns, in order to avoid generating redundant
2188 * prefixed and default namespaces for cases such as:
2190 * x = <t xmlns="http://foo.com"/>
2191 * print(x.toXMLString());
2193 * Per 10.3.2.1, the namespace attribute in t has an empty string
2194 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2196 * 1. If the [local name] property of a is "xmlns"
2197 * a. Map ns.prefix to the empty string
2199 * But t's name has a null prefix in this implementation, meaning
2200 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2201 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2202 * saying how "no value" maps to an ECMA-357 value -- but it must
2203 * map to the *undefined* prefix value).
2205 * Since "" != undefined (or null, in the current implementation)
2206 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2207 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2208 * This spec bug leads to ToXMLString results that duplicate the
2209 * declared namespace.
2211 if (EqualStrings(ns->getNameURI(), uri)) {
2212 nsprefix = ns->getNamePrefix();
2213 if (nsprefix == prefix ||
2214 ((nsprefix && prefix)
2215 ? EqualStrings(nsprefix, prefix)
2216 : (nsprefix ? nsprefix : prefix)->empty())) {
2217 match = ns;
2218 break;
2224 /* If we didn't match, make a new namespace from qn. */
2225 if (!match) {
2226 argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2227 argv[1] = STRING_TO_JSVAL(uri);
2228 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL,
2229 2, Valueify(argv));
2230 if (!ns)
2231 return NULL;
2232 match = ns;
2234 return match;
2237 static JSLinearString *
2238 GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls)
2240 const jschar *cp, *start, *end;
2241 size_t length, newlength, offset;
2242 uint32 i, n, m, serial;
2243 jschar *bp, *dp;
2244 JSBool done;
2245 JSObject *ns;
2246 JSLinearString *nsprefix, *prefix;
2248 JS_ASSERT(!uri->empty());
2251 * If there are no *declared* namespaces, skip all collision detection and
2252 * return a short prefix quickly; an example of such a situation:
2254 * var x = <f/>;
2255 * var n = new Namespace("http://example.com/");
2256 * x.@n::att = "val";
2257 * x.toXMLString();
2259 * This is necessary for various log10 uses below to be valid.
2261 if (decls->length == 0)
2262 return js_NewStringCopyZ(cx, "a");
2265 * Try peeling off the last filename suffix or pathname component till
2266 * we have a valid XML name. This heuristic will prefer "xul" given
2267 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2268 * likely URI of the form ".../xbl2/2005".
2270 start = uri->chars();
2271 end = start + uri->length();
2272 cp = end;
2273 while (--cp > start) {
2274 if (*cp == '.' || *cp == '/' || *cp == ':') {
2275 ++cp;
2276 length = end - cp;
2277 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2278 break;
2279 end = --cp;
2282 length = end - cp;
2285 * If the namespace consisted only of non-XML names or names that begin
2286 * case-insensitively with "xml", arbitrarily create a prefix consisting
2287 * of 'a's of size length (allowing dp-calculating code to work with or
2288 * without this branch executing) plus the space for storing a hyphen and
2289 * the serial number (avoiding reallocation if a collision happens).
2291 bp = (jschar *) cp;
2292 newlength = length;
2293 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2294 newlength = length + 2 + (size_t) log10((double) decls->length);
2295 bp = (jschar *)
2296 cx->malloc((newlength + 1) * sizeof(jschar));
2297 if (!bp)
2298 return NULL;
2300 bp[newlength] = 0;
2301 for (i = 0; i < newlength; i++)
2302 bp[i] = 'a';
2306 * Now search through decls looking for a collision. If we collide with
2307 * an existing prefix, start tacking on a hyphen and a serial number.
2309 serial = 0;
2310 do {
2311 done = JS_TRUE;
2312 for (i = 0, n = decls->length; i < n; i++) {
2313 ns = XMLARRAY_MEMBER(decls, i, JSObject);
2314 if (ns && (nsprefix = ns->getNamePrefix()) &&
2315 nsprefix->length() == newlength &&
2316 !memcmp(nsprefix->chars(), bp,
2317 newlength * sizeof(jschar))) {
2318 if (bp == cp) {
2319 newlength = length + 2 + (size_t) log10((double) n);
2320 bp = (jschar *)
2321 cx->malloc((newlength + 1) * sizeof(jschar));
2322 if (!bp)
2323 return NULL;
2324 js_strncpy(bp, cp, length);
2327 ++serial;
2328 JS_ASSERT(serial <= n);
2329 dp = bp + length + 2 + (size_t) log10((double) serial);
2330 *dp = 0;
2331 for (m = serial; m != 0; m /= 10)
2332 *--dp = (jschar)('0' + m % 10);
2333 *--dp = '-';
2334 JS_ASSERT(dp == bp + length);
2336 done = JS_FALSE;
2337 break;
2340 } while (!done);
2342 if (bp == cp) {
2343 offset = cp - start;
2344 prefix = js_NewDependentString(cx, uri, offset, length);
2345 } else {
2346 prefix = js_NewString(cx, bp, newlength);
2347 if (!prefix)
2348 cx->free(bp);
2350 return prefix;
2353 static JSBool
2354 namespace_match(const void *a, const void *b)
2356 const JSObject *nsa = (const JSObject *) a;
2357 const JSObject *nsb = (const JSObject *) b;
2358 JSLinearString *prefixa, *prefixb = nsb->getNamePrefix();
2360 if (prefixb) {
2361 prefixa = nsa->getNamePrefix();
2362 return prefixa && EqualStrings(prefixa, prefixb);
2364 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
2367 /* ECMA-357 10.2.1 and 10.2.2 */
2368 #define TO_SOURCE_FLAG 0x80000000
2370 static JSString *
2371 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2372 uint32 indentLevel)
2374 JSBool pretty, indentKids;
2375 StringBuffer sb(cx);
2376 JSString *str;
2377 JSLinearString *prefix, *nsuri;
2378 uint32 i, n, nextIndentLevel;
2379 JSObject *ns, *ns2;
2380 AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx);
2382 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2383 return NULL;
2385 if (pretty) {
2386 if (!sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2387 return NULL;
2390 str = NULL;
2392 switch (xml->xml_class) {
2393 case JSXML_CLASS_TEXT:
2394 /* Step 4. */
2395 if (pretty) {
2396 str = ChompXMLWhitespace(cx, xml->xml_value);
2397 if (!str)
2398 return NULL;
2399 } else {
2400 str = xml->xml_value;
2402 return EscapeElementValue(cx, sb, str, indentLevel & TO_SOURCE_FLAG);
2404 case JSXML_CLASS_ATTRIBUTE:
2405 /* Step 5. */
2406 return EscapeAttributeValue(cx, sb, xml->xml_value,
2407 (indentLevel & TO_SOURCE_FLAG) != 0);
2409 case JSXML_CLASS_COMMENT:
2410 /* Step 6. */
2411 return MakeXMLCommentString(cx, sb, xml->xml_value);
2413 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2414 /* Step 7. */
2415 return MakeXMLPIString(cx, sb, xml->name->getQNameLocalName(),
2416 xml->xml_value);
2418 case JSXML_CLASS_LIST:
2419 /* ECMA-357 10.2.2. */
2421 JSXMLArrayCursor cursor(&xml->xml_kids);
2422 i = 0;
2423 while (JSXML *kid = (JSXML *) cursor.getNext()) {
2424 if (pretty && i != 0) {
2425 if (!sb.append('\n'))
2426 return NULL;
2429 JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2430 if (!kidstr || !sb.append(kidstr))
2431 return NULL;
2432 ++i;
2436 if (sb.empty())
2437 return cx->runtime->emptyString;
2438 return sb.finishString();
2440 default:;
2443 /* After this point, control must flow through label out: to exit. */
2444 if (!js_EnterLocalRootScope(cx))
2445 return NULL;
2447 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2448 if (!ancestorNSes)
2449 ancestorNSes = &empty.array;
2451 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2453 JSXMLArrayCursor cursor(&xml->xml_namespaces);
2454 while ((ns = (JSObject *) cursor.getNext()) != NULL) {
2455 if (!IsDeclared(ns))
2456 continue;
2457 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2458 /* NOTE: may want to exclude unused namespaces here. */
2459 ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), JS_TRUE);
2460 if (!ns2 || !XMLARRAY_APPEND(cx, &decls.array, ns2))
2461 goto out;
2467 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2468 * not own its member references. In the spec, ancdecls has no name, but
2469 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2472 if (!ancdecls.array.setCapacity(cx, ancestorNSes->length + decls.length()))
2473 goto out;
2474 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2475 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2476 if (!ns2)
2477 continue;
2478 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls.array, ns2, namespace_identity));
2479 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2480 goto out;
2482 for (i = 0, n = decls.length(); i < n; i++) {
2483 ns2 = XMLARRAY_MEMBER(&decls.array, i, JSObject);
2484 if (!ns2)
2485 continue;
2486 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls.array, ns2, namespace_identity));
2487 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2488 goto out;
2491 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2492 ns = GetNamespace(cx, xml->name, &ancdecls.array);
2493 if (!ns)
2494 goto out;
2496 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2497 prefix = ns->getNamePrefix();
2498 if (!prefix) {
2500 * Create a namespace prefix that isn't used by any member of decls.
2501 * Assign the new prefix to a copy of ns. Flag this namespace as if
2502 * it were declared, for assertion-testing's sake later below.
2504 * Erratum: if prefix and xml->name are both null (*undefined* in
2505 * ECMA-357), we know that xml was named using the default namespace
2506 * (proof: see GetNamespace and the Namespace constructor called with
2507 * two arguments). So we ought not generate a new prefix here, when
2508 * we can declare ns as the default namespace for xml.
2510 * This helps descendants inherit the namespace instead of redundantly
2511 * redeclaring it with generated prefixes in each descendant.
2513 nsuri = ns->getNameURI();
2514 if (!xml->name->getNamePrefix()) {
2515 prefix = cx->runtime->emptyString;
2516 } else {
2517 prefix = GeneratePrefix(cx, nsuri, &ancdecls.array);
2518 if (!prefix)
2519 goto out;
2521 ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2522 if (!ns)
2523 goto out;
2526 * If the xml->name was unprefixed, we must remove any declared default
2527 * namespace from decls before appending ns. How can you get a default
2528 * namespace in decls that doesn't match the one from name? Apparently
2529 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2530 * to fix this is to update x's in-scope namespaces when setNamespace
2531 * is called, but that's not specified by ECMA-357.
2533 * Likely Erratum here, depending on whether the lack of update to x's
2534 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2535 * erratum or not. Note that changing setNamespace to update the list
2536 * of in-scope namespaces will change x.namespaceDeclarations().
2538 if (prefix->empty()) {
2539 i = XMLArrayFindMember(&decls.array, ns, namespace_match);
2540 if (i != XML_NOT_FOUND)
2541 XMLArrayDelete(cx, &decls.array, i, JS_TRUE);
2545 * In the spec, ancdecls has no name, but is always written out as
2546 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2547 * that union in ancdecls, any time we append a namespace strong
2548 * ref to decls, we must also append a weak ref to ancdecls. Order
2549 * matters here: code at label out: releases strong refs in decls.
2551 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns) ||
2552 !XMLARRAY_APPEND(cx, &decls.array, ns)) {
2553 goto out;
2557 /* Format the element or point-tag into sb. */
2558 if (!sb.append('<'))
2559 goto out;
2561 if (!prefix->empty()) {
2562 if (!sb.append(prefix) || !sb.append(':'))
2563 goto out;
2565 if (!sb.append(xml->name->getQNameLocalName()))
2566 goto out;
2569 * Step 16 makes a union to avoid writing two loops in step 17, to share
2570 * common attribute value appending spec-code. We prefer two loops for
2571 * faster code and less data overhead.
2574 /* Step 17(b): append attributes. */
2576 JSXMLArrayCursor cursor(&xml->xml_attrs);
2577 while (JSXML *attr = (JSXML *) cursor.getNext()) {
2578 if (!sb.append(' '))
2579 goto out;
2580 ns2 = GetNamespace(cx, attr->name, &ancdecls.array);
2581 if (!ns2)
2582 goto out;
2584 /* 17(b)(ii): NULL means *undefined* here. */
2585 prefix = ns2->getNamePrefix();
2586 if (!prefix) {
2587 prefix = GeneratePrefix(cx, ns2->getNameURI(), &ancdecls.array);
2588 if (!prefix)
2589 goto out;
2591 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2592 ns2 = NewXMLNamespace(cx, prefix, ns2->getNameURI(), JS_TRUE);
2593 if (!ns2)
2594 goto out;
2597 * In the spec, ancdecls has no name, but is always written out as
2598 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2599 * that union in ancdecls, any time we append a namespace strong
2600 * ref to decls, we must also append a weak ref to ancdecls. Order
2601 * matters here: code at label out: releases strong refs in decls.
2603 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2) ||
2604 !XMLARRAY_APPEND(cx, &decls.array, ns2)) {
2605 goto out;
2609 /* 17(b)(iii). */
2610 if (!prefix->empty()) {
2611 if (!sb.append(prefix) || !sb.append(':'))
2612 goto out;
2615 /* 17(b)(iv). */
2616 if (!sb.append(attr->name->getQNameLocalName()))
2617 goto out;
2619 /* 17(d-g). */
2620 if (!AppendAttributeValue(cx, sb, attr->xml_value))
2621 goto out;
2625 /* Step 17(c): append XML namespace declarations. */
2627 JSXMLArrayCursor cursor(&decls.array);
2628 while (JSObject *ns3 = (JSObject *) cursor.getNext()) {
2629 JS_ASSERT(IsDeclared(ns3));
2631 if (!sb.append(" xmlns"))
2632 goto out;
2634 /* 17(c)(ii): NULL means *undefined* here. */
2635 prefix = ns3->getNamePrefix();
2636 if (!prefix) {
2637 prefix = GeneratePrefix(cx, ns3->getNameURI(), &ancdecls.array);
2638 if (!prefix)
2639 goto out;
2640 ns3->setNamePrefix(prefix);
2643 /* 17(c)(iii). */
2644 if (!prefix->empty()) {
2645 if (!sb.append(':') || !sb.append(prefix))
2646 goto out;
2649 /* 17(d-g). */
2650 if (!AppendAttributeValue(cx, sb, ns3->getNameURI()))
2651 goto out;
2655 /* Step 18: handle point tags. */
2656 n = xml->xml_kids.length;
2657 if (n == 0) {
2658 if (!sb.append("/>"))
2659 goto out;
2660 } else {
2661 /* Steps 19 through 25: handle element content, and open the end-tag. */
2662 if (!sb.append('>'))
2663 goto out;
2665 JSXML *kid;
2666 indentKids = n > 1 ||
2667 (n == 1 &&
2668 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2669 kid->xml_class != JSXML_CLASS_TEXT);
2672 if (pretty && indentKids) {
2673 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2674 goto out;
2675 nextIndentLevel = indentLevel + i;
2676 } else {
2677 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2681 JSXMLArrayCursor cursor(&xml->xml_kids);
2682 while (JSXML *kid = (JSXML *) cursor.getNext()) {
2683 if (pretty && indentKids) {
2684 if (!sb.append('\n'))
2685 goto out;
2688 JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls.array, nextIndentLevel);
2689 if (!kidstr)
2690 goto out;
2692 if (!sb.append(kidstr))
2693 goto out;
2697 if (pretty && indentKids) {
2698 if (!sb.append('\n') ||
2699 !sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2700 goto out;
2702 if (!sb.append("</"))
2703 goto out;
2705 /* Step 26. */
2706 prefix = ns->getNamePrefix();
2707 if (prefix && !prefix->empty()) {
2708 if (!sb.append(prefix) || !sb.append(':'))
2709 goto out;
2712 /* Step 27. */
2713 if (!sb.append(xml->name->getQNameLocalName()) || !sb.append('>'))
2714 goto out;
2717 str = sb.finishString();
2718 out:
2719 js_LeaveLocalRootScopeWithResult(cx, str);
2720 return str;
2723 /* ECMA-357 10.2 */
2724 static JSString *
2725 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
2727 JSObject *obj;
2728 JSString *str;
2729 JSXML *xml;
2731 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2732 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2733 JSMSG_BAD_XML_CONVERSION,
2734 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2735 return NULL;
2738 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2739 return js_ValueToString(cx, Valueify(v));
2741 if (JSVAL_IS_STRING(v)) {
2742 StringBuffer sb(cx);
2743 return EscapeElementValue(cx, sb, JSVAL_TO_STRING(v), toSourceFlag);
2746 obj = JSVAL_TO_OBJECT(v);
2747 if (!obj->isXML()) {
2748 if (!DefaultValue(cx, obj, JSTYPE_STRING, Valueify(&v)))
2749 return NULL;
2750 str = js_ValueToString(cx, Valueify(v));
2751 if (!str)
2752 return NULL;
2753 StringBuffer sb(cx);
2754 return EscapeElementValue(cx, sb, str, toSourceFlag);
2757 /* Handle non-element cases in this switch, returning from each case. */
2758 xml = (JSXML *) obj->getPrivate();
2759 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
2762 static JSObject *
2763 ToAttributeName(JSContext *cx, jsval v)
2765 JSLinearString *name, *uri, *prefix;
2766 JSObject *obj;
2767 Class *clasp;
2768 JSObject *qn;
2770 if (JSVAL_IS_STRING(v)) {
2771 name = JSVAL_TO_STRING(v)->ensureLinear(cx);
2772 if (!name)
2773 return NULL;
2774 uri = prefix = cx->runtime->emptyString;
2775 } else {
2776 if (JSVAL_IS_PRIMITIVE(v)) {
2777 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2778 JSDVG_IGNORE_STACK, Valueify(v), NULL);
2779 return NULL;
2782 obj = JSVAL_TO_OBJECT(v);
2783 clasp = obj->getClass();
2784 if (clasp == &js_AttributeNameClass)
2785 return obj;
2787 if (clasp == &js_QNameClass) {
2788 qn = obj;
2789 uri = qn->getNameURI();
2790 prefix = qn->getNamePrefix();
2791 name = qn->getQNameLocalName();
2792 } else {
2793 if (clasp == &js_AnyNameClass) {
2794 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2795 } else {
2796 JSString *str = js_ValueToString(cx, Valueify(v));
2797 if (!str)
2798 return NULL;
2799 name = str->ensureLinear(cx);
2800 if (!name)
2801 return NULL;
2803 uri = prefix = cx->runtime->emptyString;
2807 qn = NewXMLAttributeName(cx, uri, prefix, name);
2808 if (!qn)
2809 return NULL;
2810 return qn;
2813 static void
2814 ReportBadXMLName(JSContext *cx, const Value &idval)
2816 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL);
2819 static JSBool
2820 IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
2822 JSAtom *atom;
2823 JSLinearString *uri;
2825 atom = cx->runtime->atomState.functionNamespaceURIAtom;
2826 uri = qn->getNameURI();
2827 if (uri &&
2828 (uri == ATOM_TO_STRING(atom) ||
2829 EqualStrings(uri, ATOM_TO_STRING(atom)))) {
2830 return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp);
2832 *funidp = JSID_VOID;
2833 return JS_TRUE;
2836 JSBool
2837 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
2839 if (obj->getClass() == &js_QNameClass)
2840 return IsFunctionQName(cx, obj, funidp);
2841 *funidp = JSID_VOID;
2842 return JS_TRUE;
2845 static JSObject *
2846 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
2848 JSAtom *atomizedName;
2849 JSString *name;
2850 JSObject *obj;
2851 Class *clasp;
2852 uint32 index;
2854 if (JSVAL_IS_STRING(v)) {
2855 name = JSVAL_TO_STRING(v);
2856 } else {
2857 if (JSVAL_IS_PRIMITIVE(v)) {
2858 ReportBadXMLName(cx, Valueify(v));
2859 return NULL;
2862 obj = JSVAL_TO_OBJECT(v);
2863 clasp = obj->getClass();
2864 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass)
2865 goto out;
2866 if (clasp == &js_AnyNameClass) {
2867 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2868 goto construct;
2870 name = js_ValueToString(cx, Valueify(v));
2871 if (!name)
2872 return NULL;
2875 atomizedName = js_AtomizeString(cx, name, 0);
2876 if (!atomizedName)
2877 return NULL;
2880 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
2882 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
2884 * First, _P_ should be _s_, to refer to the given string.
2886 * Second, why does ToXMLName applied to the string type throw TypeError
2887 * only for numeric literals without any leading or trailing whitespace?
2889 * If the idea is to reject uint32 property names, then the check needs to
2890 * be stricter, to exclude hexadecimal and floating point literals.
2892 if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index))
2893 goto bad;
2895 if (*atomizedName->chars() == '@') {
2896 name = js_NewDependentString(cx, name, 1, name->length() - 1);
2897 if (!name)
2898 return NULL;
2899 *funidp = JSID_VOID;
2900 return ToAttributeName(cx, STRING_TO_JSVAL(name));
2903 construct:
2904 v = STRING_TO_JSVAL(name);
2905 obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v));
2906 if (!obj)
2907 return NULL;
2909 out:
2910 if (!IsFunctionQName(cx, obj, funidp))
2911 return NULL;
2912 return obj;
2914 bad:
2915 JSAutoByteString bytes;
2916 if (js_ValueToPrintable(cx, StringValue(name), &bytes))
2917 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, bytes.ptr());
2918 return NULL;
2921 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
2922 static JSBool
2923 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
2925 JSLinearString *prefix, *prefix2;
2926 JSObject *match, *ns2;
2927 uint32 i, n, m;
2929 if (xml->xml_class != JSXML_CLASS_ELEMENT)
2930 return JS_TRUE;
2932 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
2933 prefix = ns->getNamePrefix();
2934 if (!prefix) {
2935 match = NULL;
2936 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2937 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2938 if (ns2 && EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
2939 match = ns2;
2940 break;
2943 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
2944 return JS_FALSE;
2945 } else {
2946 if (prefix->empty() && xml->name->getNameURI()->empty())
2947 return JS_TRUE;
2948 match = NULL;
2949 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2950 m = XML_NOT_FOUND;
2951 #endif
2952 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2953 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2954 if (ns2 && (prefix2 = ns2->getNamePrefix()) &&
2955 EqualStrings(prefix2, prefix)) {
2956 match = ns2;
2957 m = i;
2958 break;
2961 if (match && !EqualStrings(match->getNameURI(), ns->getNameURI())) {
2962 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
2963 JSObject);
2964 JS_ASSERT(ns2 == match);
2965 match->clearNamePrefix();
2966 if (!AddInScopeNamespace(cx, xml, match))
2967 return JS_FALSE;
2969 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
2970 return JS_FALSE;
2973 /* OPTION: enforce that descendants have superset namespaces. */
2974 return JS_TRUE;
2977 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
2978 static JSBool
2979 Append(JSContext *cx, JSXML *list, JSXML *xml)
2981 uint32 i, j, k, n;
2982 JSXML *kid;
2984 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
2985 i = list->xml_kids.length;
2986 n = 1;
2987 if (xml->xml_class == JSXML_CLASS_LIST) {
2988 list->xml_target = xml->xml_target;
2989 list->xml_targetprop = xml->xml_targetprop;
2990 n = JSXML_LENGTH(xml);
2991 k = i + n;
2992 if (!list->xml_kids.setCapacity(cx, k))
2993 return JS_FALSE;
2994 for (j = 0; j < n; j++) {
2995 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
2996 if (kid)
2997 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
2999 return JS_TRUE;
3002 list->xml_target = xml->parent;
3003 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3004 list->xml_targetprop = NULL;
3005 else
3006 list->xml_targetprop = xml->name;
3007 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3008 return JS_FALSE;
3009 return JS_TRUE;
3012 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3013 static JSXML *
3014 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3016 static JSXML *
3017 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3019 JSXML *copy;
3021 /* Our caller may not be protecting newborns with a local root scope. */
3022 if (!js_EnterLocalRootScope(cx))
3023 return NULL;
3024 copy = DeepCopyInLRS(cx, xml, flags);
3025 if (copy) {
3026 if (obj) {
3027 /* Caller provided the object for this copy, hook 'em up. */
3028 obj->setPrivate(copy);
3029 copy->object = obj;
3030 } else if (!js_GetXMLObject(cx, copy)) {
3031 copy = NULL;
3034 js_LeaveLocalRootScopeWithResult(cx, copy);
3035 return copy;
3039 * (i) We must be in a local root scope (InLRS).
3040 * (ii) parent must have a rooted object.
3041 * (iii) from's owning object must be locked if not thread-local.
3043 static JSBool
3044 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3045 uintN flags)
3047 uint32 j, n;
3048 JSXML *kid2;
3049 JSString *str;
3051 n = from->length;
3052 if (!to->setCapacity(cx, n))
3053 return JS_FALSE;
3055 JSXMLArrayCursor cursor(from);
3056 j = 0;
3057 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3058 if ((flags & XSF_IGNORE_COMMENTS) &&
3059 kid->xml_class == JSXML_CLASS_COMMENT) {
3060 continue;
3062 if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3063 kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3064 continue;
3066 if ((flags & XSF_IGNORE_WHITESPACE) &&
3067 (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3068 continue;
3070 kid2 = DeepCopyInLRS(cx, kid, flags);
3071 if (!kid2) {
3072 to->length = j;
3073 return JS_FALSE;
3076 if ((flags & XSF_IGNORE_WHITESPACE) &&
3077 n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3078 str = ChompXMLWhitespace(cx, kid2->xml_value);
3079 if (!str) {
3080 to->length = j;
3081 return JS_FALSE;
3083 kid2->xml_value = str;
3086 XMLARRAY_SET_MEMBER(to, j, kid2);
3087 ++j;
3088 if (parent->xml_class != JSXML_CLASS_LIST)
3089 kid2->parent = parent;
3092 if (j < n)
3093 to->trim();
3094 return JS_TRUE;
3097 static JSXML *
3098 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
3100 JSXML *copy;
3101 JSObject *qn;
3102 JSBool ok;
3103 uint32 i, n;
3104 JSObject *ns, *ns2;
3106 JS_CHECK_RECURSION(cx, return NULL);
3108 copy = js_NewXML(cx, JSXMLClass(xml->xml_class));
3109 if (!copy)
3110 return NULL;
3111 qn = xml->name;
3112 if (qn) {
3113 qn = NewXMLQName(cx, qn->getNameURI(), qn->getNamePrefix(), qn->getQNameLocalName());
3114 if (!qn) {
3115 ok = JS_FALSE;
3116 goto out;
3119 copy->name = qn;
3120 copy->xml_flags = xml->xml_flags;
3122 if (JSXML_HAS_VALUE(xml)) {
3123 copy->xml_value = xml->xml_value;
3124 ok = JS_TRUE;
3125 } else {
3126 ok = DeepCopySetInLRS(cx, &xml->xml_kids, &copy->xml_kids, copy, flags);
3127 if (!ok)
3128 goto out;
3130 if (xml->xml_class == JSXML_CLASS_LIST) {
3131 copy->xml_target = xml->xml_target;
3132 copy->xml_targetprop = xml->xml_targetprop;
3133 } else {
3134 n = xml->xml_namespaces.length;
3135 ok = copy->xml_namespaces.setCapacity(cx, n);
3136 if (!ok)
3137 goto out;
3138 for (i = 0; i < n; i++) {
3139 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3140 if (!ns)
3141 continue;
3142 ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(),
3143 IsDeclared(ns));
3144 if (!ns2) {
3145 copy->xml_namespaces.length = i;
3146 ok = JS_FALSE;
3147 goto out;
3149 XMLARRAY_SET_MEMBER(&copy->xml_namespaces, i, ns2);
3152 ok = DeepCopySetInLRS(cx, &xml->xml_attrs, &copy->xml_attrs, copy,
3154 if (!ok)
3155 goto out;
3159 out:
3160 if (!ok)
3161 return NULL;
3162 return copy;
3165 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3166 static void
3167 DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index)
3169 JSXML *kid;
3171 if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3172 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3173 if (kid)
3174 kid->parent = NULL;
3175 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3179 typedef JSBool (*JSXMLNameMatcher)(JSObject *nameqn, JSXML *xml);
3181 static JSBool
3182 MatchAttrName(JSObject *nameqn, JSXML *attr)
3184 JSObject *attrqn = attr->name;
3185 JSLinearString *localName = nameqn->getQNameLocalName();
3186 JSLinearString *uri;
3188 return (IS_STAR(localName) ||
3189 EqualStrings(attrqn->getQNameLocalName(), localName)) &&
3190 (!(uri = nameqn->getNameURI()) ||
3191 EqualStrings(attrqn->getNameURI(), uri));
3194 static JSBool
3195 MatchElemName(JSObject *nameqn, JSXML *elem)
3197 JSLinearString *localName = nameqn->getQNameLocalName();
3198 JSLinearString *uri;
3200 return (IS_STAR(localName) ||
3201 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3202 EqualStrings(elem->name->getQNameLocalName(), localName))) &&
3203 (!(uri = nameqn->getNameURI()) ||
3204 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3205 EqualStrings(elem->name->getNameURI(), uri)));
3208 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3209 static JSBool
3210 DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list)
3212 uint32 i, n;
3213 JSXML *attr, *kid;
3215 JS_CHECK_RECURSION(cx, return JS_FALSE);
3217 if (xml->xml_class == JSXML_CLASS_ELEMENT &&
3218 nameqn->getClass() == &js_AttributeNameClass) {
3219 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
3220 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3221 if (attr && MatchAttrName(nameqn, attr)) {
3222 if (!Append(cx, list, attr))
3223 return JS_FALSE;
3228 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3229 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3230 if (!kid)
3231 continue;
3232 if (nameqn->getClass() != &js_AttributeNameClass &&
3233 MatchElemName(nameqn, kid)) {
3234 if (!Append(cx, list, kid))
3235 return JS_FALSE;
3237 if (!DescendantsHelper(cx, kid, nameqn, list))
3238 return JS_FALSE;
3240 return JS_TRUE;
3243 static JSXML *
3244 Descendants(JSContext *cx, JSXML *xml, jsval id)
3246 jsid funid;
3247 JSObject *nameqn;
3248 JSObject *listobj;
3249 JSXML *list, *kid;
3250 uint32 i, n;
3251 JSBool ok;
3253 nameqn = ToXMLName(cx, id, &funid);
3254 if (!nameqn)
3255 return NULL;
3257 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3258 if (!listobj)
3259 return NULL;
3260 list = (JSXML *) listobj->getPrivate();
3261 if (!JSID_IS_VOID(funid))
3262 return list;
3265 * Protect nameqn's object and strings from GC by linking list to it
3266 * temporarily. The newborn GC root for the last allocated object
3267 * protects listobj, which protects list. Any other object allocations
3268 * occurring beneath DescendantsHelper use local roots.
3270 list->name = nameqn;
3271 if (!js_EnterLocalRootScope(cx))
3272 return NULL;
3273 if (xml->xml_class == JSXML_CLASS_LIST) {
3274 ok = JS_TRUE;
3275 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
3276 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3277 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
3278 ok = DescendantsHelper(cx, kid, nameqn, list);
3279 if (!ok)
3280 break;
3283 } else {
3284 ok = DescendantsHelper(cx, xml, nameqn, list);
3286 js_LeaveLocalRootScopeWithResult(cx, list);
3287 if (!ok)
3288 return NULL;
3289 list->name = NULL;
3290 return list;
3293 /* Recursive (JSXML *) parameterized version of Equals. */
3294 static JSBool
3295 XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3297 JSObject *qn, *vqn;
3298 uint32 i, j, n;
3299 JSXML *kid, *vkid, *attr, *vattr;
3300 JSObject *xobj, *vobj;
3302 retry:
3303 if (xml->xml_class != vxml->xml_class) {
3304 if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
3305 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3306 if (xml)
3307 goto retry;
3309 if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3310 vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3311 if (vxml)
3312 goto retry;
3314 *bp = JS_FALSE;
3315 return JS_TRUE;
3318 qn = xml->name;
3319 vqn = vxml->name;
3320 if (qn) {
3321 *bp = vqn &&
3322 EqualStrings(qn->getQNameLocalName(), vqn->getQNameLocalName()) &&
3323 EqualStrings(qn->getNameURI(), vqn->getNameURI());
3324 } else {
3325 *bp = vqn == NULL;
3327 if (!*bp)
3328 return JS_TRUE;
3330 if (JSXML_HAS_VALUE(xml)) {
3331 if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, bp))
3332 return JS_FALSE;
3333 } else if (xml->xml_kids.length != vxml->xml_kids.length) {
3334 *bp = JS_FALSE;
3335 } else {
3337 JSXMLArrayCursor cursor(&xml->xml_kids);
3338 JSXMLArrayCursor vcursor(&vxml->xml_kids);
3339 for (;;) {
3340 kid = (JSXML *) cursor.getNext();
3341 vkid = (JSXML *) vcursor.getNext();
3342 if (!kid || !vkid) {
3343 *bp = !kid && !vkid;
3344 break;
3346 xobj = js_GetXMLObject(cx, kid);
3347 vobj = js_GetXMLObject(cx, vkid);
3348 if (!xobj || !vobj ||
3349 !js_TestXMLEquality(cx, ObjectValue(*xobj), ObjectValue(*vobj), bp))
3350 return JS_FALSE;
3351 if (!*bp)
3352 break;
3356 if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3357 n = xml->xml_attrs.length;
3358 if (n != vxml->xml_attrs.length)
3359 *bp = JS_FALSE;
3360 for (i = 0; *bp && i < n; i++) {
3361 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3362 if (!attr)
3363 continue;
3364 j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3365 if (j == XML_NOT_FOUND) {
3366 *bp = JS_FALSE;
3367 break;
3369 vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3370 if (!vattr)
3371 continue;
3372 if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, bp))
3373 return JS_FALSE;
3378 return JS_TRUE;
3381 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3382 static JSBool
3383 Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3385 JSObject *vobj;
3386 JSXML *vxml;
3388 if (JSVAL_IS_PRIMITIVE(v)) {
3389 *bp = JS_FALSE;
3390 if (xml->xml_class == JSXML_CLASS_LIST) {
3391 if (xml->xml_kids.length == 1) {
3392 vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3393 if (!vxml)
3394 return JS_TRUE;
3395 vobj = js_GetXMLObject(cx, vxml);
3396 if (!vobj)
3397 return JS_FALSE;
3398 return js_TestXMLEquality(cx, ObjectValue(*vobj), Valueify(v), bp);
3400 if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3401 *bp = JS_TRUE;
3403 } else {
3404 vobj = JSVAL_TO_OBJECT(v);
3405 if (!vobj->isXML()) {
3406 *bp = JS_FALSE;
3407 } else {
3408 vxml = (JSXML *) vobj->getPrivate();
3409 if (!XMLEquals(cx, xml, vxml, bp))
3410 return JS_FALSE;
3413 return JS_TRUE;
3416 static JSBool
3417 CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3419 JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3421 do {
3422 if (xml == kid) {
3423 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3424 JSMSG_CYCLIC_VALUE, js_XML_str);
3425 return JS_FALSE;
3427 } while ((xml = xml->parent) != NULL);
3429 return JS_TRUE;
3432 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3433 static JSBool
3434 Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3436 uint32 j, n;
3437 JSXML *vxml, *kid;
3438 JSObject *vobj;
3439 JSString *str;
3441 if (!JSXML_HAS_KIDS(xml))
3442 return JS_TRUE;
3444 n = 1;
3445 vxml = NULL;
3446 if (!JSVAL_IS_PRIMITIVE(v)) {
3447 vobj = JSVAL_TO_OBJECT(v);
3448 if (vobj->isXML()) {
3449 vxml = (JSXML *) vobj->getPrivate();
3450 if (vxml->xml_class == JSXML_CLASS_LIST) {
3451 n = vxml->xml_kids.length;
3452 if (n == 0)
3453 return JS_TRUE;
3454 for (j = 0; j < n; j++) {
3455 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3456 if (!kid)
3457 continue;
3458 if (!CheckCycle(cx, xml, kid))
3459 return JS_FALSE;
3461 } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3462 /* OPTION: enforce that descendants have superset namespaces. */
3463 if (!CheckCycle(cx, xml, vxml))
3464 return JS_FALSE;
3468 if (!vxml) {
3469 str = js_ValueToString(cx, Valueify(v));
3470 if (!str)
3471 return JS_FALSE;
3473 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3474 if (!vxml)
3475 return JS_FALSE;
3476 vxml->xml_value = str;
3479 if (i > xml->xml_kids.length)
3480 i = xml->xml_kids.length;
3482 if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3483 return JS_FALSE;
3485 if (vxml->xml_class == JSXML_CLASS_LIST) {
3486 for (j = 0; j < n; j++) {
3487 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3488 if (!kid)
3489 continue;
3490 kid->parent = xml;
3491 XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3493 /* OPTION: enforce that descendants have superset namespaces. */
3495 } else {
3496 vxml->parent = xml;
3497 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3499 return JS_TRUE;
3502 static JSBool
3503 IndexToId(JSContext *cx, uint32 index, jsid *idp)
3505 JSAtom *atom;
3506 JSString *str;
3508 if (index <= JSID_INT_MAX) {
3509 *idp = INT_TO_JSID(index);
3510 } else {
3511 str = js_NumberToString(cx, (jsdouble) index);
3512 if (!str)
3513 return JS_FALSE;
3514 atom = js_AtomizeString(cx, str, 0);
3515 if (!atom)
3516 return JS_FALSE;
3517 *idp = ATOM_TO_JSID(atom);
3519 return JS_TRUE;
3522 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3523 static JSBool
3524 Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3526 uint32 n;
3527 JSXML *vxml, *kid;
3528 JSObject *vobj;
3529 JSString *str;
3531 if (!JSXML_HAS_KIDS(xml))
3532 return JS_TRUE;
3535 * 9.1.1.12
3536 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3537 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3539 n = xml->xml_kids.length;
3540 if (i > n)
3541 i = n;
3543 vxml = NULL;
3544 if (!JSVAL_IS_PRIMITIVE(v)) {
3545 vobj = JSVAL_TO_OBJECT(v);
3546 if (vobj->isXML())
3547 vxml = (JSXML *) vobj->getPrivate();
3550 switch (vxml ? JSXMLClass(vxml->xml_class) : JSXML_CLASS_LIMIT) {
3551 case JSXML_CLASS_ELEMENT:
3552 /* OPTION: enforce that descendants have superset namespaces. */
3553 if (!CheckCycle(cx, xml, vxml))
3554 return JS_FALSE;
3555 case JSXML_CLASS_COMMENT:
3556 case JSXML_CLASS_PROCESSING_INSTRUCTION:
3557 case JSXML_CLASS_TEXT:
3558 goto do_replace;
3560 case JSXML_CLASS_LIST:
3561 if (i < n)
3562 DeleteByIndex(cx, xml, i);
3563 if (!Insert(cx, xml, i, v))
3564 return JS_FALSE;
3565 break;
3567 default:
3568 str = js_ValueToString(cx, Valueify(v));
3569 if (!str)
3570 return JS_FALSE;
3572 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3573 if (!vxml)
3574 return JS_FALSE;
3575 vxml->xml_value = str;
3577 do_replace:
3578 vxml->parent = xml;
3579 if (i < n) {
3580 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3581 if (kid)
3582 kid->parent = NULL;
3584 if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3585 return JS_FALSE;
3586 break;
3589 return JS_TRUE;
3592 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3593 static void
3594 DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn,
3595 JSBool attributes)
3597 JSXMLArray *array;
3598 uint32 index, deleteCount;
3599 JSXML *kid;
3600 JSXMLNameMatcher matcher;
3602 if (xml->xml_class == JSXML_CLASS_LIST) {
3603 array = &xml->xml_kids;
3604 for (index = 0; index < array->length; index++) {
3605 kid = XMLARRAY_MEMBER(array, index, JSXML);
3606 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT)
3607 DeleteNamedProperty(cx, kid, nameqn, attributes);
3609 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3610 if (attributes) {
3611 array = &xml->xml_attrs;
3612 matcher = MatchAttrName;
3613 } else {
3614 array = &xml->xml_kids;
3615 matcher = MatchElemName;
3617 deleteCount = 0;
3618 for (index = 0; index < array->length; index++) {
3619 kid = XMLARRAY_MEMBER(array, index, JSXML);
3620 if (kid && matcher(nameqn, kid)) {
3621 kid->parent = NULL;
3622 XMLArrayDelete(cx, array, index, JS_FALSE);
3623 ++deleteCount;
3624 } else if (deleteCount != 0) {
3625 XMLARRAY_SET_MEMBER(array,
3626 index - deleteCount,
3627 array->vector[index]);
3630 array->length -= deleteCount;
3634 /* ECMA-357 9.2.1.3 index case. */
3635 static void
3636 DeleteListElement(JSContext *cx, JSXML *xml, uint32 index)
3638 JSXML *kid, *parent;
3639 uint32 kidIndex;
3641 JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST);
3643 if (index < xml->xml_kids.length) {
3644 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3645 if (kid) {
3646 parent = kid->parent;
3647 if (parent) {
3648 JS_ASSERT(parent != xml);
3649 JS_ASSERT(JSXML_HAS_KIDS(parent));
3651 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
3652 DeleteNamedProperty(cx, parent, kid->name, JS_TRUE);
3653 } else {
3654 kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
3655 NULL);
3656 JS_ASSERT(kidIndex != XML_NOT_FOUND);
3657 DeleteByIndex(cx, parent, kidIndex);
3660 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3665 static JSBool
3666 SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
3668 JSXMLArray *nsarray;
3669 uint32 i, n;
3670 JSObject *ns;
3672 nsarray = &xml->xml_namespaces;
3673 while ((xml = xml->parent) != NULL) {
3674 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3675 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3676 if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
3677 if (!XMLARRAY_APPEND(cx, nsarray, ns))
3678 return JS_FALSE;
3682 return JS_TRUE;
3685 static JSBool
3686 GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list)
3688 JSXMLArray *array;
3689 JSXMLNameMatcher matcher;
3690 JSBool attrs;
3692 if (xml->xml_class == JSXML_CLASS_LIST) {
3693 JSXMLArrayCursor cursor(&xml->xml_kids);
3694 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3695 if (kid->xml_class == JSXML_CLASS_ELEMENT &&
3696 !GetNamedProperty(cx, kid, nameqn, list)) {
3697 return JS_FALSE;
3700 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3701 attrs = (nameqn->getClass() == &js_AttributeNameClass);
3702 if (attrs) {
3703 array = &xml->xml_attrs;
3704 matcher = MatchAttrName;
3705 } else {
3706 array = &xml->xml_kids;
3707 matcher = MatchElemName;
3710 JSXMLArrayCursor cursor(array);
3711 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3712 if (matcher(nameqn, kid)) {
3713 if (!attrs &&
3714 kid->xml_class == JSXML_CLASS_ELEMENT &&
3715 !SyncInScopeNamespaces(cx, kid)) {
3716 return JS_FALSE;
3718 if (!Append(cx, list, kid))
3719 return JS_FALSE;
3724 return JS_TRUE;
3727 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3728 static JSBool
3729 GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3731 JSXML *xml, *list, *kid;
3732 uint32 index;
3733 JSObject *kidobj, *listobj;
3734 JSObject *nameqn;
3735 jsid funid;
3737 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3738 if (!xml)
3739 return true;
3741 if (js_IdIsIndex(id, &index)) {
3742 if (!JSXML_HAS_KIDS(xml)) {
3743 *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
3744 } else {
3746 * ECMA-357 9.2.1.1 starts here.
3748 * Erratum: 9.2 is not completely clear that indexed properties
3749 * correspond to kids, but that's what it seems to say, and it's
3750 * what any sane user would want.
3752 if (index < xml->xml_kids.length) {
3753 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3754 if (!kid) {
3755 *vp = JSVAL_VOID;
3756 return true;
3758 kidobj = js_GetXMLObject(cx, kid);
3759 if (!kidobj)
3760 return false;
3762 *vp = OBJECT_TO_JSVAL(kidobj);
3763 } else {
3764 *vp = JSVAL_VOID;
3767 return true;
3771 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3773 nameqn = ToXMLName(cx, IdToJsval(id), &funid);
3774 if (!nameqn)
3775 return false;
3776 if (!JSID_IS_VOID(funid))
3777 return GetXMLFunction(cx, obj, funid, vp);
3779 jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL };
3780 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
3782 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3783 if (!listobj)
3784 return false;
3786 roots[1] = OBJECT_TO_JSVAL(listobj);
3788 list = (JSXML *) listobj->getPrivate();
3789 if (!GetNamedProperty(cx, xml, nameqn, list))
3790 return false;
3793 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
3794 * given list's [[TargetProperty]] to the property that is being
3795 * appended. This means that any use of the internal [[Get]]
3796 * property returns a list which, when used by e.g. [[Insert]]
3797 * duplicates the last element matched by id. See bug 336921.
3799 list->xml_target = xml;
3800 list->xml_targetprop = nameqn;
3801 *vp = OBJECT_TO_JSVAL(listobj);
3802 return true;
3805 static JSXML *
3806 CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
3808 JS_ASSERT(xml->object != obj);
3810 xml = DeepCopy(cx, xml, obj, 0);
3811 if (!xml)
3812 return NULL;
3814 JS_ASSERT(xml->object == obj);
3815 return xml;
3818 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
3819 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
3821 static JSString *
3822 KidToString(JSContext *cx, JSXML *xml, uint32 index)
3824 JSXML *kid;
3825 JSObject *kidobj;
3827 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3828 if (!kid)
3829 return cx->runtime->emptyString;
3830 kidobj = js_GetXMLObject(cx, kid);
3831 if (!kidobj)
3832 return NULL;
3833 return js_ValueToString(cx, ObjectValue(*kidobj));
3836 /* Forward declared -- its implementation uses other statics that call it. */
3837 static JSBool
3838 ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
3840 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
3841 static JSBool
3842 PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
3844 JSBool ok, primitiveAssign;
3845 enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
3846 JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
3847 JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
3848 JSObject *targetprop, *nameqn, *attrqn;
3849 uint32 index, i, j, k, n, q, matchIndex;
3850 jsval attrval, nsval;
3851 jsid funid;
3852 JSObject *ns;
3854 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3855 if (!xml)
3856 return JS_TRUE;
3858 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
3859 if (!xml)
3860 return JS_FALSE;
3862 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
3863 vxml = NULL;
3864 if (!JSVAL_IS_PRIMITIVE(*vp)) {
3865 vobj = JSVAL_TO_OBJECT(*vp);
3866 if (vobj->isXML())
3867 vxml = (JSXML *) vobj->getPrivate();
3870 ok = js_EnterLocalRootScope(cx);
3871 if (!ok)
3872 return JS_FALSE;
3874 MUST_FLOW_THROUGH("out");
3875 jsval roots[3];
3876 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
3877 roots[ID_ROOT] = IdToJsval(id);
3878 roots[VAL_ROOT] = *vp;
3879 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
3881 if (js_IdIsIndex(id, &index)) {
3882 if (xml->xml_class != JSXML_CLASS_LIST) {
3883 /* See NOTE in spec: this variation is reserved for future use. */
3884 ReportBadXMLName(cx, IdToValue(id));
3885 goto bad;
3889 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
3891 i = index;
3893 /* 2(a-b). */
3894 if (xml->xml_target) {
3895 ok = ResolveValue(cx, xml->xml_target, &rxml);
3896 if (!ok)
3897 goto out;
3898 if (!rxml)
3899 goto out;
3900 JS_ASSERT(rxml->object);
3901 } else {
3902 rxml = NULL;
3905 /* 2(c). */
3906 if (index >= xml->xml_kids.length) {
3907 /* 2(c)(i). */
3908 if (rxml) {
3909 if (rxml->xml_class == JSXML_CLASS_LIST) {
3910 if (rxml->xml_kids.length != 1)
3911 goto out;
3912 rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
3913 if (!rxml)
3914 goto out;
3915 ok = js_GetXMLObject(cx, rxml) != NULL;
3916 if (!ok)
3917 goto out;
3921 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
3922 * _y.[[Parent]] = r_ where _r_ is the result of
3923 * [[ResolveValue]] called on _x.[[TargetObject]] in
3924 * 2(a)(i). This can result in text parenting text:
3926 * var MYXML = new XML();
3927 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
3929 * (testcase from Werner Sharp <wsharp@macromedia.com>).
3931 * To match insertChildAfter, insertChildBefore,
3932 * prependChild, and setChildren, we should silently
3933 * do nothing in this case.
3935 if (!JSXML_HAS_KIDS(rxml))
3936 goto out;
3939 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
3940 targetprop = xml->xml_targetprop;
3941 if (!targetprop || IS_STAR(targetprop->getQNameLocalName())) {
3942 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
3943 kid = js_NewXML(cx, JSXML_CLASS_TEXT);
3944 if (!kid)
3945 goto bad;
3946 } else {
3947 nameobj = targetprop;
3948 if (nameobj->getClass() == &js_AttributeNameClass) {
3950 * 2(c)(iii)(1-3).
3951 * Note that rxml can't be null here, because target
3952 * and targetprop are non-null.
3954 ok = GetProperty(cx, rxml->object, id, &attrval);
3955 if (!ok)
3956 goto out;
3957 if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
3958 goto out;
3959 attrobj = JSVAL_TO_OBJECT(attrval);
3960 attr = (JSXML *) attrobj->getPrivate();
3961 if (JSXML_LENGTH(attr) != 0)
3962 goto out;
3964 kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
3965 } else {
3966 /* 2(c)(v). */
3967 kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
3969 if (!kid)
3970 goto bad;
3972 /* An important bit of 2(c)(ii). */
3973 kid->name = targetprop;
3976 /* Final important bit of 2(c)(ii). */
3977 kid->parent = rxml;
3979 /* 2(c)(vi-vii). */
3980 i = xml->xml_kids.length;
3981 if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
3983 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
3984 * y.[[Parent]] is here called kid->parent, which we know
3985 * from 2(c)(ii) is _r_, here called rxml. So let's just
3986 * test that! Erratum, the spec should be simpler here.
3988 if (rxml) {
3989 JS_ASSERT(JSXML_HAS_KIDS(rxml));
3990 n = rxml->xml_kids.length;
3991 j = n - 1;
3992 if (n != 0 && i != 0) {
3993 for (n = j, j = 0; j < n; j++) {
3994 if (rxml->xml_kids.vector[j] ==
3995 xml->xml_kids.vector[i-1]) {
3996 break;
4001 kidobj = js_GetXMLObject(cx, kid);
4002 if (!kidobj)
4003 goto bad;
4004 ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4005 if (!ok)
4006 goto out;
4010 * 2(c)(vii)(2-3).
4011 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4012 * typo for [[TargetProperty]].
4014 if (vxml) {
4015 kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4016 ? vxml->xml_targetprop
4017 : vxml->name;
4021 /* 2(c)(viii). */
4022 ok = Append(cx, xml, kid);
4023 if (!ok)
4024 goto out;
4027 /* 2(d). */
4028 if (!vxml ||
4029 vxml->xml_class == JSXML_CLASS_TEXT ||
4030 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4031 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4032 if (!ok)
4033 goto out;
4034 roots[VAL_ROOT] = *vp;
4037 /* 2(e). */
4038 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4039 if (!kid)
4040 goto out;
4041 parent = kid->parent;
4042 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4043 nameobj = kid->name;
4044 if (nameobj->getClass() != &js_AttributeNameClass) {
4045 nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(),
4046 nameobj->getQNameLocalName());
4047 if (!nameobj)
4048 goto bad;
4050 id = OBJECT_TO_JSID(nameobj);
4052 if (parent) {
4053 /* 2(e)(i). */
4054 parentobj = js_GetXMLObject(cx, parent);
4055 if (!parentobj)
4056 goto bad;
4057 ok = PutProperty(cx, parentobj, id, strict, vp);
4058 if (!ok)
4059 goto out;
4061 /* 2(e)(ii). */
4062 ok = GetProperty(cx, parentobj, id, vp);
4063 if (!ok)
4064 goto out;
4065 attr = (JSXML *) JSVAL_TO_OBJECT(*vp)->getPrivate();
4067 /* 2(e)(iii) - the length check comes from the bug 375406. */
4068 if (attr->xml_kids.length != 0)
4069 xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
4073 /* 2(f). */
4074 else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4076 * 2(f)(i)
4078 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4079 * if we do that we never change the parent of each child in the
4080 * list. Since [[Put]] when called on an XML object deeply copies
4081 * the provided list _V_, we also do so here. Perhaps the shallow
4082 * copy was a misguided optimization?
4084 copy = DeepCopyInLRS(cx, vxml, 0);
4085 if (!copy)
4086 goto bad;
4087 copyobj = js_GetXMLObject(cx, copy);
4088 if (!copyobj)
4089 goto bad;
4091 JS_ASSERT(parent != xml);
4092 if (parent) {
4093 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4094 JS_ASSERT(q != XML_NOT_FOUND);
4095 ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj));
4096 if (!ok)
4097 goto out;
4099 #ifdef DEBUG
4100 /* Erratum: this loop in the spec is useless. */
4101 for (j = 0, n = copy->xml_kids.length; j < n; j++) {
4102 kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
4103 JS_ASSERT(XMLARRAY_MEMBER(&copy->xml_kids, j, JSXML)
4104 == kid2);
4106 #endif
4110 * 2(f)(iv-vi).
4111 * Erratum: notice the unhandled zero-length V basis case and
4112 * the off-by-one errors for the n != 0 cases in the spec.
4114 n = copy->xml_kids.length;
4115 if (n == 0) {
4116 XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4117 } else {
4118 ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4119 if (!ok)
4120 goto out;
4122 for (j = 0; j < n; j++)
4123 xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4127 /* 2(g). */
4128 else if (vxml || JSXML_HAS_VALUE(kid)) {
4129 if (parent) {
4130 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4131 JS_ASSERT(q != XML_NOT_FOUND);
4132 ok = Replace(cx, parent, q, *vp);
4133 if (!ok)
4134 goto out;
4136 vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4137 if (!vxml)
4138 goto out;
4139 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
4143 * 2(g)(iii).
4144 * Erratum: _V_ may not be of type XML, but all index-named
4145 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4146 * according to 9.2.1.1 Overview and other places in the spec.
4148 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4149 * or an XML/XMLList object. If *vp is a string, call ToXML
4150 * on it to satisfy the constraint.
4152 if (!vxml) {
4153 JS_ASSERT(JSVAL_IS_STRING(*vp));
4154 vobj = ToXML(cx, *vp);
4155 if (!vobj)
4156 goto bad;
4157 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4158 vxml = (JSXML *) vobj->getPrivate();
4160 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4163 /* 2(h). */
4164 else {
4165 kidobj = js_GetXMLObject(cx, kid);
4166 if (!kidobj)
4167 goto bad;
4168 id = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
4169 ok = PutProperty(cx, kidobj, id, strict, vp);
4170 if (!ok)
4171 goto out;
4173 } else {
4175 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4177 nameqn = ToXMLName(cx, IdToJsval(id), &funid);
4178 if (!nameqn)
4179 goto bad;
4180 if (!JSID_IS_VOID(funid)) {
4181 ok = js_SetProperty(cx, obj, funid, Valueify(vp), false);
4182 goto out;
4184 nameobj = nameqn;
4185 roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
4187 if (xml->xml_class == JSXML_CLASS_LIST) {
4189 * Step 3 of 9.2.1.2.
4190 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4191 * or an r with r.[[Length]] != 1, throw TypeError.
4193 n = JSXML_LENGTH(xml);
4194 if (n > 1)
4195 goto type_error;
4196 if (n == 0) {
4197 ok = ResolveValue(cx, xml, &rxml);
4198 if (!ok)
4199 goto out;
4200 if (!rxml || JSXML_LENGTH(rxml) != 1)
4201 goto type_error;
4202 ok = Append(cx, xml, rxml);
4203 if (!ok)
4204 goto out;
4206 JS_ASSERT(JSXML_LENGTH(xml) == 1);
4207 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4208 if (!xml)
4209 goto out;
4210 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
4211 obj = js_GetXMLObject(cx, xml);
4212 if (!obj)
4213 goto bad;
4214 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4216 /* FALL THROUGH to non-list case */
4220 * ECMA-357 9.1.1.2.
4221 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4222 * effort in ToString or [[DeepCopy]].
4225 if (JSXML_HAS_VALUE(xml))
4226 goto out;
4228 if (!vxml ||
4229 vxml->xml_class == JSXML_CLASS_TEXT ||
4230 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4231 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4232 if (!ok)
4233 goto out;
4234 } else {
4235 rxml = DeepCopyInLRS(cx, vxml, 0);
4236 if (!rxml || !js_GetXMLObject(cx, rxml))
4237 goto bad;
4238 vxml = rxml;
4239 *vp = OBJECT_TO_JSVAL(vxml->object);
4241 roots[VAL_ROOT] = *vp;
4244 * 6.
4245 * Erratum: why is this done here, so early? use is way later....
4247 ok = js_GetDefaultXMLNamespace(cx, &nsval);
4248 if (!ok)
4249 goto out;
4251 if (nameobj->getClass() == &js_AttributeNameClass) {
4252 /* 7(a). */
4253 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4254 goto out;
4256 /* 7(b-c). */
4257 if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4258 n = vxml->xml_kids.length;
4259 if (n == 0) {
4260 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4261 } else {
4262 JSString *left = KidToString(cx, vxml, 0);
4263 if (!left)
4264 goto bad;
4266 JSString *space = cx->runtime->atomState.spaceAtom;
4267 for (i = 1; i < n; i++) {
4268 left = js_ConcatStrings(cx, left, space);
4269 if (!left)
4270 goto bad;
4271 JSString *right = KidToString(cx, vxml, i);
4272 if (!right)
4273 goto bad;
4274 left = js_ConcatStrings(cx, left, right);
4275 if (!left)
4276 goto bad;
4279 roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4281 } else {
4282 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4283 if (!ok)
4284 goto out;
4285 roots[VAL_ROOT] = *vp;
4288 /* 7(d-e). */
4289 match = NULL;
4290 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4291 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4292 if (!attr)
4293 continue;
4294 attrqn = attr->name;
4295 if (EqualStrings(attrqn->getQNameLocalName(), nameqn->getQNameLocalName())) {
4296 JSLinearString *uri = nameqn->getNameURI();
4297 if (!uri || EqualStrings(attrqn->getNameURI(), uri)) {
4298 if (!match) {
4299 match = attr;
4300 } else {
4301 DeleteNamedProperty(cx, xml, attrqn, JS_TRUE);
4302 --i;
4308 /* 7(f). */
4309 attr = match;
4310 if (!attr) {
4311 /* 7(f)(i-ii). */
4312 JSLinearString *uri = nameqn->getNameURI();
4313 JSLinearString *left, *right;
4314 if (!uri) {
4315 left = right = cx->runtime->emptyString;
4316 } else {
4317 left = uri;
4318 right = nameqn->getNamePrefix();
4320 nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4321 if (!nameqn)
4322 goto bad;
4324 /* 7(f)(iii). */
4325 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4326 if (!attr)
4327 goto bad;
4328 attr->parent = xml;
4329 attr->name = nameqn;
4331 /* 7(f)(iv). */
4332 ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4333 if (!ok)
4334 goto out;
4336 /* 7(f)(v-vi). */
4337 ns = GetNamespace(cx, nameqn, NULL);
4338 if (!ns)
4339 goto bad;
4340 ok = AddInScopeNamespace(cx, xml, ns);
4341 if (!ok)
4342 goto out;
4345 /* 7(g). */
4346 attr->xml_value = JSVAL_TO_STRING(*vp);
4347 goto out;
4350 /* 8-9. */
4351 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4352 !IS_STAR(nameqn->getQNameLocalName())) {
4353 goto out;
4356 /* 10-11. */
4357 id = JSID_VOID;
4358 primitiveAssign = !vxml && !IS_STAR(nameqn->getQNameLocalName());
4360 /* 12. */
4361 k = n = xml->xml_kids.length;
4362 matchIndex = XML_NOT_FOUND;
4363 kid2 = NULL;
4364 while (k != 0) {
4365 --k;
4366 kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
4367 if (kid && MatchElemName(nameqn, kid)) {
4368 if (matchIndex != XML_NOT_FOUND)
4369 DeleteByIndex(cx, xml, matchIndex);
4370 matchIndex = k;
4371 kid2 = kid;
4376 * Erratum: ECMA-357 specified child insertion inconsistently:
4377 * insertChildBefore and insertChildAfter insert an arbitrary XML
4378 * instance, and therefore can create cycles, but appendChild as
4379 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4380 * its argument. But the "Semantics" in 13.4.4.3 do not include
4381 * any [[DeepCopy]] call.
4383 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4384 * required adding cycle detection, and allowing duplicate kids to
4385 * be created (see comment 6 in the bug). Allowing duplicate kid
4386 * references means the loop above will delete all but the lowest
4387 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4388 * parent. Thus the need to restore parent here. This is covered
4389 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4391 if (kid2) {
4392 JS_ASSERT(kid2->parent == xml || !kid2->parent);
4393 if (!kid2->parent)
4394 kid2->parent = xml;
4397 /* 13. */
4398 if (matchIndex == XML_NOT_FOUND) {
4399 /* 13(a). */
4400 matchIndex = n;
4402 /* 13(b). */
4403 if (primitiveAssign) {
4404 JSLinearString *uri = nameqn->getNameURI();
4405 JSLinearString *left, *right;
4406 if (!uri) {
4407 ns = JSVAL_TO_OBJECT(nsval);
4408 left = ns->getNameURI();
4409 right = ns->getNamePrefix();
4410 } else {
4411 left = uri;
4412 right = nameqn->getNamePrefix();
4414 nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4415 if (!nameqn)
4416 goto bad;
4418 /* 13(b)(iii). */
4419 vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4420 if (!vobj)
4421 goto bad;
4422 vxml = (JSXML *) vobj->getPrivate();
4423 vxml->parent = xml;
4424 vxml->name = nameqn;
4426 /* 13(b)(iv-vi). */
4427 ns = GetNamespace(cx, nameqn, NULL);
4428 if (!ns)
4429 goto bad;
4430 ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj));
4431 if (!ok)
4432 goto out;
4433 ok = AddInScopeNamespace(cx, vxml, ns);
4434 if (!ok)
4435 goto out;
4439 /* 14. */
4440 if (primitiveAssign) {
4441 JSXMLArrayCursor cursor(&xml->xml_kids);
4442 cursor.index = matchIndex;
4443 kid = (JSXML *) cursor.getCurrent();
4444 if (JSXML_HAS_KIDS(kid)) {
4445 kid->xml_kids.finish(cx);
4446 kid->xml_kids.init();
4447 ok = kid->xml_kids.setCapacity(cx, 1);
4450 /* 14(b-c). */
4451 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4452 if (ok) {
4453 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4454 if (ok && !JSVAL_TO_STRING(*vp)->empty()) {
4455 roots[VAL_ROOT] = *vp;
4456 if ((JSXML *) cursor.getCurrent() == kid)
4457 ok = Replace(cx, kid, 0, *vp);
4460 } else {
4461 /* 15(a). */
4462 ok = Replace(cx, xml, matchIndex, *vp);
4466 out:
4467 js_LeaveLocalRootScope(cx);
4468 return ok;
4470 type_error:
4472 JSAutoByteString bytes;
4473 if (js_ValueToPrintable(cx, IdToValue(id), &bytes))
4474 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XMLLIST_PUT, bytes.ptr());
4476 bad:
4477 ok = JS_FALSE;
4478 goto out;
4481 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4482 static JSBool
4483 ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4485 JSXML *target, *base;
4486 JSObject *targetprop;
4487 jsid id;
4488 jsval tv;
4490 if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4491 if (!js_GetXMLObject(cx, list))
4492 return JS_FALSE;
4493 *result = list;
4494 return JS_TRUE;
4497 target = list->xml_target;
4498 targetprop = list->xml_targetprop;
4499 if (!target || !targetprop || IS_STAR(targetprop->getQNameLocalName())) {
4500 *result = NULL;
4501 return JS_TRUE;
4504 if (targetprop->getClass() == &js_AttributeNameClass) {
4505 *result = NULL;
4506 return JS_TRUE;
4509 if (!ResolveValue(cx, target, &base))
4510 return JS_FALSE;
4511 if (!base) {
4512 *result = NULL;
4513 return JS_TRUE;
4515 if (!js_GetXMLObject(cx, base))
4516 return JS_FALSE;
4518 id = OBJECT_TO_JSID(targetprop);
4519 if (!GetProperty(cx, base->object, id, &tv))
4520 return JS_FALSE;
4521 target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4523 if (JSXML_LENGTH(target) == 0) {
4524 if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4525 *result = NULL;
4526 return JS_TRUE;
4528 tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4529 if (!PutProperty(cx, base->object, id, false, &tv))
4530 return JS_FALSE;
4531 if (!GetProperty(cx, base->object, id, &tv))
4532 return JS_FALSE;
4533 target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4536 *result = target;
4537 return JS_TRUE;
4540 static JSBool
4541 HasNamedProperty(JSXML *xml, JSObject *nameqn)
4543 JSBool found;
4544 JSXMLArray *array;
4545 JSXMLNameMatcher matcher;
4546 uint32 i, n;
4548 if (xml->xml_class == JSXML_CLASS_LIST) {
4549 found = JS_FALSE;
4550 JSXMLArrayCursor cursor(&xml->xml_kids);
4551 while (JSXML *kid = (JSXML *) cursor.getNext()) {
4552 found = HasNamedProperty(kid, nameqn);
4553 if (found)
4554 break;
4556 return found;
4559 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4560 if (nameqn->getClass() == &js_AttributeNameClass) {
4561 array = &xml->xml_attrs;
4562 matcher = MatchAttrName;
4563 } else {
4564 array = &xml->xml_kids;
4565 matcher = MatchElemName;
4567 for (i = 0, n = array->length; i < n; i++) {
4568 JSXML *kid = XMLARRAY_MEMBER(array, i, JSXML);
4569 if (kid && matcher(nameqn, kid))
4570 return JS_TRUE;
4574 return JS_FALSE;
4577 static JSBool
4578 HasIndexedProperty(JSXML *xml, uint32 i)
4580 if (xml->xml_class == JSXML_CLASS_LIST)
4581 return i < JSXML_LENGTH(xml);
4583 if (xml->xml_class == JSXML_CLASS_ELEMENT)
4584 return i == 0;
4586 return JS_FALSE;
4589 static JSBool
4590 HasSimpleContent(JSXML *xml);
4592 static JSBool
4593 HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
4595 JSObject *pobj;
4596 JSProperty *prop;
4597 JSXML *xml;
4599 JS_ASSERT(obj->getClass() == &js_XMLClass);
4601 if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
4602 return false;
4603 if (!prop) {
4604 xml = (JSXML *) obj->getPrivate();
4605 if (HasSimpleContent(xml)) {
4606 AutoObjectRooter tvr(cx);
4609 * Search in String.prototype to set found whenever
4610 * GetXMLFunction returns existing function.
4612 if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
4613 return false;
4615 JS_ASSERT(tvr.object());
4616 if (!js_LookupProperty(cx, tvr.object(), funid, &pobj, &prop))
4617 return false;
4620 *found = (prop != NULL);
4621 return true;
4624 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4625 static JSBool
4626 HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found)
4628 JSXML *xml;
4629 bool isIndex;
4630 uint32 i;
4631 JSObject *qn;
4632 jsid funid;
4634 xml = (JSXML *) obj->getPrivate();
4635 if (!js_IdValIsIndex(cx, id, &i, &isIndex))
4636 return JS_FALSE;
4638 if (isIndex) {
4639 *found = HasIndexedProperty(xml, i);
4640 } else {
4641 qn = ToXMLName(cx, id, &funid);
4642 if (!qn)
4643 return JS_FALSE;
4644 if (!JSID_IS_VOID(funid)) {
4645 if (!HasFunctionProperty(cx, obj, funid, found))
4646 return JS_FALSE;
4647 } else {
4648 *found = HasNamedProperty(xml, qn);
4651 return JS_TRUE;
4654 static void
4655 xml_finalize(JSContext *cx, JSObject *obj)
4657 JSXML *xml = (JSXML *) obj->getPrivate();
4658 if (!xml)
4659 return;
4660 if (xml->object == obj)
4661 xml->object = NULL;
4664 static void
4665 xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
4667 uint32 i;
4668 JSXML *xml;
4670 for (i = 0; i < len; i++) {
4671 xml = vec[i];
4672 if (xml) {
4673 JS_SET_TRACING_INDEX(trc, "xml_vector", i);
4674 Mark(trc, xml);
4680 * XML objects are native. Thus xml_lookupProperty must return a valid
4681 * Shape pointer parameter via *propp to signify "property found". Since the
4682 * only call to xml_lookupProperty is via JSObject::lookupProperty, and then
4683 * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
4684 * JSOP_IN case in the interpreter, the only time we add a Shape here is when
4685 * an unqualified name is being accessed or when "name in xml" is called.
4687 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4688 * giving it an shape with (getter, setter) == (GetProperty, PutProperty).
4690 * NB: xml_deleteProperty must take care to remove any property added here.
4692 * FIXME This clashes with the function namespace implementation which also
4693 * uses native properties. Effectively after xml_lookupProperty any property
4694 * stored previously using assignments to xml.function::name will be removed.
4695 * We partially workaround the problem in GetXMLFunction. There we take
4696 * advantage of the fact that typically function:: is used to access the
4697 * functions from XML.prototype. So when js_GetProperty returns a non-function
4698 * property, we assume that it represents the result of GetProperty setter
4699 * hiding the function and use an extra prototype chain lookup to recover it.
4700 * For a proper solution see bug 355257.
4702 static JSBool
4703 xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4704 JSProperty **propp)
4706 JSBool found;
4707 JSXML *xml;
4708 uint32 i;
4709 JSObject *qn;
4710 jsid funid;
4712 xml = (JSXML *) obj->getPrivate();
4713 if (js_IdIsIndex(id, &i)) {
4714 found = HasIndexedProperty(xml, i);
4715 } else {
4716 qn = ToXMLName(cx, IdToJsval(id), &funid);
4717 if (!qn)
4718 return JS_FALSE;
4719 if (!JSID_IS_VOID(funid))
4720 return js_LookupProperty(cx, obj, funid, objp, propp);
4721 found = HasNamedProperty(xml, qn);
4723 if (!found) {
4724 *objp = NULL;
4725 *propp = NULL;
4726 } else {
4727 const Shape *shape =
4728 js_AddNativeProperty(cx, obj, id,
4729 Valueify(GetProperty), Valueify(PutProperty),
4730 SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
4731 0, 0);
4732 if (!shape)
4733 return JS_FALSE;
4735 *objp = obj;
4736 *propp = (JSProperty *) shape;
4738 return JS_TRUE;
4741 static JSBool
4742 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
4743 PropertyOp getter, StrictPropertyOp setter, uintN attrs)
4745 if (IsFunctionObject(*v) || getter || setter ||
4746 (attrs & JSPROP_ENUMERATE) == 0 ||
4747 (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
4748 return js_DefineProperty(cx, obj, id, v, getter, setter, attrs);
4751 jsval tmp = Jsvalify(*v);
4752 return PutProperty(cx, obj, id, false, &tmp);
4755 static JSBool
4756 xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
4758 if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) {
4759 vp->setUndefined();
4760 return JS_TRUE;
4763 return GetProperty(cx, obj, id, Jsvalify(vp));
4766 static JSBool
4767 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
4769 return PutProperty(cx, obj, id, strict, Jsvalify(vp));
4772 static JSBool
4773 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
4775 JSBool found;
4776 if (!HasProperty(cx, obj, IdToJsval(id), &found))
4777 return false;
4779 *attrsp = found ? JSPROP_ENUMERATE : 0;
4780 return JS_TRUE;
4783 static JSBool
4784 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
4786 JSBool found;
4787 if (!HasProperty(cx, obj, IdToJsval(id), &found))
4788 return false;
4790 if (found) {
4791 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4792 JSMSG_CANT_SET_XML_ATTRS);
4793 return false;
4795 return true;
4798 static JSBool
4799 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
4801 JSXML *xml;
4802 jsval idval;
4803 uint32 index;
4804 JSObject *nameqn;
4805 jsid funid;
4807 idval = IdToJsval(id);
4808 xml = (JSXML *) obj->getPrivate();
4809 if (js_IdIsIndex(id, &index)) {
4810 if (xml->xml_class != JSXML_CLASS_LIST) {
4811 /* See NOTE in spec: this variation is reserved for future use. */
4812 ReportBadXMLName(cx, IdToValue(id));
4813 return false;
4816 /* ECMA-357 9.2.1.3. */
4817 DeleteListElement(cx, xml, index);
4818 } else {
4819 nameqn = ToXMLName(cx, idval, &funid);
4820 if (!nameqn)
4821 return false;
4822 if (!JSID_IS_VOID(funid))
4823 return js_DeleteProperty(cx, obj, funid, rval, false);
4825 DeleteNamedProperty(cx, xml, nameqn,
4826 nameqn->getClass() == &js_AttributeNameClass);
4830 * If this object has its own (mutable) scope, then we may have added a
4831 * property to the scope in xml_lookupProperty for it to return to mean
4832 * "found" and to provide a handle for access operations to call the
4833 * property's getter or setter. But now it's time to remove any such
4834 * property, to purge the property cache and remove the scope entry.
4836 if (!obj->nativeEmpty() && !js_DeleteProperty(cx, obj, id, rval, false))
4837 return false;
4839 rval->setBoolean(true);
4840 return true;
4843 JSBool
4844 xml_convert(JSContext *cx, JSObject *obj, JSType type, Value *rval)
4846 return js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, rval);
4849 static JSBool
4850 xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
4852 JSXML *xml;
4853 uint32 length, index;
4854 JSXMLArrayCursor *cursor;
4856 xml = (JSXML *)obj->getPrivate();
4857 length = JSXML_LENGTH(xml);
4859 switch (enum_op) {
4860 case JSENUMERATE_INIT:
4861 case JSENUMERATE_INIT_ALL:
4862 if (length == 0) {
4863 statep->setInt32(0);
4864 } else {
4865 cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
4866 if (!cursor)
4867 return JS_FALSE;
4868 statep->setPrivate(cursor);
4870 if (idp)
4871 *idp = INT_TO_JSID(length);
4872 break;
4874 case JSENUMERATE_NEXT:
4875 if (statep->isInt32(0)) {
4876 statep->setNull();
4877 break;
4879 cursor = (JSXMLArrayCursor *) statep->toPrivate();
4880 if (cursor && cursor->array && (index = cursor->index) < length) {
4881 *idp = INT_TO_JSID(index);
4882 cursor->index = index + 1;
4883 break;
4885 /* FALL THROUGH */
4887 case JSENUMERATE_DESTROY:
4888 if (!statep->isInt32(0)) {
4889 cursor = (JSXMLArrayCursor *) statep->toPrivate();
4890 if (cursor)
4891 cx->destroy(cursor);
4893 statep->setNull();
4894 break;
4896 return JS_TRUE;
4899 static JSType
4900 xml_typeOf(JSContext *cx, JSObject *obj)
4902 return JSTYPE_XML;
4905 static JSBool
4906 xml_hasInstance(JSContext *cx, JSObject *obj, const Value *, JSBool *bp)
4908 return JS_TRUE;
4911 static void
4912 xml_trace(JSTracer *trc, JSObject *obj)
4914 JSXML *xml = (JSXML *) obj->getPrivate();
4915 if (xml)
4916 JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private");
4919 static JSBool
4920 xml_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
4922 JS_ASSERT(obj->isExtensible());
4923 *success = false;
4924 return true;
4927 static void
4928 xml_clear(JSContext *cx, JSObject *obj)
4932 static JSBool
4933 HasSimpleContent(JSXML *xml)
4935 JSXML *kid;
4936 JSBool simple;
4937 uint32 i, n;
4939 again:
4940 switch (xml->xml_class) {
4941 case JSXML_CLASS_COMMENT:
4942 case JSXML_CLASS_PROCESSING_INSTRUCTION:
4943 return JS_FALSE;
4944 case JSXML_CLASS_LIST:
4945 if (xml->xml_kids.length == 0)
4946 return JS_TRUE;
4947 if (xml->xml_kids.length == 1) {
4948 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4949 if (kid) {
4950 xml = kid;
4951 goto again;
4954 /* FALL THROUGH */
4955 default:
4956 simple = JS_TRUE;
4957 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
4958 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4959 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
4960 simple = JS_FALSE;
4961 break;
4964 return simple;
4969 * 11.2.2.1 Step 3(d) onward.
4971 JSBool
4972 js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
4974 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
4976 if (JSID_IS_OBJECT(id)) {
4977 jsid funid;
4979 if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid))
4980 return JS_FALSE;
4981 if (!JSID_IS_VOID(funid))
4982 id = funid;
4986 * As our callers have a bad habit of passing a pointer to an unrooted
4987 * local value as vp, we use a proper root here.
4989 AutoValueRooter tvr(cx);
4990 JSBool ok = GetXMLFunction(cx, obj, id, Jsvalify(tvr.addr()));
4991 *vp = tvr.value();
4992 return ok;
4995 JSBool
4996 js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp)
4998 JSXML *xml, *vxml;
4999 JSObject *vobj;
5000 JSBool ok;
5001 JSString *str, *vstr;
5002 jsdouble d, d2;
5004 JSObject *obj;
5005 jsval v;
5006 if (v1.isObject() && v1.toObject().isXML()) {
5007 obj = &v1.toObject();
5008 v = Jsvalify(v2);
5009 } else {
5010 v = Jsvalify(v1);
5011 obj = &v2.toObject();
5014 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
5016 xml = (JSXML *) obj->getPrivate();
5017 vxml = NULL;
5018 if (!JSVAL_IS_PRIMITIVE(v)) {
5019 vobj = JSVAL_TO_OBJECT(v);
5020 if (vobj->isXML())
5021 vxml = (JSXML *) vobj->getPrivate();
5024 if (xml->xml_class == JSXML_CLASS_LIST) {
5025 ok = Equals(cx, xml, v, bp);
5026 } else if (vxml) {
5027 if (vxml->xml_class == JSXML_CLASS_LIST) {
5028 ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
5029 } else {
5030 if (((xml->xml_class == JSXML_CLASS_TEXT ||
5031 xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5032 HasSimpleContent(vxml)) ||
5033 ((vxml->xml_class == JSXML_CLASS_TEXT ||
5034 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5035 HasSimpleContent(xml))) {
5036 ok = js_EnterLocalRootScope(cx);
5037 if (ok) {
5038 ok = (str = js_ValueToString(cx, ObjectValue(*obj))) &&
5039 (vstr = js_ValueToString(cx, Valueify(v)));
5040 if (ok)
5041 ok = EqualStrings(cx, str, vstr, bp);
5042 js_LeaveLocalRootScope(cx);
5044 } else {
5045 ok = XMLEquals(cx, xml, vxml, bp);
5048 } else {
5049 ok = js_EnterLocalRootScope(cx);
5050 if (ok) {
5051 if (HasSimpleContent(xml)) {
5052 ok = (str = js_ValueToString(cx, ObjectValue(*obj))) &&
5053 (vstr = js_ValueToString(cx, Valueify(v)));
5054 if (ok)
5055 ok = EqualStrings(cx, str, vstr, bp);
5056 } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
5057 str = js_ValueToString(cx, ObjectValue(*obj));
5058 if (!str) {
5059 ok = JS_FALSE;
5060 } else if (JSVAL_IS_STRING(v)) {
5061 ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), bp);
5062 } else {
5063 ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5064 if (ok) {
5065 d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5066 : JSVAL_TO_DOUBLE(v);
5067 *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
5070 } else {
5071 *bp = JS_FALSE;
5073 js_LeaveLocalRootScope(cx);
5076 return ok;
5079 JSBool
5080 js_ConcatenateXML(JSContext *cx, JSObject *obj, JSObject *robj, Value *vp)
5082 JSBool ok;
5083 JSObject *listobj;
5084 JSXML *list, *lxml, *rxml;
5086 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
5087 ok = js_EnterLocalRootScope(cx);
5088 if (!ok)
5089 return JS_FALSE;
5091 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5092 if (!listobj) {
5093 ok = JS_FALSE;
5094 goto out;
5097 list = (JSXML *) listobj->getPrivate();
5098 lxml = (JSXML *) obj->getPrivate();
5099 ok = Append(cx, list, lxml);
5100 if (!ok)
5101 goto out;
5103 JS_ASSERT(robj->isXML());
5104 rxml = (JSXML *) robj->getPrivate();
5105 ok = Append(cx, list, rxml);
5106 if (!ok)
5107 goto out;
5109 vp->setObject(*listobj);
5110 out:
5111 js_LeaveLocalRootScopeWithResult(cx, *vp);
5112 return ok;
5115 JS_FRIEND_DATA(Class) js_XMLClass = {
5116 js_XML_str,
5117 JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
5118 JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
5119 PropertyStub, /* addProperty */
5120 PropertyStub, /* delProperty */
5121 PropertyStub, /* getProperty */
5122 StrictPropertyStub, /* setProperty */
5123 EnumerateStub,
5124 ResolveStub,
5125 xml_convert,
5126 xml_finalize,
5127 NULL, /* reserved0 */
5128 NULL, /* checkAccess */
5129 NULL, /* call */
5130 NULL, /* construct */
5131 NULL, /* xdrObject */
5132 xml_hasInstance,
5133 JS_CLASS_TRACE(xml_trace),
5134 JS_NULL_CLASS_EXT,
5136 xml_lookupProperty,
5137 xml_defineProperty,
5138 xml_getProperty,
5139 xml_setProperty,
5140 xml_getAttributes,
5141 xml_setAttributes,
5142 xml_deleteProperty,
5143 xml_enumerate,
5144 xml_typeOf,
5145 NULL, /* trace */
5146 xml_fix,
5147 NULL, /* thisObject */
5148 xml_clear
5152 static JSXML *
5153 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
5155 JSXML *xml;
5156 JSFunction *fun;
5157 char numBuf[12];
5159 JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
5161 *objp = ToObject(cx, Valueify(&vp[1]));
5162 if (!*objp)
5163 return NULL;
5164 xml = (JSXML *) GetInstancePrivate(cx, *objp, &js_XMLClass, Valueify(vp + 2));
5165 if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5166 return xml;
5168 if (xml->xml_kids.length == 1) {
5169 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5170 if (xml) {
5171 *objp = js_GetXMLObject(cx, xml);
5172 if (!*objp)
5173 return NULL;
5174 vp[1] = OBJECT_TO_JSVAL(*objp);
5175 return xml;
5179 fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp));
5180 JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
5181 JSAutoByteString funNameBytes;
5182 if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
5183 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_LIST_XML_METHOD,
5184 funName, numBuf);
5186 return NULL;
5189 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5190 #define XML_METHOD_PROLOG \
5191 JSObject *obj = ToObject(cx, Valueify(&vp[1])); \
5192 if (!obj) \
5193 return JS_FALSE; \
5194 JSXML *xml = (JSXML *)GetInstancePrivate(cx, obj, &js_XMLClass, Valueify(vp+2)); \
5195 if (!xml) \
5196 return JS_FALSE
5198 #define NON_LIST_XML_METHOD_PROLOG \
5199 JSObject *obj; \
5200 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5201 if (!xml) \
5202 return JS_FALSE; \
5203 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5205 static JSBool
5206 xml_addNamespace(JSContext *cx, uintN argc, jsval *vp)
5208 JSObject *ns;
5210 NON_LIST_XML_METHOD_PROLOG;
5211 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5212 goto done;
5213 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5214 if (!xml)
5215 return JS_FALSE;
5217 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
5218 return JS_FALSE;
5219 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5221 ns = JSVAL_TO_OBJECT(*vp);
5222 if (!AddInScopeNamespace(cx, xml, ns))
5223 return JS_FALSE;
5224 ns->setNamespaceDeclared(JSVAL_TRUE);
5226 done:
5227 *vp = OBJECT_TO_JSVAL(obj);
5228 return JS_TRUE;
5231 static JSBool
5232 xml_appendChild(JSContext *cx, uintN argc, jsval *vp)
5234 jsval v;
5235 JSObject *vobj;
5236 JSXML *vxml;
5238 NON_LIST_XML_METHOD_PROLOG;
5239 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5240 if (!xml)
5241 return JS_FALSE;
5243 jsid name;
5244 if (!js_GetAnyName(cx, &name))
5245 return JS_FALSE;
5247 if (!GetProperty(cx, obj, name, &v))
5248 return JS_FALSE;
5250 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5251 vobj = JSVAL_TO_OBJECT(v);
5252 JS_ASSERT(vobj->isXML());
5253 vxml = (JSXML *) vobj->getPrivate();
5254 JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
5256 if (!IndexToId(cx, vxml->xml_kids.length, &name))
5257 return JS_FALSE;
5258 *vp = (argc != 0) ? vp[2] : JSVAL_VOID;
5260 if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, false, vp))
5261 return JS_FALSE;
5263 *vp = OBJECT_TO_JSVAL(obj);
5264 return JS_TRUE;
5267 /* XML and XMLList */
5268 static JSBool
5269 xml_attribute(JSContext *cx, uintN argc, jsval *vp)
5271 JSObject *qn;
5273 if (argc == 0) {
5274 js_ReportMissingArg(cx, Valueify(*vp), 0);
5275 return JS_FALSE;
5278 qn = ToAttributeName(cx, vp[2]);
5279 if (!qn)
5280 return JS_FALSE;
5281 vp[2] = OBJECT_TO_JSVAL(qn); /* local root */
5283 jsid id = OBJECT_TO_JSID(qn);
5284 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5285 if (!obj)
5286 return JS_FALSE;
5287 return GetProperty(cx, obj, id, vp);
5290 /* XML and XMLList */
5291 static JSBool
5292 xml_attributes(JSContext *cx, uintN argc, jsval *vp)
5294 jsval name = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
5295 JSObject *qn = ToAttributeName(cx, name);
5296 if (!qn)
5297 return JS_FALSE;
5299 AutoObjectRooter tvr(cx, qn);
5300 jsid id = OBJECT_TO_JSID(qn);
5301 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5302 if (!obj)
5303 return JS_FALSE;
5304 return GetProperty(cx, obj, id, vp);
5307 static JSXML *
5308 xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5310 JSObject *listobj;
5311 JSXML *list;
5313 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5314 if (!listobj)
5315 return NULL;
5317 *rval = OBJECT_TO_JSVAL(listobj);
5318 list = (JSXML *) listobj->getPrivate();
5319 list->xml_target = xml;
5320 return list;
5323 static JSBool
5324 ValueToId(JSContext *cx, jsval v, AutoIdRooter *idr)
5326 if (JSVAL_IS_INT(v)) {
5327 jsint i = JSVAL_TO_INT(v);
5328 if (INT_FITS_IN_JSID(i))
5329 *idr->addr() = INT_TO_JSID(i);
5330 else if (!js_ValueToStringId(cx, Valueify(v), idr->addr()))
5331 return JS_FALSE;
5332 } else if (JSVAL_IS_STRING(v)) {
5333 JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
5334 if (!atom)
5335 return JS_FALSE;
5336 *idr->addr() = ATOM_TO_JSID(atom);
5337 } else if (!JSVAL_IS_PRIMITIVE(v)) {
5338 *idr->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v));
5339 } else {
5340 ReportBadXMLName(cx, Valueify(v));
5341 return JS_FALSE;
5343 return JS_TRUE;
5346 static JSBool
5347 xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5348 jsval *rval)
5350 bool isIndex;
5351 uint32 index;
5352 JSXML *kid;
5353 JSObject *kidobj;
5355 /* ECMA-357 13.4.4.6 */
5356 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5358 if (!js_IdValIsIndex(cx, name, &index, &isIndex))
5359 return JS_FALSE;
5361 if (isIndex) {
5362 if (index >= JSXML_LENGTH(xml)) {
5363 *rval = JSVAL_VOID;
5364 } else {
5365 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5366 if (!kid) {
5367 *rval = JSVAL_VOID;
5368 } else {
5369 kidobj = js_GetXMLObject(cx, kid);
5370 if (!kidobj)
5371 return JS_FALSE;
5372 *rval = OBJECT_TO_JSVAL(kidobj);
5375 return JS_TRUE;
5378 AutoIdRooter idr(cx);
5379 if (!ValueToId(cx, name, &idr))
5380 return JS_FALSE;
5382 return GetProperty(cx, obj, idr.id(), rval);
5385 /* XML and XMLList */
5386 static JSBool
5387 xml_child(JSContext *cx, uintN argc, jsval *vp)
5389 jsval v;
5390 JSXML *list, *vxml;
5391 JSObject *kidobj;
5393 XML_METHOD_PROLOG;
5394 jsval name = argc != 0 ? vp[2] : JSVAL_VOID;
5395 if (xml->xml_class == JSXML_CLASS_LIST) {
5396 /* ECMA-357 13.5.4.4 */
5397 list = xml_list_helper(cx, xml, vp);
5398 if (!list)
5399 return JS_FALSE;
5401 JSXMLArrayCursor cursor(&xml->xml_kids);
5402 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5403 kidobj = js_GetXMLObject(cx, kid);
5404 if (!kidobj)
5405 return JS_FALSE;
5406 if (!xml_child_helper(cx, kidobj, kid, name, &v))
5407 return JS_FALSE;
5408 if (JSVAL_IS_VOID(v)) {
5409 /* The property didn't exist in this kid. */
5410 continue;
5413 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5414 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5415 if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
5416 !Append(cx, list, vxml)) {
5417 return JS_FALSE;
5420 return JS_TRUE;
5423 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5424 if (!xml_child_helper(cx, obj, xml, name, vp))
5425 return JS_FALSE;
5426 if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp))
5427 return JS_FALSE;
5428 return JS_TRUE;
5431 static JSBool
5432 xml_childIndex(JSContext *cx, uintN argc, jsval *vp)
5434 JSXML *parent;
5435 uint32 i, n;
5437 NON_LIST_XML_METHOD_PROLOG;
5438 parent = xml->parent;
5439 if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
5440 *vp = DOUBLE_TO_JSVAL(js_NaN);
5441 return JS_TRUE;
5443 for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5444 if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5445 break;
5447 JS_ASSERT(i < n);
5448 if (i <= JSVAL_INT_MAX)
5449 *vp = INT_TO_JSVAL(i);
5450 else
5451 *vp = DOUBLE_TO_JSVAL(i);
5452 return JS_TRUE;
5455 /* XML and XMLList */
5456 static JSBool
5457 xml_children(JSContext *cx, uintN argc, jsval *vp)
5459 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5460 if (!obj)
5461 return false;
5462 jsid name = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
5463 return GetProperty(cx, obj, name, vp);
5466 /* XML and XMLList */
5467 static JSBool
5468 xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
5470 JSXML *list, *kid, *vxml;
5471 JSBool ok;
5472 uint32 i, n;
5473 JSObject *kidobj;
5474 jsval v;
5476 list = xml_list_helper(cx, xml, vp);
5477 if (!list)
5478 return JS_FALSE;
5480 ok = JS_TRUE;
5482 if (xml->xml_class == JSXML_CLASS_LIST) {
5483 /* 13.5.4.6 Step 2. */
5484 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5485 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5486 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5487 ok = js_EnterLocalRootScope(cx);
5488 if (!ok)
5489 break;
5490 kidobj = js_GetXMLObject(cx, kid);
5491 if (kidobj) {
5492 ok = xml_comments_helper(cx, kidobj, kid, &v);
5493 } else {
5494 ok = JS_FALSE;
5495 v = JSVAL_NULL;
5497 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
5498 if (!ok)
5499 break;
5500 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5501 if (JSXML_LENGTH(vxml) != 0) {
5502 ok = Append(cx, list, vxml);
5503 if (!ok)
5504 break;
5508 } else {
5509 /* 13.4.4.9 Step 2. */
5510 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5511 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5512 if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
5513 ok = Append(cx, list, kid);
5514 if (!ok)
5515 break;
5520 return ok;
5523 static JSBool
5524 xml_comments(JSContext *cx, uintN argc, jsval *vp)
5526 XML_METHOD_PROLOG;
5527 return xml_comments_helper(cx, obj, xml, vp);
5530 /* XML and XMLList */
5531 static JSBool
5532 xml_contains(JSContext *cx, uintN argc, jsval *vp)
5534 jsval value;
5535 JSBool eq;
5536 JSObject *kidobj;
5538 XML_METHOD_PROLOG;
5539 value = argc != 0 ? vp[2] : JSVAL_VOID;
5540 if (xml->xml_class == JSXML_CLASS_LIST) {
5541 eq = JS_FALSE;
5542 JSXMLArrayCursor cursor(&xml->xml_kids);
5543 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5544 kidobj = js_GetXMLObject(cx, kid);
5545 if (!kidobj || !js_TestXMLEquality(cx, ObjectValue(*kidobj), Valueify(value), &eq))
5546 return JS_FALSE;
5547 if (eq)
5548 break;
5550 } else {
5551 if (!js_TestXMLEquality(cx, ObjectValue(*obj), Valueify(value), &eq))
5552 return JS_FALSE;
5554 *vp = BOOLEAN_TO_JSVAL(eq);
5555 return JS_TRUE;
5558 /* XML and XMLList */
5559 static JSBool
5560 xml_copy(JSContext *cx, uintN argc, jsval *vp)
5562 JSXML *copy;
5564 XML_METHOD_PROLOG;
5565 copy = DeepCopy(cx, xml, NULL, 0);
5566 if (!copy)
5567 return JS_FALSE;
5568 *vp = OBJECT_TO_JSVAL(copy->object);
5569 return JS_TRUE;
5572 /* XML and XMLList */
5573 static JSBool
5574 xml_descendants(JSContext *cx, uintN argc, jsval *vp)
5576 jsval name;
5577 JSXML *list;
5579 XML_METHOD_PROLOG;
5580 name = argc == 0 ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5581 list = Descendants(cx, xml, name);
5582 if (!list)
5583 return JS_FALSE;
5584 *vp = OBJECT_TO_JSVAL(list->object);
5585 return JS_TRUE;
5588 /* XML and XMLList */
5589 static JSBool
5590 xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml,
5591 JSObject *nameqn, jsval *vp)
5593 JSXML *list, *vxml;
5594 jsval v;
5595 JSBool ok;
5596 JSObject *kidobj;
5597 uint32 i, n;
5599 list = xml_list_helper(cx, xml, vp);
5600 if (!list)
5601 return JS_FALSE;
5603 list->xml_targetprop = nameqn;
5604 ok = JS_TRUE;
5606 if (xml->xml_class == JSXML_CLASS_LIST) {
5607 /* 13.5.4.6 */
5608 JSXMLArrayCursor cursor(&xml->xml_kids);
5609 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5610 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
5611 ok = js_EnterLocalRootScope(cx);
5612 if (!ok)
5613 break;
5614 kidobj = js_GetXMLObject(cx, kid);
5615 if (kidobj) {
5616 ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v);
5617 } else {
5618 ok = JS_FALSE;
5619 v = JSVAL_NULL;
5621 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
5622 if (!ok)
5623 break;
5624 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5625 if (JSXML_LENGTH(vxml) != 0) {
5626 ok = Append(cx, list, vxml);
5627 if (!ok)
5628 break;
5632 } else {
5633 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5634 JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5635 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
5636 MatchElemName(nameqn, kid)) {
5637 ok = Append(cx, list, kid);
5638 if (!ok)
5639 break;
5644 return ok;
5647 static JSBool
5648 xml_elements(JSContext *cx, uintN argc, jsval *vp)
5650 jsval name;
5651 JSObject *nameqn;
5652 jsid funid;
5654 XML_METHOD_PROLOG;
5656 name = (argc == 0) ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5657 nameqn = ToXMLName(cx, name, &funid);
5658 if (!nameqn)
5659 return JS_FALSE;
5661 if (!JSID_IS_VOID(funid))
5662 return xml_list_helper(cx, xml, vp) != NULL;
5664 return xml_elements_helper(cx, obj, xml, nameqn, vp);
5667 /* XML and XMLList */
5668 static JSBool
5669 xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
5671 jsval name;
5672 JSBool found;
5674 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
5675 if (!obj)
5676 return JS_FALSE;
5677 if (!InstanceOf(cx, obj, &js_XMLClass, Valueify(vp + 2)))
5678 return JS_FALSE;
5680 name = argc != 0 ? vp[2] : JSVAL_VOID;
5681 if (!HasProperty(cx, obj, name, &found))
5682 return JS_FALSE;
5683 if (found) {
5684 *vp = JSVAL_TRUE;
5685 return JS_TRUE;
5687 return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, Valueify(vp));
5690 /* XML and XMLList */
5691 static JSBool
5692 xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp)
5694 JSXML *kid;
5695 JSObject *kidobj;
5696 uint32 i, n;
5698 XML_METHOD_PROLOG;
5699 again:
5700 switch (xml->xml_class) {
5701 case JSXML_CLASS_ATTRIBUTE:
5702 case JSXML_CLASS_COMMENT:
5703 case JSXML_CLASS_PROCESSING_INSTRUCTION:
5704 case JSXML_CLASS_TEXT:
5705 *vp = JSVAL_FALSE;
5706 break;
5707 case JSXML_CLASS_LIST:
5708 if (xml->xml_kids.length == 0) {
5709 *vp = JSVAL_TRUE;
5710 } else if (xml->xml_kids.length == 1) {
5711 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5712 if (kid) {
5713 kidobj = js_GetXMLObject(cx, kid);
5714 if (!kidobj)
5715 return JS_FALSE;
5716 obj = kidobj;
5717 xml = (JSXML *) obj->getPrivate();
5718 goto again;
5721 /* FALL THROUGH */
5722 default:
5723 *vp = JSVAL_FALSE;
5724 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
5725 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5726 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5727 *vp = JSVAL_TRUE;
5728 break;
5731 break;
5733 return JS_TRUE;
5736 /* XML and XMLList */
5737 static JSBool
5738 xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp)
5740 XML_METHOD_PROLOG;
5741 *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
5742 return JS_TRUE;
5745 static JSBool
5746 FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
5748 uint32 length, i, j, n;
5749 JSObject *ns, *ns2;
5750 JSLinearString *prefix, *prefix2;
5752 length = nsarray->length;
5753 do {
5754 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5755 continue;
5756 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
5757 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
5758 if (!ns)
5759 continue;
5761 prefix = ns->getNamePrefix();
5762 for (j = 0; j < length; j++) {
5763 ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject);
5764 if (ns2) {
5765 prefix2 = ns2->getNamePrefix();
5766 if ((prefix2 && prefix)
5767 ? EqualStrings(prefix2, prefix)
5768 : EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
5769 break;
5774 if (j == length) {
5775 if (!XMLARRAY_APPEND(cx, nsarray, ns))
5776 return JS_FALSE;
5777 ++length;
5780 } while ((xml = xml->parent) != NULL);
5781 JS_ASSERT(length == nsarray->length);
5783 return JS_TRUE;
5787 * Populate a new JS array with elements of array and place the result into
5788 * rval. rval must point to a rooted location.
5790 static bool
5791 NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
5793 JSObject *arrayobj = NewDenseEmptyArray(cx);
5794 if (!arrayobj)
5795 return false;
5796 *rval = OBJECT_TO_JSVAL(arrayobj);
5798 AutoValueRooter tvr(cx);
5799 for (uint32 i = 0, n = array->length; i < n; i++) {
5800 JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
5801 if (!ns)
5802 continue;
5803 tvr.set(ObjectValue(*ns));
5804 if (!arrayobj->setProperty(cx, INT_TO_JSID(i), tvr.addr(), false))
5805 return false;
5807 return true;
5810 static JSBool
5811 xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp)
5813 NON_LIST_XML_METHOD_PROLOG;
5815 AutoNamespaceArray namespaces(cx);
5816 return FindInScopeNamespaces(cx, xml, &namespaces.array) &&
5817 NamespacesToJSArray(cx, &namespaces.array, vp);
5820 static JSBool
5821 xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp)
5823 jsval arg;
5824 JSXML *kid;
5825 uint32 i;
5827 NON_LIST_XML_METHOD_PROLOG;
5828 *vp = OBJECT_TO_JSVAL(obj);
5829 if (!JSXML_HAS_KIDS(xml) || argc == 0)
5830 return JS_TRUE;
5832 arg = vp[2];
5833 if (JSVAL_IS_NULL(arg)) {
5834 kid = NULL;
5835 i = 0;
5836 } else {
5837 if (!VALUE_IS_XML(arg))
5838 return JS_TRUE;
5839 kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
5840 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
5841 if (i == XML_NOT_FOUND)
5842 return JS_TRUE;
5843 ++i;
5846 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5847 if (!xml)
5848 return JS_FALSE;
5849 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
5852 static JSBool
5853 xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp)
5855 jsval arg;
5856 JSXML *kid;
5857 uint32 i;
5859 NON_LIST_XML_METHOD_PROLOG;
5860 *vp = OBJECT_TO_JSVAL(obj);
5861 if (!JSXML_HAS_KIDS(xml) || argc == 0)
5862 return JS_TRUE;
5864 arg = vp[2];
5865 if (JSVAL_IS_NULL(arg)) {
5866 kid = NULL;
5867 i = xml->xml_kids.length;
5868 } else {
5869 if (!VALUE_IS_XML(arg))
5870 return JS_TRUE;
5871 kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
5872 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
5873 if (i == XML_NOT_FOUND)
5874 return JS_TRUE;
5877 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5878 if (!xml)
5879 return JS_FALSE;
5880 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
5883 /* XML and XMLList */
5884 static JSBool
5885 xml_length(JSContext *cx, uintN argc, jsval *vp)
5887 XML_METHOD_PROLOG;
5888 if (xml->xml_class != JSXML_CLASS_LIST) {
5889 *vp = JSVAL_ONE;
5890 } else {
5891 uint32 l = xml->xml_kids.length;
5892 if (l <= JSVAL_INT_MAX)
5893 *vp = INT_TO_JSVAL(l);
5894 else
5895 *vp = DOUBLE_TO_JSVAL(l);
5897 return JS_TRUE;
5900 static JSBool
5901 xml_localName(JSContext *cx, uintN argc, jsval *vp)
5903 NON_LIST_XML_METHOD_PROLOG;
5904 *vp = xml->name ? xml->name->getQNameLocalNameVal() : JSVAL_NULL;
5905 return JS_TRUE;
5908 static JSBool
5909 xml_name(JSContext *cx, uintN argc, jsval *vp)
5911 NON_LIST_XML_METHOD_PROLOG;
5912 *vp = OBJECT_TO_JSVAL(xml->name);
5913 return JS_TRUE;
5916 static JSBool
5917 xml_namespace(JSContext *cx, uintN argc, jsval *vp)
5919 JSLinearString *prefix, *nsprefix;
5920 jsuint i, length;
5921 JSObject *ns;
5923 NON_LIST_XML_METHOD_PROLOG;
5924 if (argc == 0 && !JSXML_HAS_NAME(xml)) {
5925 *vp = JSVAL_NULL;
5926 return true;
5929 if (argc == 0) {
5930 prefix = NULL;
5931 } else {
5932 JSString *str = js_ValueToString(cx, Valueify(vp[2]));
5933 if (!str)
5934 return false;
5935 prefix = str->ensureLinear(cx);
5936 if (!prefix)
5937 return false;
5938 vp[2] = STRING_TO_JSVAL(prefix); /* local root */
5941 AutoNamespaceArray inScopeNSes(cx);
5942 if (!FindInScopeNamespaces(cx, xml, &inScopeNSes.array))
5943 return false;
5945 if (!prefix) {
5946 ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
5947 if (!ns)
5948 return false;
5949 } else {
5950 ns = NULL;
5951 for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
5952 ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject);
5953 if (ns) {
5954 nsprefix = ns->getNamePrefix();
5955 if (nsprefix && EqualStrings(nsprefix, prefix))
5956 break;
5957 ns = NULL;
5962 *vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns);
5963 return true;
5966 static JSBool
5967 xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp)
5969 NON_LIST_XML_METHOD_PROLOG;
5970 if (JSXML_HAS_VALUE(xml))
5971 return true;
5973 AutoNamespaceArray ancestors(cx);
5974 AutoNamespaceArray declared(cx);
5976 JSXML *yml = xml;
5977 while ((yml = yml->parent) != NULL) {
5978 JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
5979 for (uint32 i = 0, n = yml->xml_namespaces.length; i < n; i++) {
5980 JSObject *ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
5981 if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
5982 if (!XMLARRAY_APPEND(cx, &ancestors.array, ns))
5983 return false;
5988 for (uint32 i = 0, n = xml->xml_namespaces.length; i < n; i++) {
5989 JSObject *ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
5990 if (!ns)
5991 continue;
5992 if (!IsDeclared(ns))
5993 continue;
5994 if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
5995 if (!XMLARRAY_APPEND(cx, &declared.array, ns))
5996 return false;
6000 return NamespacesToJSArray(cx, &declared.array, vp);
6003 static const char js_attribute_str[] = "attribute";
6004 static const char js_text_str[] = "text";
6006 /* Exported to jsgc.c #ifdef DEBUG. */
6007 const char *js_xml_class_str[] = {
6008 "list",
6009 "element",
6010 js_attribute_str,
6011 "processing-instruction",
6012 js_text_str,
6013 "comment"
6016 static JSBool
6017 xml_nodeKind(JSContext *cx, uintN argc, jsval *vp)
6019 JSString *str;
6021 NON_LIST_XML_METHOD_PROLOG;
6022 str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6023 if (!str)
6024 return JS_FALSE;
6025 *vp = STRING_TO_JSVAL(str);
6026 return JS_TRUE;
6029 static void
6030 NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index)
6032 if (xml->xml_class == JSXML_CLASS_LIST)
6033 DeleteListElement(cx, xml, index);
6034 else
6035 DeleteByIndex(cx, xml, index);
6038 /* XML and XMLList */
6039 static JSBool
6040 xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml)
6042 JSXML *kid, *kid2;
6043 uint32 i, n;
6044 JSObject *kidobj;
6045 JSString *str;
6047 if (!JSXML_HAS_KIDS(xml))
6048 return JS_TRUE;
6050 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6051 if (!xml)
6052 return JS_FALSE;
6054 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6055 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6056 if (!kid)
6057 continue;
6058 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6059 kidobj = js_GetXMLObject(cx, kid);
6060 if (!kidobj || !xml_normalize_helper(cx, kidobj, kid))
6061 return JS_FALSE;
6062 } else if (kid->xml_class == JSXML_CLASS_TEXT) {
6063 while (i + 1 < n &&
6064 (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
6065 kid2->xml_class == JSXML_CLASS_TEXT) {
6066 str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
6067 if (!str)
6068 return JS_FALSE;
6069 NormalizingDelete(cx, xml, i + 1);
6070 n = xml->xml_kids.length;
6071 kid->xml_value = str;
6073 if (kid->xml_value->empty()) {
6074 NormalizingDelete(cx, xml, i);
6075 n = xml->xml_kids.length;
6076 --i;
6081 return JS_TRUE;
6084 static JSBool
6085 xml_normalize(JSContext *cx, uintN argc, jsval *vp)
6087 XML_METHOD_PROLOG;
6088 *vp = OBJECT_TO_JSVAL(obj);
6089 return xml_normalize_helper(cx, obj, xml);
6092 /* XML and XMLList */
6093 static JSBool
6094 xml_parent(JSContext *cx, uintN argc, jsval *vp)
6096 JSXML *parent, *kid;
6097 uint32 i, n;
6098 JSObject *parentobj;
6100 XML_METHOD_PROLOG;
6101 parent = xml->parent;
6102 if (xml->xml_class == JSXML_CLASS_LIST) {
6103 *vp = JSVAL_VOID;
6104 n = xml->xml_kids.length;
6105 if (n == 0)
6106 return JS_TRUE;
6108 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6109 if (!kid)
6110 return JS_TRUE;
6111 parent = kid->parent;
6112 for (i = 1; i < n; i++) {
6113 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6114 if (kid && kid->parent != parent)
6115 return JS_TRUE;
6119 if (!parent) {
6120 *vp = JSVAL_NULL;
6121 return JS_TRUE;
6124 parentobj = js_GetXMLObject(cx, parent);
6125 if (!parentobj)
6126 return JS_FALSE;
6127 *vp = OBJECT_TO_JSVAL(parentobj);
6128 return JS_TRUE;
6131 /* XML and XMLList */
6132 static JSBool
6133 xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6134 JSObject *nameqn, jsval *vp)
6136 JSXML *list, *vxml;
6137 JSBool ok;
6138 JSObject *kidobj;
6139 jsval v;
6140 uint32 i, n;
6142 list = xml_list_helper(cx, xml, vp);
6143 if (!list)
6144 return JS_FALSE;
6146 list->xml_targetprop = nameqn;
6147 ok = JS_TRUE;
6149 if (xml->xml_class == JSXML_CLASS_LIST) {
6150 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6151 JSXMLArrayCursor cursor(&xml->xml_kids);
6152 while (JSXML *kid = (JSXML *) cursor.getNext()) {
6153 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6154 ok = js_EnterLocalRootScope(cx);
6155 if (!ok)
6156 break;
6157 kidobj = js_GetXMLObject(cx, kid);
6158 if (kidobj) {
6159 ok = xml_processingInstructions_helper(cx, kidobj, kid,
6160 nameqn, &v);
6161 } else {
6162 ok = JS_FALSE;
6163 v = JSVAL_NULL;
6165 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
6166 if (!ok)
6167 break;
6168 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6169 if (JSXML_LENGTH(vxml) != 0) {
6170 ok = Append(cx, list, vxml);
6171 if (!ok)
6172 break;
6176 } else {
6177 /* 13.4.4.28 Step 4. */
6178 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6179 JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6180 if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
6181 JSLinearString *localName = nameqn->getQNameLocalName();
6182 if (IS_STAR(localName) ||
6183 EqualStrings(localName, kid->name->getQNameLocalName())) {
6184 ok = Append(cx, list, kid);
6185 if (!ok)
6186 break;
6192 return ok;
6195 static JSBool
6196 xml_processingInstructions(JSContext *cx, uintN argc, jsval *vp)
6198 jsval name;
6199 JSObject *nameqn;
6200 jsid funid;
6202 XML_METHOD_PROLOG;
6204 name = (argc == 0) ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
6205 nameqn = ToXMLName(cx, name, &funid);
6206 if (!nameqn)
6207 return JS_FALSE;
6208 vp[2] = OBJECT_TO_JSVAL(nameqn);
6210 if (!JSID_IS_VOID(funid))
6211 return xml_list_helper(cx, xml, vp) != NULL;
6213 return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp);
6216 static JSBool
6217 xml_prependChild(JSContext *cx, uintN argc, jsval *vp)
6219 NON_LIST_XML_METHOD_PROLOG;
6220 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6221 if (!xml)
6222 return JS_FALSE;
6223 *vp = OBJECT_TO_JSVAL(obj);
6224 return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID);
6227 /* XML and XMLList */
6228 static JSBool
6229 xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
6231 bool isIndex;
6232 uint32 index;
6234 XML_METHOD_PROLOG;
6235 *vp = JSVAL_FALSE;
6236 if (argc != 0) {
6237 if (!js_IdValIsIndex(cx, vp[2], &index, &isIndex))
6238 return JS_FALSE;
6240 if (isIndex) {
6241 if (xml->xml_class == JSXML_CLASS_LIST) {
6242 /* 13.5.4.18. */
6243 *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6244 } else {
6245 /* 13.4.4.30. */
6246 *vp = BOOLEAN_TO_JSVAL(index == 0);
6250 return JS_TRUE;
6253 static JSBool
6254 namespace_full_match(const void *a, const void *b)
6256 const JSObject *nsa = (const JSObject *) a;
6257 const JSObject *nsb = (const JSObject *) b;
6258 JSLinearString *prefixa = nsa->getNamePrefix();
6259 JSLinearString *prefixb;
6261 if (prefixa) {
6262 prefixb = nsb->getNamePrefix();
6263 if (prefixb && !EqualStrings(prefixa, prefixb))
6264 return JS_FALSE;
6266 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
6269 static JSBool
6270 xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns)
6272 JSObject *thisns, *attrns;
6273 uint32 i, n;
6274 JSXML *attr, *kid;
6276 thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6277 JS_ASSERT(thisns);
6278 if (thisns == ns)
6279 return JS_TRUE;
6281 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6282 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6283 if (!attr)
6284 continue;
6285 attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
6286 JS_ASSERT(attrns);
6287 if (attrns == ns)
6288 return JS_TRUE;
6291 i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
6292 if (i != XML_NOT_FOUND)
6293 XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
6295 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6296 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6297 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6298 if (!xml_removeNamespace_helper(cx, kid, ns))
6299 return JS_FALSE;
6302 return JS_TRUE;
6305 static JSBool
6306 xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp)
6308 JSObject *ns;
6310 NON_LIST_XML_METHOD_PROLOG;
6311 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6312 goto done;
6313 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6314 if (!xml)
6315 return JS_FALSE;
6317 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6318 return JS_FALSE;
6319 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6320 ns = JSVAL_TO_OBJECT(*vp);
6322 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6323 if (!xml_removeNamespace_helper(cx, xml, ns))
6324 return JS_FALSE;
6325 done:
6326 *vp = OBJECT_TO_JSVAL(obj);
6327 return JS_TRUE;
6330 static JSBool
6331 xml_replace(JSContext *cx, uintN argc, jsval *vp)
6333 jsval value;
6334 JSXML *vxml, *kid;
6335 uint32 index, i;
6336 JSObject *nameqn;
6338 NON_LIST_XML_METHOD_PROLOG;
6339 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6340 goto done;
6342 if (argc <= 1) {
6343 value = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6344 } else {
6345 value = vp[3];
6346 vxml = VALUE_IS_XML(value)
6347 ? (JSXML *) JSVAL_TO_OBJECT(value)->getPrivate()
6348 : NULL;
6349 if (!vxml) {
6350 if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
6351 return JS_FALSE;
6352 value = vp[3];
6353 } else {
6354 vxml = DeepCopy(cx, vxml, NULL, 0);
6355 if (!vxml)
6356 return JS_FALSE;
6357 value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
6361 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6362 if (!xml)
6363 return JS_FALSE;
6365 bool haveIndex;
6366 if (argc == 0) {
6367 haveIndex = false;
6368 } else {
6369 if (!js_IdValIsIndex(cx, vp[2], &index, &haveIndex))
6370 return JS_FALSE;
6373 if (!haveIndex) {
6375 * Call function QName per spec, not ToXMLName, to avoid attribute
6376 * names.
6378 if (!QNameHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6379 return JS_FALSE;
6380 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6381 nameqn = JSVAL_TO_OBJECT(*vp);
6383 i = xml->xml_kids.length;
6384 index = XML_NOT_FOUND;
6385 while (i != 0) {
6386 --i;
6387 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6388 if (kid && MatchElemName(nameqn, kid)) {
6389 if (i != XML_NOT_FOUND)
6390 DeleteByIndex(cx, xml, i);
6391 index = i;
6395 if (index == XML_NOT_FOUND)
6396 goto done;
6399 if (!Replace(cx, xml, index, value))
6400 return JS_FALSE;
6402 done:
6403 *vp = OBJECT_TO_JSVAL(obj);
6404 return JS_TRUE;
6407 static JSBool
6408 xml_setChildren(JSContext *cx, uintN argc, jsval *vp)
6410 JSObject *obj;
6412 if (!StartNonListXMLMethod(cx, vp, &obj))
6413 return JS_FALSE;
6415 *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */
6416 if (!PutProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.starAtom), false, vp))
6417 return JS_FALSE;
6419 *vp = OBJECT_TO_JSVAL(obj);
6420 return JS_TRUE;
6423 static JSBool
6424 xml_setLocalName(JSContext *cx, uintN argc, jsval *vp)
6426 jsval name;
6427 JSObject *nameqn;
6428 JSLinearString *namestr;
6430 NON_LIST_XML_METHOD_PROLOG;
6431 if (!JSXML_HAS_NAME(xml))
6432 return JS_TRUE;
6434 if (argc == 0) {
6435 namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6436 } else {
6437 name = vp[2];
6438 if (!JSVAL_IS_PRIMITIVE(name) &&
6439 JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) {
6440 nameqn = JSVAL_TO_OBJECT(name);
6441 namestr = nameqn->getQNameLocalName();
6442 } else {
6443 if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
6444 return JS_FALSE;
6445 name = vp[2];
6446 namestr = JSVAL_TO_STRING(name)->ensureLinear(cx);
6447 if (!namestr)
6448 return JS_FALSE;
6452 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6453 if (!xml)
6454 return JS_FALSE;
6455 if (namestr)
6456 xml->name->setQNameLocalName(namestr);
6457 return JS_TRUE;
6460 static JSBool
6461 xml_setName(JSContext *cx, uintN argc, jsval *vp)
6463 jsval name;
6464 JSObject *nameqn;
6465 JSXML *nsowner;
6466 JSXMLArray *nsarray;
6467 uint32 i, n;
6468 JSObject *ns;
6470 NON_LIST_XML_METHOD_PROLOG;
6471 if (!JSXML_HAS_NAME(xml))
6472 return JS_TRUE;
6474 if (argc == 0) {
6475 name = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6476 } else {
6477 name = vp[2];
6478 if (!JSVAL_IS_PRIMITIVE(name) &&
6479 JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass &&
6480 !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) {
6481 name = vp[2] = nameqn->getQNameLocalNameVal();
6485 nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&name));
6486 if (!nameqn)
6487 return JS_FALSE;
6489 /* ECMA-357 13.4.4.35 Step 4. */
6490 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6491 nameqn->setNameURI(cx->runtime->emptyString);
6493 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6494 if (!xml)
6495 return JS_FALSE;
6496 xml->name = nameqn;
6499 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6500 * in-scope namespaces, either by finding an in-scope namespace with a
6501 * matching uri and setting the new name's prefix to that namespace's
6502 * prefix, or by extending the in-scope namespaces for xml (which are in
6503 * xml->parent if xml is an attribute or a PI).
6505 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6506 nsowner = xml;
6507 } else {
6508 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6509 return JS_TRUE;
6510 nsowner = xml->parent;
6513 if (nameqn->getNamePrefix()) {
6515 * The name being set has a prefix, which originally came from some
6516 * namespace object (which may be the null namespace, where both the
6517 * prefix and uri are the empty string). We must go through a full
6518 * GetNamespace in case that namespace is in-scope in nsowner.
6520 * If we find such an in-scope namespace, we return true right away,
6521 * in this block. Otherwise, we fall through to the final return of
6522 * AddInScopeNamespace(cx, nsowner, ns).
6524 ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
6525 if (!ns)
6526 return JS_FALSE;
6528 /* XXXbe have to test membership to see whether GetNamespace added */
6529 if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
6530 return JS_TRUE;
6531 } else {
6533 * At this point, we know prefix of nameqn is null, so its uri can't
6534 * be the empty string (the null namespace always uses the empty string
6535 * for both prefix and uri).
6537 * This means we must inline GetNamespace and specialize it to match
6538 * uri only, never prefix. If we find a namespace with nameqn's uri
6539 * already in nsowner->xml_namespaces, then all that we need do is set
6540 * prefix of nameqn to that namespace's prefix.
6542 * If no such namespace exists, we can create one without going through
6543 * the constructor, because we know uri of nameqn is non-empty (so
6544 * prefix does not need to be converted from null to empty by QName).
6546 JS_ASSERT(!nameqn->getNameURI()->empty());
6548 nsarray = &nsowner->xml_namespaces;
6549 for (i = 0, n = nsarray->length; i < n; i++) {
6550 ns = XMLARRAY_MEMBER(nsarray, i, JSObject);
6551 if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) {
6552 nameqn->setNamePrefix(ns->getNamePrefix());
6553 return JS_TRUE;
6557 ns = NewXMLNamespace(cx, NULL, nameqn->getNameURI(), JS_TRUE);
6558 if (!ns)
6559 return JS_FALSE;
6562 if (!AddInScopeNamespace(cx, nsowner, ns))
6563 return JS_FALSE;
6564 vp[0] = JSVAL_VOID;
6565 return JS_TRUE;
6568 static JSBool
6569 xml_setNamespace(JSContext *cx, uintN argc, jsval *vp)
6571 JSObject *qn;
6572 JSObject *ns;
6573 jsval qnargv[2];
6574 JSXML *nsowner;
6576 NON_LIST_XML_METHOD_PROLOG;
6577 if (!JSXML_HAS_NAME(xml))
6578 return JS_TRUE;
6580 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6581 if (!xml)
6582 return JS_FALSE;
6584 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj,
6585 argc == 0 ? 0 : 1, Valueify(vp + 2));
6586 if (!ns)
6587 return JS_FALSE;
6588 vp[0] = OBJECT_TO_JSVAL(ns);
6589 ns->setNamespaceDeclared(JSVAL_TRUE);
6591 qnargv[0] = OBJECT_TO_JSVAL(ns);
6592 qnargv[1] = OBJECT_TO_JSVAL(xml->name);
6593 qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv));
6594 if (!qn)
6595 return JS_FALSE;
6597 xml->name = qn;
6600 * Erratum: the spec fails to update the governing in-scope namespaces.
6601 * See the erratum noted in xml_setName, above.
6603 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6604 nsowner = xml;
6605 } else {
6606 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6607 return JS_TRUE;
6608 nsowner = xml->parent;
6610 if (!AddInScopeNamespace(cx, nsowner, ns))
6611 return JS_FALSE;
6612 vp[0] = JSVAL_VOID;
6613 return JS_TRUE;
6616 /* XML and XMLList */
6617 static JSBool
6618 xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
6620 JSXML *list, *kid, *vxml;
6621 uint32 i, n;
6622 JSBool ok;
6623 JSObject *kidobj;
6624 jsval v;
6626 list = xml_list_helper(cx, xml, vp);
6627 if (!list)
6628 return JS_FALSE;
6630 if (xml->xml_class == JSXML_CLASS_LIST) {
6631 ok = JS_TRUE;
6632 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6633 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6634 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6635 ok = js_EnterLocalRootScope(cx);
6636 if (!ok)
6637 break;
6638 kidobj = js_GetXMLObject(cx, kid);
6639 if (kidobj) {
6640 ok = xml_text_helper(cx, kidobj, kid, &v);
6641 } else {
6642 ok = JS_FALSE;
6643 v = JSVAL_NULL;
6645 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
6646 if (!ok)
6647 return JS_FALSE;
6648 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6649 if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
6650 return JS_FALSE;
6653 } else {
6654 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6655 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6656 if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
6657 if (!Append(cx, list, kid))
6658 return JS_FALSE;
6662 return JS_TRUE;
6665 static JSBool
6666 xml_text(JSContext *cx, uintN argc, jsval *vp)
6668 XML_METHOD_PROLOG;
6669 return xml_text_helper(cx, obj, xml, vp);
6672 /* XML and XMLList */
6673 static JSString *
6674 xml_toString_helper(JSContext *cx, JSXML *xml)
6676 JSString *str, *kidstr;
6678 if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
6679 xml->xml_class == JSXML_CLASS_TEXT) {
6680 return xml->xml_value;
6683 if (!HasSimpleContent(xml))
6684 return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0);
6686 str = cx->runtime->emptyString;
6687 if (!js_EnterLocalRootScope(cx))
6688 return NULL;
6689 JSXMLArrayCursor cursor(&xml->xml_kids);
6690 while (JSXML *kid = (JSXML *) cursor.getNext()) {
6691 if (kid->xml_class != JSXML_CLASS_COMMENT &&
6692 kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
6693 kidstr = xml_toString_helper(cx, kid);
6694 if (!kidstr) {
6695 str = NULL;
6696 break;
6698 str = js_ConcatStrings(cx, str, kidstr);
6699 if (!str)
6700 break;
6703 js_LeaveLocalRootScopeWithResult(cx, str);
6704 return str;
6707 static JSBool
6708 xml_toSource(JSContext *cx, uintN argc, jsval *vp)
6710 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6711 if (!obj)
6712 return JS_FALSE;
6713 JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), TO_SOURCE_FLAG);
6714 if (!str)
6715 return JS_FALSE;
6716 *vp = STRING_TO_JSVAL(str);
6717 return JS_TRUE;
6720 static JSBool
6721 xml_toString(JSContext *cx, uintN argc, jsval *vp)
6723 JSString *str;
6725 XML_METHOD_PROLOG;
6726 str = xml_toString_helper(cx, xml);
6727 if (!str)
6728 return JS_FALSE;
6729 *vp = STRING_TO_JSVAL(str);
6730 return JS_TRUE;
6733 /* XML and XMLList */
6734 static JSBool
6735 xml_toXMLString(JSContext *cx, uintN argc, jsval *vp)
6737 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6738 if (!obj)
6739 return JS_FALSE;
6740 JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), 0);
6741 if (!str)
6742 return JS_FALSE;
6743 *vp = STRING_TO_JSVAL(str);
6744 return JS_TRUE;
6747 /* XML and XMLList */
6748 static JSBool
6749 xml_valueOf(JSContext *cx, uintN argc, jsval *vp)
6751 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6752 if (!obj)
6753 return false;
6754 *vp = OBJECT_TO_JSVAL(obj);
6755 return true;
6758 static JSFunctionSpec xml_methods[] = {
6759 JS_FN("addNamespace", xml_addNamespace, 1,0),
6760 JS_FN("appendChild", xml_appendChild, 1,0),
6761 JS_FN(js_attribute_str, xml_attribute, 1,0),
6762 JS_FN("attributes", xml_attributes, 0,0),
6763 JS_FN("child", xml_child, 1,0),
6764 JS_FN("childIndex", xml_childIndex, 0,0),
6765 JS_FN("children", xml_children, 0,0),
6766 JS_FN("comments", xml_comments, 0,0),
6767 JS_FN("contains", xml_contains, 1,0),
6768 JS_FN("copy", xml_copy, 0,0),
6769 JS_FN("descendants", xml_descendants, 1,0),
6770 JS_FN("elements", xml_elements, 1,0),
6771 JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0),
6772 JS_FN("hasComplexContent", xml_hasComplexContent, 1,0),
6773 JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0),
6774 JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0),
6775 JS_FN("insertChildAfter", xml_insertChildAfter, 2,0),
6776 JS_FN("insertChildBefore", xml_insertChildBefore, 2,0),
6777 JS_FN(js_length_str, xml_length, 0,0),
6778 JS_FN(js_localName_str, xml_localName, 0,0),
6779 JS_FN(js_name_str, xml_name, 0,0),
6780 JS_FN(js_namespace_str, xml_namespace, 1,0),
6781 JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0),
6782 JS_FN("nodeKind", xml_nodeKind, 0,0),
6783 JS_FN("normalize", xml_normalize, 0,0),
6784 JS_FN(js_xml_parent_str, xml_parent, 0,0),
6785 JS_FN("processingInstructions",xml_processingInstructions,1,0),
6786 JS_FN("prependChild", xml_prependChild, 1,0),
6787 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0),
6788 JS_FN("removeNamespace", xml_removeNamespace, 1,0),
6789 JS_FN("replace", xml_replace, 2,0),
6790 JS_FN("setChildren", xml_setChildren, 1,0),
6791 JS_FN("setLocalName", xml_setLocalName, 1,0),
6792 JS_FN("setName", xml_setName, 1,0),
6793 JS_FN("setNamespace", xml_setNamespace, 1,0),
6794 JS_FN(js_text_str, xml_text, 0,0),
6795 JS_FN(js_toSource_str, xml_toSource, 0,0),
6796 JS_FN(js_toString_str, xml_toString, 0,0),
6797 JS_FN(js_toXMLString_str, xml_toXMLString, 0,0),
6798 JS_FN(js_valueOf_str, xml_valueOf, 0,0),
6799 JS_FS_END
6802 static JSBool
6803 CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
6805 int i;
6806 const char *name;
6807 jsval v;
6809 /* Note: PRETTY_INDENT is not a boolean setting. */
6810 for (i = 0; xml_static_props[i].name; i++) {
6811 name = xml_static_props[i].name;
6812 if (!JS_GetProperty(cx, from, name, &v))
6813 return false;
6814 if (name == js_prettyIndent_str) {
6815 if (!JSVAL_IS_NUMBER(v))
6816 continue;
6817 } else {
6818 if (!JSVAL_IS_BOOLEAN(v))
6819 continue;
6821 if (!JS_SetProperty(cx, to, name, &v))
6822 return false;
6825 return true;
6828 static JSBool
6829 SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
6831 int i;
6832 jsval v;
6834 /* Note: PRETTY_INDENT is not a boolean setting. */
6835 for (i = 0; xml_static_props[i].name; i++) {
6836 v = (xml_static_props[i].name != js_prettyIndent_str)
6837 ? JSVAL_TRUE : INT_TO_JSVAL(2);
6838 if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
6839 return JS_FALSE;
6841 return true;
6844 static JSBool
6845 xml_settings(JSContext *cx, uintN argc, jsval *vp)
6847 JSObject *settings = JS_NewObject(cx, NULL, NULL, NULL);
6848 if (!settings)
6849 return false;
6850 *vp = OBJECT_TO_JSVAL(settings);
6851 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6852 if (!obj)
6853 return false;
6854 return CopyXMLSettings(cx, obj, settings);
6857 static JSBool
6858 xml_setSettings(JSContext *cx, uintN argc, jsval *vp)
6860 JSObject *settings;
6861 jsval v;
6862 JSBool ok;
6864 JSObject *obj = ToObject(cx, Valueify(&vp[1]));
6865 if (!obj)
6866 return JS_FALSE;
6867 v = (argc == 0) ? JSVAL_VOID : vp[2];
6868 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
6869 ok = SetDefaultXMLSettings(cx, obj);
6870 } else {
6871 if (JSVAL_IS_PRIMITIVE(v))
6872 return JS_TRUE;
6873 settings = JSVAL_TO_OBJECT(v);
6874 ok = CopyXMLSettings(cx, settings, obj);
6876 return ok;
6879 static JSBool
6880 xml_defaultSettings(JSContext *cx, uintN argc, jsval *vp)
6882 JSObject *settings;
6884 settings = JS_NewObject(cx, NULL, NULL, NULL);
6885 if (!settings)
6886 return JS_FALSE;
6887 *vp = OBJECT_TO_JSVAL(settings);
6888 return SetDefaultXMLSettings(cx, settings);
6891 static JSFunctionSpec xml_static_methods[] = {
6892 JS_FN("settings", xml_settings, 0,0),
6893 JS_FN("setSettings", xml_setSettings, 1,0),
6894 JS_FN("defaultSettings", xml_defaultSettings, 0,0),
6895 JS_FS_END
6898 static JSBool
6899 XML(JSContext *cx, uintN argc, Value *vp)
6901 JSXML *xml, *copy;
6902 JSObject *xobj, *vobj;
6903 Class *clasp;
6905 jsval v = argc ? Jsvalify(vp[2]) : JSVAL_VOID;
6907 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
6908 v = STRING_TO_JSVAL(cx->runtime->emptyString);
6910 xobj = ToXML(cx, v);
6911 if (!xobj)
6912 return JS_FALSE;
6913 xml = (JSXML *) xobj->getPrivate();
6915 if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) {
6916 vobj = JSVAL_TO_OBJECT(v);
6917 clasp = vobj->getClass();
6918 if (clasp == &js_XMLClass ||
6919 (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
6920 copy = DeepCopy(cx, xml, NULL, 0);
6921 if (!copy)
6922 return JS_FALSE;
6923 vp->setObject(*copy->object);
6924 return JS_TRUE;
6928 vp->setObject(*xobj);
6929 return JS_TRUE;
6932 static JSBool
6933 XMLList(JSContext *cx, uintN argc, jsval *vp)
6935 JSObject *vobj, *listobj;
6936 JSXML *xml, *list;
6938 jsval v = argc ? vp[2] : JSVAL_VOID;
6940 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
6941 v = STRING_TO_JSVAL(cx->runtime->emptyString);
6943 if (IsConstructing(Valueify(vp)) && !JSVAL_IS_PRIMITIVE(v)) {
6944 vobj = JSVAL_TO_OBJECT(v);
6945 if (vobj->isXML()) {
6946 xml = (JSXML *) vobj->getPrivate();
6947 if (xml->xml_class == JSXML_CLASS_LIST) {
6948 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
6949 if (!listobj)
6950 return JS_FALSE;
6951 *vp = OBJECT_TO_JSVAL(listobj);
6953 list = (JSXML *) listobj->getPrivate();
6954 if (!Append(cx, list, xml))
6955 return JS_FALSE;
6956 return JS_TRUE;
6961 /* Toggle on XML support since the script has explicitly requested it. */
6962 listobj = ToXMLList(cx, v);
6963 if (!listobj)
6964 return JS_FALSE;
6966 *vp = OBJECT_TO_JSVAL(listobj);
6967 return JS_TRUE;
6970 #ifdef DEBUG_notme
6971 JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
6972 uint32 xml_serial;
6973 #endif
6975 JSXML *
6976 js_NewXML(JSContext *cx, JSXMLClass xml_class)
6978 JSXML *xml = js_NewGCXML(cx);
6979 if (!xml)
6980 return NULL;
6982 xml->object = NULL;
6983 xml->domnode = NULL;
6984 xml->parent = NULL;
6985 xml->name = NULL;
6986 xml->xml_class = xml_class;
6987 xml->xml_flags = 0;
6988 if (JSXML_CLASS_HAS_VALUE(xml_class)) {
6989 xml->xml_value = cx->runtime->emptyString;
6990 } else {
6991 xml->xml_kids.init();
6992 if (xml_class == JSXML_CLASS_LIST) {
6993 xml->xml_target = NULL;
6994 xml->xml_targetprop = NULL;
6995 } else {
6996 xml->xml_namespaces.init();
6997 xml->xml_attrs.init();
7001 #ifdef DEBUG_notme
7002 JS_APPEND_LINK(&xml->links, &xml_leaks);
7003 xml->serial = xml_serial++;
7004 #endif
7005 METER(xml_stats.xml);
7006 return xml;
7009 void
7010 js_TraceXML(JSTracer *trc, JSXML *xml)
7012 if (xml->object)
7013 MarkObject(trc, *xml->object, "object");
7014 if (xml->name)
7015 MarkObject(trc, *xml->name, "name");
7016 if (xml->parent)
7017 JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent");
7019 if (JSXML_HAS_VALUE(xml)) {
7020 if (xml->xml_value)
7021 MarkString(trc, xml->xml_value, "value");
7022 return;
7025 xml_trace_vector(trc,
7026 (JSXML **) xml->xml_kids.vector,
7027 xml->xml_kids.length);
7028 XMLArrayCursorTrace(trc, xml->xml_kids.cursors);
7029 if (IS_GC_MARKING_TRACER(trc))
7030 xml->xml_kids.trim();
7032 if (xml->xml_class == JSXML_CLASS_LIST) {
7033 if (xml->xml_target)
7034 JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target");
7035 if (xml->xml_targetprop)
7036 MarkObject(trc, *xml->xml_targetprop, "targetprop");
7037 } else {
7038 MarkObjectRange(trc, xml->xml_namespaces.length,
7039 (JSObject **) xml->xml_namespaces.vector,
7040 "xml_namespaces");
7041 XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
7042 if (IS_GC_MARKING_TRACER(trc))
7043 xml->xml_namespaces.trim();
7045 xml_trace_vector(trc,
7046 (JSXML **) xml->xml_attrs.vector,
7047 xml->xml_attrs.length);
7048 XMLArrayCursorTrace(trc, xml->xml_attrs.cursors);
7049 if (IS_GC_MARKING_TRACER(trc))
7050 xml->xml_attrs.trim();
7054 JSObject *
7055 js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7057 JSXML *xml = js_NewXML(cx, xml_class);
7058 if (!xml)
7059 return NULL;
7061 AutoXMLRooter root(cx, xml);
7062 return js_GetXMLObject(cx, xml);
7065 static JSObject *
7066 NewXMLObject(JSContext *cx, JSXML *xml)
7068 JSObject *obj;
7070 obj = NewNonFunction<WithProto::Class>(cx, &js_XMLClass, NULL, NULL);
7071 if (!obj)
7072 return NULL;
7073 obj->setPrivate(xml);
7074 METER(xml_stats.xmlobj);
7075 return obj;
7078 JSObject *
7079 js_GetXMLObject(JSContext *cx, JSXML *xml)
7081 JSObject *obj;
7083 obj = xml->object;
7084 if (obj) {
7085 JS_ASSERT(obj->getPrivate() == xml);
7086 return obj;
7089 obj = NewXMLObject(cx, xml);
7090 if (!obj)
7091 return NULL;
7092 xml->object = obj;
7093 return obj;
7096 JSObject *
7097 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7099 return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2,
7100 namespace_props, namespace_methods, NULL, NULL);
7103 JSObject *
7104 js_InitQNameClass(JSContext *cx, JSObject *obj)
7106 return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
7107 qname_props, qname_methods, NULL, NULL);
7110 JSObject *
7111 js_InitXMLClass(JSContext *cx, JSObject *obj)
7113 JSObject *proto, *pobj;
7114 JSFunction *fun;
7115 JSXML *xml;
7116 JSProperty *prop;
7117 Shape *shape;
7118 jsval cval, vp[3];
7120 /* Define the isXMLName function. */
7121 if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7122 return NULL;
7124 /* Define the XML class constructor and prototype. */
7125 proto = js_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
7126 NULL, xml_methods,
7127 xml_static_props, xml_static_methods);
7128 if (!proto)
7129 return NULL;
7131 xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7132 if (!xml)
7133 return NULL;
7134 proto->setPrivate(xml);
7135 xml->object = proto;
7136 METER(xml_stats.xmlobj);
7139 * Prepare to set default settings on the XML constructor we just made.
7140 * NB: We can't use JS_GetConstructor, because it calls
7141 * JSObject::getProperty, which is xml_getProperty, which creates a new
7142 * XMLList every time! We must instead call js_LookupProperty directly.
7144 if (!js_LookupProperty(cx, proto,
7145 ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
7146 &pobj, &prop)) {
7147 return NULL;
7149 JS_ASSERT(prop);
7150 shape = (Shape *) prop;
7151 cval = Jsvalify(pobj->nativeGetSlot(shape->slot));
7152 JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
7154 /* Set default settings. */
7155 vp[0] = JSVAL_NULL;
7156 vp[1] = cval;
7157 vp[2] = JSVAL_VOID;
7158 if (!xml_setSettings(cx, 1, vp))
7159 return NULL;
7161 /* Define the XMLList function and give it the same prototype as XML. */
7162 fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR);
7163 if (!fun)
7164 return NULL;
7165 if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
7166 JSPROP_READONLY | JSPROP_PERMANENT)) {
7167 return NULL;
7169 return proto;
7172 JSObject *
7173 js_InitXMLClasses(JSContext *cx, JSObject *obj)
7175 if (!js_InitNamespaceClass(cx, obj))
7176 return NULL;
7177 if (!js_InitQNameClass(cx, obj))
7178 return NULL;
7179 return js_InitXMLClass(cx, obj);
7182 JSBool
7183 js_GetFunctionNamespace(JSContext *cx, Value *vp)
7185 JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
7187 *vp = global->getReservedSlot(JSRESERVED_GLOBAL_FUNCTION_NS);
7188 if (vp->isUndefined()) {
7189 JSRuntime *rt = cx->runtime;
7190 JSLinearString *prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION];
7191 JSLinearString *uri = rt->atomState.functionNamespaceURIAtom;
7192 JSObject *obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE);
7193 if (!obj)
7194 return false;
7197 * Avoid entraining any in-scope Object.prototype. The loss of
7198 * Namespace.prototype is not detectable, as there is no way to
7199 * refer to this instance in scripts. When used to qualify method
7200 * names, its prefix and uri references are copied to the QName.
7201 * The parent remains set and links back to global.
7203 obj->clearProto();
7205 vp->setObject(*obj);
7206 if (!js_SetReservedSlot(cx, global, JSRESERVED_GLOBAL_FUNCTION_NS, *vp))
7207 return false;
7210 return true;
7214 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7215 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7216 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
7217 * requirement that fp->varobj lie directly on fp->scopeChain, although
7218 * it should be reachable using the prototype chain from a scope object (cf.
7219 * JSOPTION_VAROBJFIX in jsapi.h).
7221 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7222 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7223 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7224 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7225 * the default XML namespace will be set to ("", n.uri). So the uri string
7226 * is really the only usefully stored value of the default namespace.
7228 JSBool
7229 js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7231 JSObject *ns, *obj, *tmp;
7232 jsval v;
7234 JSObject *scopeChain = GetScopeChain(cx);
7236 obj = NULL;
7237 for (tmp = scopeChain; tmp; tmp = tmp->getParent()) {
7238 Class *clasp = tmp->getClass();
7239 if (clasp == &js_BlockClass || clasp == &js_WithClass)
7240 continue;
7241 if (!tmp->getProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(&v)))
7242 return JS_FALSE;
7243 if (!JSVAL_IS_PRIMITIVE(v)) {
7244 *vp = v;
7245 return JS_TRUE;
7247 obj = tmp;
7250 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, 0, NULL);
7251 if (!ns)
7252 return JS_FALSE;
7253 v = OBJECT_TO_JSVAL(ns);
7254 if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(v),
7255 PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) {
7256 return JS_FALSE;
7258 *vp = v;
7259 return JS_TRUE;
7262 JSBool
7263 js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
7265 Value argv[2];
7266 argv[0].setString(cx->runtime->emptyString);
7267 argv[1] = v;
7268 JSObject *ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, 2, argv);
7269 if (!ns)
7270 return JS_FALSE;
7272 JSStackFrame *fp = js_GetTopStackFrame(cx);
7273 JSObject &varobj = fp->varobj(cx);
7274 if (!varobj.defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns),
7275 PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) {
7276 return JS_FALSE;
7278 return JS_TRUE;
7281 JSBool
7282 js_ToAttributeName(JSContext *cx, Value *vp)
7284 JSObject *qn;
7286 qn = ToAttributeName(cx, Jsvalify(*vp));
7287 if (!qn)
7288 return JS_FALSE;
7289 vp->setObject(*qn);
7290 return JS_TRUE;
7293 JSFlatString *
7294 js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote)
7296 StringBuffer sb(cx);
7297 return EscapeAttributeValue(cx, sb, str, quote);
7300 JSString *
7301 js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7303 size_t len = str->length();
7304 const jschar *chars = str->getChars(cx);
7305 if (!chars)
7306 return NULL;
7308 size_t len2 = str2->length();
7309 const jschar *chars2 = str2->getChars(cx);
7310 if (!chars2)
7311 return NULL;
7313 size_t newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
7314 jschar *newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar));
7315 if (!newchars)
7316 return NULL;
7318 js_strncpy(newchars, chars, len);
7319 newchars += len;
7320 if (isName) {
7321 *newchars++ = ' ';
7322 js_strncpy(newchars, chars2, len2);
7323 newchars += len2;
7324 } else {
7325 *newchars++ = '=';
7326 *newchars++ = '"';
7327 js_strncpy(newchars, chars2, len2);
7328 newchars += len2;
7329 *newchars++ = '"';
7331 *newchars = 0;
7332 return js_NewString(cx, newchars - newlen, newlen);
7335 JSFlatString *
7336 js_EscapeElementValue(JSContext *cx, JSString *str)
7338 StringBuffer sb(cx);
7339 return EscapeElementValue(cx, sb, str, 0);
7342 JSString *
7343 js_ValueToXMLString(JSContext *cx, const Value &v)
7345 return ToXMLString(cx, Jsvalify(v), 0);
7348 JSBool
7349 js_GetAnyName(JSContext *cx, jsid *idp)
7351 JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
7352 Value v = global->getReservedSlot(JSProto_AnyName);
7353 if (v.isUndefined()) {
7354 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AnyNameClass, NULL, global);
7355 if (!obj)
7356 return false;
7358 JS_ASSERT(!obj->getProto());
7360 JSRuntime *rt = cx->runtime;
7361 InitXMLQName(obj, rt->emptyString, rt->emptyString,
7362 ATOM_TO_STRING(rt->atomState.starAtom));
7363 METER(xml_stats.qname);
7365 v.setObject(*obj);
7366 if (!js_SetReservedSlot(cx, global, JSProto_AnyName, v))
7367 return false;
7369 *idp = OBJECT_TO_JSID(&v.toObject());
7370 return true;
7373 JSBool
7374 js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *idp)
7376 JSObject *nameobj;
7377 jsval v;
7378 JSObject *qn;
7379 jsid funid;
7380 JSObject *obj, *target, *proto, *pobj;
7381 JSXML *xml;
7382 JSBool found;
7383 JSProperty *prop;
7385 JS_ASSERT(nameval.isObject());
7386 nameobj = &nameval.toObject();
7387 if (nameobj->getClass() == &js_AnyNameClass) {
7388 v = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
7389 nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1,
7390 Valueify(&v));
7391 if (!nameobj)
7392 return JS_FALSE;
7393 } else {
7394 JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
7395 nameobj->getClass() == &js_QNameClass);
7398 qn = nameobj;
7399 if (!IsFunctionQName(cx, qn, &funid))
7400 return JS_FALSE;
7402 obj = &js_GetTopStackFrame(cx)->scopeChain();
7403 do {
7404 /* Skip any With object that can wrap XML. */
7405 target = obj;
7406 while (target->getClass() == &js_WithClass) {
7407 proto = target->getProto();
7408 if (!proto)
7409 break;
7410 target = proto;
7413 if (target->isXML()) {
7414 if (JSID_IS_VOID(funid)) {
7415 xml = (JSXML *) target->getPrivate();
7416 found = HasNamedProperty(xml, qn);
7417 } else {
7418 if (!HasFunctionProperty(cx, target, funid, &found))
7419 return JS_FALSE;
7421 if (found) {
7422 *idp = OBJECT_TO_JSID(nameobj);
7423 *objp = target;
7424 return JS_TRUE;
7426 } else if (!JSID_IS_VOID(funid)) {
7427 if (!target->lookupProperty(cx, funid, &pobj, &prop))
7428 return JS_FALSE;
7429 if (prop) {
7430 *idp = funid;
7431 *objp = target;
7432 return JS_TRUE;
7435 } while ((obj = obj->getParent()) != NULL);
7437 JSAutoByteString printable;
7438 JSString *str = ConvertQNameToString(cx, nameobj);
7439 if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
7440 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
7441 JSMSG_UNDEFINED_XML_NAME, printable.ptr());
7443 return JS_FALSE;
7446 static JSBool
7447 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
7449 JS_ASSERT(obj->isXML());
7452 * See comments before xml_lookupProperty about the need for the proto
7453 * chain lookup.
7455 JSObject *target = obj;
7456 AutoObjectRooter tvr(cx);
7457 for (;;) {
7458 if (!js_GetProperty(cx, target, id, Valueify(vp)))
7459 return false;
7460 if (VALUE_IS_FUNCTION(cx, *vp))
7461 return true;
7462 target = target->getProto();
7463 if (target == NULL || !target->isNative())
7464 break;
7465 tvr.setObject(target);
7468 JSXML *xml = (JSXML *) obj->getPrivate();
7469 if (!HasSimpleContent(xml))
7470 return true;
7472 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
7473 if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
7474 return false;
7476 JS_ASSERT(tvr.object());
7477 return tvr.object()->getProperty(cx, id, Valueify(vp));
7480 static JSXML *
7481 GetPrivate(JSContext *cx, JSObject *obj, const char *method)
7483 JSXML *xml;
7485 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
7486 if (!xml) {
7487 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7488 JSMSG_INCOMPATIBLE_METHOD,
7489 js_XML_str, method, obj->getClass()->name);
7491 return xml;
7494 JSBool
7495 js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
7497 JSXML *xml, *list;
7499 xml = GetPrivate(cx, obj, "descendants internal method");
7500 if (!xml)
7501 return JS_FALSE;
7503 list = Descendants(cx, xml, id);
7504 if (!list)
7505 return JS_FALSE;
7506 *vp = OBJECT_TO_JSVAL(list->object);
7507 return JS_TRUE;
7510 JSBool
7511 js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
7513 JSXML *list;
7514 uint32 n;
7516 list = (JSXML *) listobj->getPrivate();
7517 for (n = list->xml_kids.length; n != 0; --n)
7518 DeleteListElement(cx, list, 0);
7520 return JS_TRUE;
7523 struct JSXMLFilter
7525 JSXML *list;
7526 JSXML *result;
7527 JSXML *kid;
7528 JSXMLArrayCursor cursor;
7530 JSXMLFilter(JSXML *list, JSXMLArray *array)
7531 : list(list), result(NULL), kid(NULL), cursor(array) {}
7533 ~JSXMLFilter() {}
7536 static void
7537 xmlfilter_trace(JSTracer *trc, JSObject *obj)
7539 JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7540 if (!filter)
7541 return;
7543 JS_ASSERT(filter->list);
7544 JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list");
7545 if (filter->result)
7546 JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result");
7547 if (filter->kid)
7548 JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid");
7551 * We do not need to trace the cursor as that would be done when
7552 * tracing the filter->list.
7556 static void
7557 xmlfilter_finalize(JSContext *cx, JSObject *obj)
7559 JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7560 if (!filter)
7561 return;
7563 cx->destroy(filter);
7566 Class js_XMLFilterClass = {
7567 "XMLFilter",
7568 JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
7569 PropertyStub, /* addProperty */
7570 PropertyStub, /* delProperty */
7571 PropertyStub, /* getProperty */
7572 StrictPropertyStub, /* setProperty */
7573 EnumerateStub,
7574 ResolveStub,
7575 ConvertStub,
7576 xmlfilter_finalize,
7577 NULL, /* reserved0 */
7578 NULL, /* checkAccess */
7579 NULL, /* call */
7580 NULL, /* construct */
7581 NULL, /* xdrObject */
7582 NULL, /* hasInstance */
7583 JS_CLASS_TRACE(xmlfilter_trace)
7586 JSBool
7587 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
7589 jsval *sp;
7590 JSObject *obj, *filterobj, *resobj, *kidobj;
7591 JSXML *xml, *list;
7592 JSXMLFilter *filter;
7594 LeaveTrace(cx);
7595 sp = Jsvalify(cx->regs->sp);
7596 if (!initialized) {
7598 * We haven't iterated yet, so initialize the filter based on the
7599 * value stored in sp[-2].
7601 if (!VALUE_IS_XML(sp[-2])) {
7602 js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, Valueify(sp[-2]), NULL);
7603 return JS_FALSE;
7605 obj = JSVAL_TO_OBJECT(sp[-2]);
7606 xml = (JSXML *) obj->getPrivate();
7608 if (xml->xml_class == JSXML_CLASS_LIST) {
7609 list = xml;
7610 } else {
7611 obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7612 if (!obj)
7613 return JS_FALSE;
7616 * Root just-created obj. sp[-2] cannot be used yet for rooting
7617 * as it may be the only root holding xml.
7619 sp[-1] = OBJECT_TO_JSVAL(obj);
7620 list = (JSXML *) obj->getPrivate();
7621 if (!Append(cx, list, xml))
7622 return JS_FALSE;
7625 filterobj = NewNonFunction<WithProto::Given>(cx, &js_XMLFilterClass, NULL, NULL);
7626 if (!filterobj)
7627 return JS_FALSE;
7630 * Init all filter fields before setPrivate exposes it to
7631 * xmlfilter_trace or xmlfilter_finalize.
7633 filter = cx->create<JSXMLFilter>(list, &list->xml_kids);
7634 if (!filter)
7635 return JS_FALSE;
7636 filterobj->setPrivate(filter);
7638 /* Store filterobj to use in the later iterations. */
7639 sp[-2] = OBJECT_TO_JSVAL(filterobj);
7641 resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7642 if (!resobj)
7643 return JS_FALSE;
7645 /* This also roots resobj. */
7646 filter->result = (JSXML *) resobj->getPrivate();
7647 } else {
7648 /* We have iterated at least once. */
7649 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
7650 JS_ASSERT(JSVAL_TO_OBJECT(sp[-2])->getClass() == &js_XMLFilterClass);
7651 filter = (JSXMLFilter *) JSVAL_TO_OBJECT(sp[-2])->getPrivate();
7652 JS_ASSERT(filter->kid);
7654 /* Check if the filter expression wants to append the element. */
7655 if (js_ValueToBoolean(Valueify(sp[-1])) &&
7656 !Append(cx, filter->result, filter->kid)) {
7657 return JS_FALSE;
7661 /* Do the iteration. */
7662 filter->kid = (JSXML *) filter->cursor.getNext();
7663 if (!filter->kid) {
7665 * Do not defer finishing the cursor until the next GC cycle to avoid
7666 * accumulation of dead cursors associated with filter->list.
7668 filter->cursor.disconnect();
7669 JS_ASSERT(filter->result->object);
7670 sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
7671 kidobj = NULL;
7672 } else {
7673 kidobj = js_GetXMLObject(cx, filter->kid);
7674 if (!kidobj)
7675 return JS_FALSE;
7678 /* Null as kidobj at sp[-1] signals filter termination. */
7679 sp[-1] = OBJECT_TO_JSVAL(kidobj);
7680 return JS_TRUE;
7683 JSObject *
7684 js_ValueToXMLObject(JSContext *cx, const Value &v)
7686 return ToXML(cx, Jsvalify(v));
7689 JSObject *
7690 js_ValueToXMLListObject(JSContext *cx, const Value &v)
7692 return ToXMLList(cx, Jsvalify(v));
7695 JSObject *
7696 js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
7697 JSString *value)
7699 uintN flags;
7700 JSObject *obj;
7701 JSXML *xml;
7702 JSObject *qn;
7704 if (!GetXMLSettingFlags(cx, &flags))
7705 return NULL;
7707 if ((xml_class == JSXML_CLASS_COMMENT &&
7708 (flags & XSF_IGNORE_COMMENTS)) ||
7709 (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
7710 (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
7711 return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
7714 obj = js_NewXMLObject(cx, xml_class);
7715 if (!obj)
7716 return NULL;
7717 xml = (JSXML *) obj->getPrivate();
7718 if (name) {
7719 JSLinearString *linearName = name->ensureLinear(cx);
7720 if (!linearName)
7721 return NULL;
7722 qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName);
7723 if (!qn)
7724 return NULL;
7725 xml->name = qn;
7727 xml->xml_value = value;
7728 return obj;
7731 JSString *
7732 js_MakeXMLCDATAString(JSContext *cx, JSString *str)
7734 StringBuffer sb(cx);
7735 return MakeXMLCDATAString(cx, sb, str);
7738 JSString *
7739 js_MakeXMLCommentString(JSContext *cx, JSString *str)
7741 StringBuffer sb(cx);
7742 return MakeXMLCommentString(cx, sb, str);
7745 JSString *
7746 js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
7748 StringBuffer sb(cx);
7749 return MakeXMLPIString(cx, sb, name, str);
7752 #endif /* JS_HAS_XML_SUPPORT */