Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / js / src / jsxml.cpp
blob94d114d072ffa6a7094efc1fb75e3c82f9ed7691
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 PropertyStub, /* 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;
266 obj = ComputeThisFromVp(cx, vp);
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 PropertyStub, /* 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 PropertyStub, /* 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 PropertyStub, /* 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 = ComputeThisFromVp(cx, vp);
455 if (!obj || !InstanceOf(cx, obj, &js_QNameClass, vp + 2))
456 return false;
458 JSString *str = ConvertQNameToString(cx, obj);
459 if (!str)
460 return false;
462 vp->setString(str);
463 return true;
466 static JSFunctionSpec qname_methods[] = {
467 JS_FN(js_toString_str, qname_toString, 0,0),
468 JS_FS_END
472 static void
473 InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
474 JSLinearString *localName)
476 JS_ASSERT(obj->isQName());
477 JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
478 JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
479 JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
480 if (uri)
481 obj->setNameURI(uri);
482 if (prefix)
483 obj->setNamePrefix(prefix);
484 if (localName)
485 obj->setQNameLocalName(localName);
488 static JSObject *
489 NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
490 JSLinearString *localName)
492 JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
493 if (!obj)
494 return NULL;
495 InitXMLQName(obj, uri, prefix, localName);
496 METER(xml_stats.qname);
497 return obj;
500 static JSObject *
501 NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
502 JSLinearString *localName)
505 * AttributeName is an internal anonymous class which instances are not
506 * exposed to scripts.
508 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AttributeNameClass, NULL, NULL);
509 if (!obj)
510 return NULL;
511 JS_ASSERT(obj->isQName());
512 InitXMLQName(obj, uri, prefix, localName);
513 METER(xml_stats.qname);
514 return obj;
517 JSObject *
518 js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval)
520 Value argv[2];
523 * ECMA-357 11.1.2,
524 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
525 * production, step 2.
527 if (nsval.isObject() &&
528 nsval.toObject().getClass() == &js_AnyNameClass) {
529 argv[0].setNull();
530 } else {
531 argv[0] = nsval;
533 argv[1] = lnval;
534 return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, argv);
537 static JSBool
538 IsXMLName(const jschar *cp, size_t n)
540 JSBool rv;
541 jschar c;
543 rv = JS_FALSE;
544 if (n != 0 && JS_ISXMLNSSTART(*cp)) {
545 while (--n != 0) {
546 c = *++cp;
547 if (!JS_ISXMLNS(c))
548 return rv;
550 rv = JS_TRUE;
552 return rv;
555 JSBool
556 js_IsXMLName(JSContext *cx, jsval v)
558 JSLinearString *name = NULL;
559 JSErrorReporter older;
562 * Inline specialization of the QName constructor called with v passed as
563 * the only argument, to compute the localName for the constructed qname,
564 * without actually allocating the object or computing its uri and prefix.
565 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
567 if (!JSVAL_IS_PRIMITIVE(v) &&
568 JSVAL_TO_OBJECT(v)->isQName()) {
569 name = JSVAL_TO_OBJECT(v)->getQNameLocalName();
570 } else {
571 older = JS_SetErrorReporter(cx, NULL);
572 JSString *str = js_ValueToString(cx, Valueify(v));
573 if (str)
574 name = str->ensureLinear(cx);
575 JS_SetErrorReporter(cx, older);
576 if (!name) {
577 JS_ClearPendingException(cx);
578 return JS_FALSE;
582 return IsXMLName(name->chars(), name->length());
586 * When argc is -1, it indicates argv is empty but the code should behave as
587 * if argc is 1 and argv[0] is JSVAL_VOID.
589 static JSBool
590 NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
591 jsval *rval)
593 jsval urival, prefixval;
594 JSObject *uriobj;
595 JSBool isNamespace, isQName;
596 Class *clasp;
597 JSLinearString *empty, *prefix, *uri;
599 isNamespace = isQName = JS_FALSE;
600 #ifdef __GNUC__ /* suppress bogus gcc warnings */
601 uriobj = NULL;
602 #endif
603 if (argc <= 0) {
604 urival = JSVAL_VOID;
605 } else {
606 urival = argv[argc > 1];
607 if (!JSVAL_IS_PRIMITIVE(urival)) {
608 uriobj = JSVAL_TO_OBJECT(urival);
609 clasp = uriobj->getClass();
610 isNamespace = (clasp == &js_NamespaceClass);
611 isQName = (clasp == &js_QNameClass);
615 if (!obj) {
616 /* Namespace called as function. */
617 if (argc == 1 && isNamespace) {
618 /* Namespace called with one Namespace argument is identity. */
619 *rval = urival;
620 return JS_TRUE;
623 obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass);
624 if (!obj)
625 return JS_FALSE;
627 *rval = OBJECT_TO_JSVAL(obj);
628 METER(xml_stats.xmlnamespace);
630 empty = cx->runtime->emptyString;
631 obj->setNamePrefix(empty);
632 obj->setNameURI(empty);
634 if (argc == 1 || argc == -1) {
635 if (isNamespace) {
636 obj->setNameURI(uriobj->getNameURI());
637 obj->setNamePrefix(uriobj->getNamePrefix());
638 } else if (isQName && (uri = uriobj->getNameURI())) {
639 obj->setNameURI(uri);
640 obj->setNamePrefix(uriobj->getNamePrefix());
641 } else {
642 JSString *str = js_ValueToString(cx, Valueify(urival));
643 if (!str)
644 return JS_FALSE;
645 uri = str->ensureLinear(cx);
646 if (!uri)
647 return JS_FALSE;
648 obj->setNameURI(uri);
649 if (!uri->empty())
650 obj->clearNamePrefix();
652 } else if (argc == 2) {
653 if (!isQName || !(uri = uriobj->getNameURI())) {
654 JSString *str = js_ValueToString(cx, Valueify(urival));
655 if (!str)
656 return JS_FALSE;
657 uri = str->ensureLinear(cx);
658 if (!uri)
659 return JS_FALSE;
661 obj->setNameURI(uri);
663 prefixval = argv[0];
664 if (uri->empty()) {
665 if (!JSVAL_IS_VOID(prefixval)) {
666 JSString *str = js_ValueToString(cx, Valueify(prefixval));
667 if (!str)
668 return JS_FALSE;
669 if (!str->empty()) {
670 JSAutoByteString bytes;
671 if (js_ValueToPrintable(cx, StringValue(str), &bytes)) {
672 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
673 JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
675 return JS_FALSE;
678 } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
679 obj->clearNamePrefix();
680 } else {
681 JSString *str = js_ValueToString(cx, Valueify(prefixval));
682 if (!str)
683 return JS_FALSE;
684 prefix = str->ensureLinear(cx);
685 if (!prefix)
686 return JS_FALSE;
687 obj->setNamePrefix(prefix);
690 return JS_TRUE;
693 static JSBool
694 Namespace(JSContext *cx, uintN argc, Value *vp)
696 JSObject *thisobj = NULL;
697 (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
698 return NamespaceHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
702 * When argc is -1, it indicates argv is empty but the code should behave as
703 * if argc is 1 and argv[0] is JSVAL_VOID.
705 static JSBool
706 QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval)
708 jsval nameval, nsval;
709 JSBool isQName, isNamespace;
710 JSObject *qn;
711 JSLinearString *uri, *prefix, *name;
712 JSObject *obj2;
714 if (argc <= 0) {
715 nameval = JSVAL_VOID;
716 isQName = JS_FALSE;
717 } else {
718 nameval = argv[argc > 1];
719 isQName =
720 !JSVAL_IS_PRIMITIVE(nameval) &&
721 JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass;
724 if (!obj) {
725 /* QName called as function. */
726 if (argc == 1 && isQName) {
727 /* QName called with one QName argument is identity. */
728 *rval = nameval;
729 return JS_TRUE;
732 /* Create and return a new QName object exactly as if constructed. */
733 obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
734 if (!obj)
735 return JS_FALSE;
737 *rval = OBJECT_TO_JSVAL(obj);
738 METER(xml_stats.qname);
740 if (isQName) {
741 /* If namespace is not specified and name is a QName, clone it. */
742 qn = JSVAL_TO_OBJECT(nameval);
743 if (argc == 1) {
744 uri = qn->getNameURI();
745 prefix = qn->getNamePrefix();
746 name = qn->getQNameLocalName();
747 goto out;
750 /* Namespace and qname were passed -- use the qname's localName. */
751 nameval = qn->getQNameLocalNameVal();
754 if (argc == 0) {
755 name = cx->runtime->emptyString;
756 } else if (argc < 0) {
757 name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
758 } else {
759 JSString *str = js_ValueToString(cx, Valueify(nameval));
760 if (!str)
761 return JS_FALSE;
762 name = str->ensureLinear(cx);
763 if (!name)
764 return JS_FALSE;
765 argv[argc > 1] = STRING_TO_JSVAL(name);
768 if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
769 nsval = argv[0];
770 } else if (IS_STAR(name)) {
771 nsval = JSVAL_NULL;
772 } else {
773 if (!js_GetDefaultXMLNamespace(cx, &nsval))
774 return JS_FALSE;
775 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
776 JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
777 &js_NamespaceClass);
780 if (JSVAL_IS_NULL(nsval)) {
781 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
782 prefix = uri = NULL;
783 } else {
785 * Inline specialization of the Namespace constructor called with
786 * nsval passed as the only argument, to compute the uri and prefix
787 * for the constructed namespace, without actually allocating the
788 * object or computing other members. See ECMA-357 13.3.2 6(a) and
789 * 13.2.2.
791 isNamespace = isQName = JS_FALSE;
792 if (!JSVAL_IS_PRIMITIVE(nsval)) {
793 obj2 = JSVAL_TO_OBJECT(nsval);
794 isNamespace = (obj2->getClass() == &js_NamespaceClass);
795 isQName = (obj2->getClass() == &js_QNameClass);
797 #ifdef __GNUC__ /* suppress bogus gcc warnings */
798 else obj2 = NULL;
799 #endif
801 if (isNamespace) {
802 uri = obj2->getNameURI();
803 prefix = obj2->getNamePrefix();
804 } else if (isQName && (uri = obj2->getNameURI())) {
805 JS_ASSERT(argc > 1);
806 prefix = obj2->getNamePrefix();
807 } else {
808 JS_ASSERT(argc > 1);
809 JSString *str = js_ValueToString(cx, Valueify(nsval));
810 if (!str)
811 return JS_FALSE;
812 uri = str->ensureLinear(cx);
813 if (!uri)
814 return JS_FALSE;
815 argv[0] = STRING_TO_JSVAL(uri); /* local root */
817 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
818 prefix = uri->empty() ? cx->runtime->emptyString : NULL;
822 out:
823 InitXMLQName(obj, uri, prefix, name);
824 return JS_TRUE;
827 static JSBool
828 QName(JSContext *cx, uintN argc, Value *vp)
830 JSObject *thisobj = NULL;
831 (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
832 return QNameHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
836 * XMLArray library functions.
838 static JSBool
839 namespace_identity(const void *a, const void *b)
841 const JSObject *nsa = (const JSObject *) a;
842 const JSObject *nsb = (const JSObject *) b;
843 JSLinearString *prefixa = nsa->getNamePrefix();
844 JSLinearString *prefixb = nsb->getNamePrefix();
846 if (prefixa && prefixb) {
847 if (!EqualStrings(prefixa, prefixb))
848 return JS_FALSE;
849 } else {
850 if (prefixa || prefixb)
851 return JS_FALSE;
853 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
856 static JSBool
857 attr_identity(const void *a, const void *b)
859 const JSXML *xmla = (const JSXML *) a;
860 const JSXML *xmlb = (const JSXML *) b;
862 return qname_identity(xmla->name, xmlb->name);
865 void
866 JSXMLArrayCursor::trace(JSTracer *trc) {
867 #ifdef DEBUG
868 size_t index = 0;
869 #endif
870 for (JSXMLArrayCursor *cursor = this; cursor; cursor = cursor->next)
871 js::gc::MarkGCThing(trc, cursor->root, "cursor_root", index++);
874 static void
875 XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
877 cursor->trace(trc);
880 /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
881 bool
882 JSXMLArray::setCapacity(JSContext *cx, uint32 newCapacity)
884 if (newCapacity == 0) {
885 /* We could let realloc(p, 0) free this, but purify gets confused. */
886 if (vector) {
887 if (cx)
888 cx->free(vector);
889 else
890 js_free(vector);
892 vector = NULL;
893 } else {
894 void **tmp;
896 if (
897 #if JS_BITS_PER_WORD == 32
898 (size_t)newCapacity > ~(size_t)0 / sizeof(void *) ||
899 #endif
900 !(tmp = (void **) js_realloc(vector, newCapacity * sizeof(void *)))) {
901 if (cx)
902 JS_ReportOutOfMemory(cx);
903 return false;
905 vector = tmp;
907 capacity = JSXML_PRESET_CAPACITY | newCapacity;
908 return true;
911 void
912 JSXMLArray::trim()
914 if (capacity & JSXML_PRESET_CAPACITY)
915 return;
916 if (length < capacity)
917 setCapacity(NULL, length);
920 void
921 JSXMLArray::finish(JSContext *cx)
923 cx->free(vector);
925 while (JSXMLArrayCursor *cursor = cursors)
926 cursor->disconnect();
928 #ifdef DEBUG
929 memset(this, 0xd5, sizeof *this);
930 #endif
933 #define XML_NOT_FOUND ((uint32) -1)
935 static uint32
936 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
938 void **vector;
939 uint32 i, n;
941 /* The identity op must not reallocate array->vector. */
942 vector = array->vector;
943 if (identity) {
944 for (i = 0, n = array->length; i < n; i++) {
945 if (identity(vector[i], elt))
946 return i;
948 } else {
949 for (i = 0, n = array->length; i < n; i++) {
950 if (vector[i] == elt)
951 return i;
954 return XML_NOT_FOUND;
958 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
959 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
960 * should be greater than increment.
962 #define LINEAR_THRESHOLD 256
963 #define LINEAR_INCREMENT 32
965 static JSBool
966 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
968 uint32 capacity, i;
969 int log2;
970 void **vector;
972 if (index >= array->length) {
973 if (index >= JSXML_CAPACITY(array)) {
974 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
975 capacity = index + 1;
976 if (index >= LINEAR_THRESHOLD) {
977 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
978 } else {
979 JS_CEILING_LOG2(log2, capacity);
980 capacity = JS_BIT(log2);
982 if (
983 #if JS_BITS_PER_WORD == 32
984 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
985 #endif
986 !(vector = (void **)
987 js_realloc(array->vector, capacity * sizeof(void *)))) {
988 JS_ReportOutOfMemory(cx);
989 return JS_FALSE;
991 array->capacity = capacity;
992 array->vector = vector;
993 for (i = array->length; i < index; i++)
994 vector[i] = NULL;
996 array->length = index + 1;
999 array->vector[index] = elt;
1000 return JS_TRUE;
1003 static JSBool
1004 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1006 uint32 j;
1007 JSXMLArrayCursor *cursor;
1009 j = array->length;
1010 JS_ASSERT(i <= j);
1011 if (!array->setCapacity(cx, j + n))
1012 return JS_FALSE;
1014 array->length = j + n;
1015 JS_ASSERT(n != (uint32)-1);
1016 while (j != i) {
1017 --j;
1018 array->vector[j + n] = array->vector[j];
1021 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1022 if (cursor->index > i)
1023 cursor->index += n;
1025 return JS_TRUE;
1028 static void *
1029 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1031 uint32 length;
1032 void **vector, *elt;
1033 JSXMLArrayCursor *cursor;
1035 length = array->length;
1036 if (index >= length)
1037 return NULL;
1039 vector = array->vector;
1040 elt = vector[index];
1041 if (compress) {
1042 while (++index < length)
1043 vector[index-1] = vector[index];
1044 array->length = length - 1;
1045 array->capacity = JSXML_CAPACITY(array);
1046 } else {
1047 vector[index] = NULL;
1050 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1051 if (cursor->index > index)
1052 --cursor->index;
1054 return elt;
1057 static void
1058 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1060 void **vector;
1062 JS_ASSERT(!array->cursors);
1063 if (length >= array->length)
1064 return;
1066 if (length == 0) {
1067 if (array->vector)
1068 cx->free(array->vector);
1069 vector = NULL;
1070 } else {
1071 vector = (void **) js_realloc(array->vector, length * sizeof(void *));
1072 if (!vector)
1073 return;
1076 if (array->length > length)
1077 array->length = length;
1078 array->capacity = length;
1079 array->vector = vector;
1082 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1083 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1084 XML_NOT_FOUND)
1085 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1086 ? (t *) (a)->vector[i] \
1087 : NULL)
1088 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1089 if ((a)->length <= (i)) \
1090 (a)->length = (i) + 1; \
1091 ((a)->vector[i] = (void *)(e)); \
1092 JS_END_MACRO
1093 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1094 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1095 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1096 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1097 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1100 * Define XML setting property strings and constants early, so everyone can
1101 * use the same names.
1103 static const char js_ignoreComments_str[] = "ignoreComments";
1104 static const char js_ignoreProcessingInstructions_str[]
1105 = "ignoreProcessingInstructions";
1106 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1107 static const char js_prettyPrinting_str[] = "prettyPrinting";
1108 static const char js_prettyIndent_str[] = "prettyIndent";
1110 #define XSF_IGNORE_COMMENTS JS_BIT(0)
1111 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1112 #define XSF_IGNORE_WHITESPACE JS_BIT(2)
1113 #define XSF_PRETTY_PRINTING JS_BIT(3)
1115 static JSPropertySpec xml_static_props[] = {
1116 {js_ignoreComments_str, 0, JSPROP_PERMANENT, NULL, NULL},
1117 {js_ignoreProcessingInstructions_str, 0, JSPROP_PERMANENT, NULL, NULL},
1118 {js_ignoreWhitespace_str, 0, JSPROP_PERMANENT, NULL, NULL},
1119 {js_prettyPrinting_str, 0, JSPROP_PERMANENT, NULL, NULL},
1120 {js_prettyIndent_str, 0, JSPROP_PERMANENT, NULL, NULL},
1121 {0,0,0,0,0}
1124 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1125 #define IS_XML(str) \
1126 (str->length() == 3 && IS_XML_CHARS(str->chars()))
1128 #define IS_XMLNS(str) \
1129 (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1131 #define IS_XML_CHARS(chars) \
1132 (JS_TOLOWER((chars)[0]) == 'x' && \
1133 JS_TOLOWER((chars)[1]) == 'm' && \
1134 JS_TOLOWER((chars)[2]) == 'l')
1136 #define HAS_NS_AFTER_XML(chars) \
1137 (JS_TOLOWER((chars)[3]) == 'n' && \
1138 JS_TOLOWER((chars)[4]) == 's')
1140 #define IS_XMLNS_CHARS(chars) \
1141 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1143 #define STARTS_WITH_XML(chars,length) \
1144 (length >= 3 && IS_XML_CHARS(chars))
1146 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1147 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1149 static JSObject *
1150 ParseNodeToQName(Parser *parser, JSParseNode *pn,
1151 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1153 JSContext *cx = parser->context;
1154 JSLinearString *str, *uri, *prefix, *localName;
1155 size_t length, offset;
1156 const jschar *start, *limit, *colon;
1157 uint32 n;
1158 JSObject *ns;
1159 JSLinearString *nsprefix;
1161 JS_ASSERT(pn->pn_arity == PN_NULLARY);
1162 str = pn->pn_atom;
1163 start = str->chars();
1164 length = str->length();
1165 JS_ASSERT(length != 0 && *start != '@');
1166 JS_ASSERT(length != 1 || *start != '*');
1168 uri = cx->runtime->emptyString;
1169 limit = start + length;
1170 colon = js_strchr_limit(start, ':', limit);
1171 if (colon) {
1172 offset = colon - start;
1173 prefix = js_NewDependentString(cx, str, 0, offset);
1174 if (!prefix)
1175 return NULL;
1177 if (STARTS_WITH_XML(start, offset)) {
1178 if (offset == 3) {
1179 uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str));
1180 if (!uri)
1181 return NULL;
1182 } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1183 uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str));
1184 if (!uri)
1185 return NULL;
1186 } else {
1187 uri = NULL;
1189 } else {
1190 uri = NULL;
1191 n = inScopeNSes->length;
1192 while (n != 0) {
1193 --n;
1194 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1195 nsprefix = ns->getNamePrefix();
1196 if (nsprefix && EqualStrings(nsprefix, prefix)) {
1197 uri = ns->getNameURI();
1198 break;
1203 if (!uri) {
1204 Value v = StringValue(prefix);
1205 JSAutoByteString bytes;
1206 if (js_ValueToPrintable(parser->context, v, &bytes)) {
1207 ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
1208 JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
1210 return NULL;
1213 localName = js_NewStringCopyN(parser->context, colon + 1, length - (offset + 1));
1214 if (!localName)
1215 return NULL;
1216 } else {
1217 if (isAttributeName) {
1219 * An unprefixed attribute is not in any namespace, so set prefix
1220 * as well as uri to the empty string.
1222 prefix = uri;
1223 } else {
1225 * Loop from back to front looking for the closest declared default
1226 * namespace.
1228 n = inScopeNSes->length;
1229 while (n != 0) {
1230 --n;
1231 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1232 nsprefix = ns->getNamePrefix();
1233 if (!nsprefix || nsprefix->empty()) {
1234 uri = ns->getNameURI();
1235 break;
1238 prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
1240 localName = str;
1243 return NewXMLQName(parser->context, uri, prefix, localName);
1246 static JSString *
1247 ChompXMLWhitespace(JSContext *cx, JSString *str)
1249 size_t length, newlength, offset;
1250 const jschar *cp, *start, *end;
1251 jschar c;
1253 length = str->length();
1254 start = str->getChars(cx);
1255 if (!start)
1256 return NULL;
1258 for (cp = start, end = cp + length; cp < end; cp++) {
1259 c = *cp;
1260 if (!JS_ISXMLSPACE(c))
1261 break;
1263 while (end > cp) {
1264 c = end[-1];
1265 if (!JS_ISXMLSPACE(c))
1266 break;
1267 --end;
1269 newlength = end - cp;
1270 if (newlength == length)
1271 return str;
1272 offset = cp - start;
1273 return js_NewDependentString(cx, str, offset, newlength);
1276 static JSXML *
1277 ParseNodeToXML(Parser *parser, JSParseNode *pn,
1278 JSXMLArray *inScopeNSes, uintN flags)
1280 JSContext *cx = parser->context;
1281 JSXML *xml, *kid, *attr, *attrj;
1282 JSLinearString *str;
1283 uint32 length, n, i, j;
1284 JSParseNode *pn2, *pn3, *head, **pnp;
1285 JSObject *ns;
1286 JSObject *qn, *attrjqn;
1287 JSXMLClass xml_class;
1288 int stackDummy;
1290 if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy)) {
1291 ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR,
1292 JSMSG_OVER_RECURSED);
1293 return NULL;
1296 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1299 * Cases return early to avoid common code that gets an outermost xml's
1300 * object, which protects GC-things owned by xml and its descendants from
1301 * garbage collection.
1303 xml = NULL;
1304 if (!js_EnterLocalRootScope(cx))
1305 return NULL;
1306 switch (pn->pn_type) {
1307 case TOK_XMLELEM:
1308 length = inScopeNSes->length;
1309 pn2 = pn->pn_head;
1310 xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1311 if (!xml)
1312 goto fail;
1314 n = pn->pn_count;
1315 JS_ASSERT(n >= 2);
1316 n -= 2;
1317 if (!xml->xml_kids.setCapacity(cx, n))
1318 goto fail;
1320 i = 0;
1321 while ((pn2 = pn2->pn_next) != NULL) {
1322 if (!pn2->pn_next) {
1323 /* Don't append the end tag! */
1324 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1325 break;
1328 if ((flags & XSF_IGNORE_WHITESPACE) &&
1329 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1330 --n;
1331 continue;
1334 kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1335 if (kid == PN2X_SKIP_CHILD) {
1336 --n;
1337 continue;
1340 if (!kid)
1341 goto fail;
1343 /* Store kid in xml right away, to protect it from GC. */
1344 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1345 kid->parent = xml;
1346 ++i;
1348 /* XXX where is this documented in an XML spec, or in E4X? */
1349 if ((flags & XSF_IGNORE_WHITESPACE) &&
1350 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1351 JSString *str = ChompXMLWhitespace(cx, kid->xml_value);
1352 if (!str)
1353 goto fail;
1354 kid->xml_value = str;
1358 JS_ASSERT(i == n);
1359 if (n < pn->pn_count - 2)
1360 xml->xml_kids.trim();
1361 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1362 break;
1364 case TOK_XMLLIST:
1365 xml = js_NewXML(cx, JSXML_CLASS_LIST);
1366 if (!xml)
1367 goto fail;
1369 n = pn->pn_count;
1370 if (!xml->xml_kids.setCapacity(cx, n))
1371 goto fail;
1373 i = 0;
1374 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1376 * Always ignore insignificant whitespace in lists -- we shouldn't
1377 * condition this on an XML.ignoreWhitespace setting when the list
1378 * constructor is XMLList (note XML/XMLList unification hazard).
1380 if (pn2->pn_type == TOK_XMLSPACE) {
1381 --n;
1382 continue;
1385 kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1386 if (kid == PN2X_SKIP_CHILD) {
1387 --n;
1388 continue;
1391 if (!kid)
1392 goto fail;
1394 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1395 ++i;
1398 if (n < pn->pn_count)
1399 xml->xml_kids.trim();
1400 break;
1402 case TOK_XMLSTAGO:
1403 case TOK_XMLPTAGC:
1404 length = inScopeNSes->length;
1405 pn2 = pn->pn_head;
1406 JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1407 if (pn2->pn_arity == PN_LIST)
1408 goto syntax;
1410 xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1411 if (!xml)
1412 goto fail;
1414 /* First pass: check syntax and process namespace declarations. */
1415 JS_ASSERT(pn->pn_count >= 1);
1416 n = pn->pn_count - 1;
1417 pnp = &pn2->pn_next;
1418 head = *pnp;
1419 while ((pn2 = *pnp) != NULL) {
1420 size_t length;
1421 const jschar *chars;
1423 if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1424 goto syntax;
1426 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1427 for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1428 if (pn3->pn_atom == pn2->pn_atom) {
1429 Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom));
1430 JSAutoByteString bytes;
1431 if (js_ValueToPrintable(cx, v, &bytes)) {
1432 ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1433 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1434 bytes.ptr());
1436 goto fail;
1440 JSAtom *atom = pn2->pn_atom;
1441 pn2 = pn2->pn_next;
1442 JS_ASSERT(pn2);
1443 if (pn2->pn_type != TOK_XMLATTR)
1444 goto syntax;
1446 chars = atom->chars();
1447 length = atom->length();
1448 if (length >= 5 &&
1449 IS_XMLNS_CHARS(chars) &&
1450 (length == 5 || chars[5] == ':')) {
1451 JSLinearString *uri, *prefix;
1453 uri = ATOM_TO_STRING(pn2->pn_atom);
1454 if (length == 5) {
1455 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1456 prefix = cx->runtime->emptyString;
1457 } else {
1458 prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1459 if (!prefix)
1460 goto fail;
1464 * Once the new ns is appended to xml->xml_namespaces, it is
1465 * protected from GC by the object that owns xml -- which is
1466 * either xml->object if outermost, or the object owning xml's
1467 * oldest ancestor if !outermost.
1469 ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1470 if (!ns)
1471 goto fail;
1474 * Don't add a namespace that's already in scope. If someone
1475 * extracts a child property from its parent via [[Get]], then
1476 * we enforce the invariant, noted many times in ECMA-357, that
1477 * the child's namespaces form a possibly-improper superset of
1478 * its ancestors' namespaces.
1480 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1481 if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1482 !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1483 goto fail;
1487 JS_ASSERT(n >= 2);
1488 n -= 2;
1489 *pnp = pn2->pn_next;
1490 /* XXXbe recycle pn2 */
1491 continue;
1494 pnp = &pn2->pn_next;
1497 xml->xml_namespaces.trim();
1499 /* Second pass: process tag name and attributes, using namespaces. */
1500 pn2 = pn->pn_head;
1501 qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_FALSE);
1502 if (!qn)
1503 goto fail;
1504 xml->name = qn;
1506 JS_ASSERT((n & 1) == 0);
1507 n >>= 1;
1508 if (!xml->xml_attrs.setCapacity(cx, n))
1509 goto fail;
1511 for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1512 qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_TRUE);
1513 if (!qn) {
1514 xml->xml_attrs.length = i;
1515 goto fail;
1519 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1520 * this time checking local name and namespace URI.
1522 for (j = 0; j < i; j++) {
1523 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1524 attrjqn = attrj->name;
1525 if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) &&
1526 EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) {
1527 Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom));
1528 JSAutoByteString bytes;
1529 if (js_ValueToPrintable(cx, v, &bytes)) {
1530 ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1531 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1532 bytes.ptr());
1534 goto fail;
1538 pn2 = pn2->pn_next;
1539 JS_ASSERT(pn2);
1540 JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1542 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1543 if (!attr)
1544 goto fail;
1546 XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1547 attr->parent = xml;
1548 attr->name = qn;
1549 attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1552 /* Point tag closes its own namespace scope. */
1553 if (pn->pn_type == TOK_XMLPTAGC)
1554 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1555 break;
1557 case TOK_XMLSPACE:
1558 case TOK_XMLTEXT:
1559 case TOK_XMLCDATA:
1560 case TOK_XMLCOMMENT:
1561 case TOK_XMLPI:
1562 str = ATOM_TO_STRING(pn->pn_atom);
1563 qn = NULL;
1564 if (pn->pn_type == TOK_XMLCOMMENT) {
1565 if (flags & XSF_IGNORE_COMMENTS)
1566 goto skip_child;
1567 xml_class = JSXML_CLASS_COMMENT;
1568 } else if (pn->pn_type == TOK_XMLPI) {
1569 if (IS_XML(str)) {
1570 Value v = StringValue(str);
1571 JSAutoByteString bytes;
1572 if (js_ValueToPrintable(cx, v, &bytes)) {
1573 ReportCompileErrorNumber(cx, &parser->tokenStream, pn,
1574 JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
1576 goto fail;
1579 if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1580 goto skip_child;
1582 qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE);
1583 if (!qn)
1584 goto fail;
1586 str = pn->pn_atom2
1587 ? ATOM_TO_STRING(pn->pn_atom2)
1588 : cx->runtime->emptyString;
1589 xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1590 } else {
1591 /* CDATA section content, or element text. */
1592 xml_class = JSXML_CLASS_TEXT;
1595 xml = js_NewXML(cx, xml_class);
1596 if (!xml)
1597 goto fail;
1598 xml->name = qn;
1599 if (pn->pn_type == TOK_XMLSPACE)
1600 xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1601 xml->xml_value = str;
1602 break;
1604 default:
1605 goto syntax;
1608 js_LeaveLocalRootScopeWithResult(cx, xml);
1609 return xml;
1611 skip_child:
1612 js_LeaveLocalRootScope(cx);
1613 return PN2X_SKIP_CHILD;
1615 #undef PN2X_SKIP_CHILD
1617 syntax:
1618 ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
1619 fail:
1620 js_LeaveLocalRootScope(cx);
1621 return NULL;
1625 * XML helper, object-ops, and library functions. We start with the helpers,
1626 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1628 static JSBool
1629 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1631 jsval v;
1633 if (!js_FindClassObject(cx, NULL, JSProto_XML, Valueify(&v)))
1634 return JS_FALSE;
1635 if (!VALUE_IS_FUNCTION(cx, v)) {
1636 *vp = JSVAL_VOID;
1637 return JS_TRUE;
1639 return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1642 static JSBool
1643 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1645 jsval v;
1647 return GetXMLSetting(cx, name, &v) && JS_ValueToBoolean(cx, v, bp);
1650 static JSBool
1651 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1653 jsval v;
1655 return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1658 static JSBool
1659 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1661 JSBool flag[4];
1663 if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag[0]) ||
1664 !GetBooleanXMLSetting(cx, js_ignoreProcessingInstructions_str, &flag[1]) ||
1665 !GetBooleanXMLSetting(cx, js_ignoreWhitespace_str, &flag[2]) ||
1666 !GetBooleanXMLSetting(cx, js_prettyPrinting_str, &flag[3])) {
1667 return false;
1670 *flagsp = 0;
1671 for (size_t n = 0; n < 4; ++n)
1672 if (flag[n])
1673 *flagsp |= JS_BIT(n);
1674 return true;
1677 static JSXML *
1678 ParseXMLSource(JSContext *cx, JSString *src)
1680 jsval nsval;
1681 JSLinearString *uri;
1682 size_t urilen, srclen, length, offset, dstlen;
1683 jschar *chars;
1684 const jschar *srcp, *endp;
1685 JSXML *xml;
1686 const char *filename;
1687 uintN lineno;
1688 JSOp op;
1690 static const char prefix[] = "<parent xmlns=\"";
1691 static const char middle[] = "\">";
1692 static const char suffix[] = "</parent>";
1694 #define constrlen(constr) (sizeof(constr) - 1)
1696 if (!js_GetDefaultXMLNamespace(cx, &nsval))
1697 return NULL;
1698 uri = JSVAL_TO_OBJECT(nsval)->getNameURI();
1699 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1700 if (!uri)
1701 return NULL;
1703 urilen = uri->length();
1704 srclen = src->length();
1705 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1706 constrlen(suffix);
1708 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
1709 if (!chars)
1710 return NULL;
1712 dstlen = length;
1713 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1714 offset = dstlen;
1715 js_strncpy(chars + offset, uri->chars(), urilen);
1716 offset += urilen;
1717 dstlen = length - offset + 1;
1718 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1719 &dstlen);
1720 offset += dstlen;
1721 srcp = src->getChars(cx);
1722 if (!srcp) {
1723 cx->free(chars);
1724 return NULL;
1726 js_strncpy(chars + offset, srcp, srclen);
1727 offset += srclen;
1728 dstlen = length - offset + 1;
1729 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1730 &dstlen);
1731 chars [offset + dstlen] = 0;
1733 LeaveTrace(cx);
1734 xml = NULL;
1735 FrameRegsIter i(cx);
1736 for (; !i.done() && !i.pc(); ++i)
1737 JS_ASSERT(!i.fp()->isScriptFrame());
1738 filename = NULL;
1739 lineno = 1;
1740 if (!i.done()) {
1741 JSStackFrame *fp = i.fp();
1742 op = (JSOp) *i.pc();
1743 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1744 filename = fp->script()->filename;
1745 lineno = js_FramePCToLineNumber(cx, fp);
1746 for (endp = srcp + srclen; srcp < endp; srcp++) {
1747 if (*srcp == '\n')
1748 --lineno;
1754 Parser parser(cx);
1755 if (parser.init(chars, length, filename, lineno)) {
1756 JSObject *scopeChain = GetScopeChain(cx);
1757 if (!scopeChain) {
1758 cx->free(chars);
1759 return NULL;
1761 JSParseNode *pn = parser.parseXMLText(scopeChain, false);
1762 uintN flags;
1763 if (pn && GetXMLSettingFlags(cx, &flags)) {
1764 AutoNamespaceArray namespaces(cx);
1765 if (namespaces.array.setCapacity(cx, 1))
1766 xml = ParseNodeToXML(&parser, pn, &namespaces.array, flags);
1771 cx->free(chars);
1772 return xml;
1774 #undef constrlen
1778 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1780 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1781 * the constraint:
1783 * for all x belonging to XML:
1784 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1786 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1787 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1789 * Same goes for 10.4.1 Step 7(a).
1791 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1792 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1793 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1794 * undeclared namespaces associated with x not belonging to ancestorNS.
1796 static JSXML *
1797 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1799 JSObject *ns;
1801 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1802 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1803 if (!ns || !xml)
1804 return xml;
1805 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1806 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1807 return NULL;
1808 ns->setNamespaceDeclared(JSVAL_VOID);
1810 xml->parent = NULL;
1811 return xml;
1814 static JSObject *
1815 ToXML(JSContext *cx, jsval v)
1817 JSObject *obj;
1818 JSXML *xml;
1819 Class *clasp;
1820 JSString *str;
1821 uint32 length;
1823 if (JSVAL_IS_PRIMITIVE(v)) {
1824 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1825 goto bad;
1826 } else {
1827 obj = JSVAL_TO_OBJECT(v);
1828 if (obj->isXML()) {
1829 xml = (JSXML *) obj->getPrivate();
1830 if (xml->xml_class == JSXML_CLASS_LIST) {
1831 if (xml->xml_kids.length != 1)
1832 goto bad;
1833 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1834 if (xml) {
1835 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1836 return js_GetXMLObject(cx, xml);
1839 return obj;
1842 clasp = obj->getClass();
1843 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1844 JS_ASSERT(0);
1847 if (clasp != &js_StringClass &&
1848 clasp != &js_NumberClass &&
1849 clasp != &js_BooleanClass) {
1850 goto bad;
1854 str = js_ValueToString(cx, Valueify(v));
1855 if (!str)
1856 return NULL;
1857 if (str->empty()) {
1858 length = 0;
1859 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1860 xml = NULL;
1861 #endif
1862 } else {
1863 xml = ParseXMLSource(cx, str);
1864 if (!xml)
1865 return NULL;
1866 length = JSXML_LENGTH(xml);
1869 if (length == 0) {
1870 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
1871 if (!obj)
1872 return NULL;
1873 } else if (length == 1) {
1874 xml = OrphanXMLChild(cx, xml, 0);
1875 if (!xml)
1876 return NULL;
1877 obj = js_GetXMLObject(cx, xml);
1878 if (!obj)
1879 return NULL;
1880 } else {
1881 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
1882 return NULL;
1884 return obj;
1886 bad:
1887 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
1888 JSDVG_IGNORE_STACK, Valueify(v), NULL);
1889 return NULL;
1892 static JSBool
1893 Append(JSContext *cx, JSXML *list, JSXML *kid);
1895 static JSObject *
1896 ToXMLList(JSContext *cx, jsval v)
1898 JSObject *obj, *listobj;
1899 JSXML *xml, *list, *kid;
1900 Class *clasp;
1901 JSString *str;
1902 uint32 i, length;
1904 if (JSVAL_IS_PRIMITIVE(v)) {
1905 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1906 goto bad;
1907 } else {
1908 obj = JSVAL_TO_OBJECT(v);
1909 if (obj->isXML()) {
1910 xml = (JSXML *) obj->getPrivate();
1911 if (xml->xml_class != JSXML_CLASS_LIST) {
1912 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1913 if (!listobj)
1914 return NULL;
1915 list = (JSXML *) listobj->getPrivate();
1916 if (!Append(cx, list, xml))
1917 return NULL;
1918 return listobj;
1920 return obj;
1923 clasp = obj->getClass();
1924 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1925 JS_ASSERT(0);
1928 if (clasp != &js_StringClass &&
1929 clasp != &js_NumberClass &&
1930 clasp != &js_BooleanClass) {
1931 goto bad;
1935 str = js_ValueToString(cx, Valueify(v));
1936 if (!str)
1937 return NULL;
1938 if (str->empty()) {
1939 xml = NULL;
1940 length = 0;
1941 } else {
1942 if (!js_EnterLocalRootScope(cx))
1943 return NULL;
1944 xml = ParseXMLSource(cx, str);
1945 if (!xml) {
1946 js_LeaveLocalRootScope(cx);
1947 return NULL;
1949 length = JSXML_LENGTH(xml);
1952 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1953 if (listobj) {
1954 list = (JSXML *) listobj->getPrivate();
1955 for (i = 0; i < length; i++) {
1956 kid = OrphanXMLChild(cx, xml, i);
1957 if (!kid || !Append(cx, list, kid)) {
1958 listobj = NULL;
1959 break;
1964 if (xml)
1965 js_LeaveLocalRootScopeWithResult(cx, listobj);
1966 return listobj;
1968 bad:
1969 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
1970 JSDVG_IGNORE_STACK, Valueify(v), NULL);
1971 return NULL;
1975 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
1976 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
1977 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
1978 * MakeXMLSpecialString subroutine.
1980 * These functions mutate sb, leaving it empty.
1982 static JSFlatString *
1983 MakeXMLSpecialString(JSContext *cx, StringBuffer &sb,
1984 JSString *str, JSString *str2,
1985 const jschar *prefix, size_t prefixlength,
1986 const jschar *suffix, size_t suffixlength)
1988 if (!sb.append(prefix, prefixlength) || !sb.append(str))
1989 return NULL;
1990 if (str2 && !str2->empty()) {
1991 if (!sb.append(' ') || !sb.append(str2))
1992 return NULL;
1994 if (!sb.append(suffix, suffixlength))
1995 return NULL;
1997 return sb.finishString();
2000 static JSFlatString *
2001 MakeXMLCDATAString(JSContext *cx, StringBuffer &sb, JSString *str)
2003 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2004 'C', 'D', 'A', 'T', 'A',
2005 '['};
2006 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2008 return MakeXMLSpecialString(cx, sb, str, NULL,
2009 cdata_prefix_ucNstr, 9,
2010 cdata_suffix_ucNstr, 3);
2013 static JSFlatString *
2014 MakeXMLCommentString(JSContext *cx, StringBuffer &sb, JSString *str)
2016 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2017 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2019 return MakeXMLSpecialString(cx, sb, str, NULL,
2020 comment_prefix_ucNstr, 4,
2021 comment_suffix_ucNstr, 3);
2024 static JSFlatString *
2025 MakeXMLPIString(JSContext *cx, StringBuffer &sb, JSString *name,
2026 JSString *value)
2028 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2029 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2031 return MakeXMLSpecialString(cx, sb, name, value,
2032 pi_prefix_ucNstr, 2,
2033 pi_suffix_ucNstr, 2);
2037 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2038 * equals, a double quote, an attribute value, and a closing double quote.
2040 static bool
2041 AppendAttributeValue(JSContext *cx, StringBuffer &sb, JSString *valstr)
2043 if (!sb.append('='))
2044 return false;
2045 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2046 return valstr && sb.append(valstr);
2050 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2052 * These functions mutate sb, leaving it empty.
2054 static JSFlatString *
2055 EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32 toSourceFlag)
2057 size_t length = str->length();
2058 const jschar *start = str->getChars(cx);
2059 if (!start)
2060 return NULL;
2062 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2063 jschar c = *cp;
2064 switch (*cp) {
2065 case '<':
2066 if (!sb.append(js_lt_entity_str))
2067 return NULL;
2068 break;
2069 case '>':
2070 if (!sb.append(js_gt_entity_str))
2071 return NULL;
2072 break;
2073 case '&':
2074 if (!sb.append(js_amp_entity_str))
2075 return NULL;
2076 break;
2077 case '{':
2079 * If EscapeElementValue is called by toSource/uneval, we also need
2080 * to escape '{'. See bug 463360.
2082 if (toSourceFlag) {
2083 if (!sb.append(js_leftcurly_entity_str))
2084 return NULL;
2085 break;
2087 /* FALL THROUGH */
2088 default:
2089 if (!sb.append(c))
2090 return NULL;
2093 return sb.finishString();
2097 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2099 * These functions mutate sb, leaving it empty.
2101 static JSFlatString *
2102 EscapeAttributeValue(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote)
2104 size_t length = str->length();
2105 const jschar *start = str->getChars(cx);
2106 if (!start)
2107 return NULL;
2109 if (quote && !sb.append('"'))
2110 return NULL;
2112 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2113 jschar c = *cp;
2114 switch (c) {
2115 case '"':
2116 if (!sb.append(js_quot_entity_str))
2117 return NULL;
2118 break;
2119 case '<':
2120 if (!sb.append(js_lt_entity_str))
2121 return NULL;
2122 break;
2123 case '&':
2124 if (!sb.append(js_amp_entity_str))
2125 return NULL;
2126 break;
2127 case '\n':
2128 if (!sb.append("&#xA;"))
2129 return NULL;
2130 break;
2131 case '\r':
2132 if (!sb.append("&#xD;"))
2133 return NULL;
2134 break;
2135 case '\t':
2136 if (!sb.append("&#x9;"))
2137 return NULL;
2138 break;
2139 default:
2140 if (!sb.append(c))
2141 return NULL;
2145 if (quote && !sb.append('"'))
2146 return NULL;
2148 return sb.finishString();
2151 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2152 static JSObject *
2153 GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2155 JSLinearString *uri, *prefix, *nsprefix;
2156 JSObject *match, *ns;
2157 uint32 i, n;
2158 jsval argv[2];
2160 uri = qn->getNameURI();
2161 prefix = qn->getNamePrefix();
2162 JS_ASSERT(uri);
2163 if (!uri) {
2164 JSAutoByteString bytes;
2165 const char *s = !prefix ?
2166 js_undefined_str
2167 : js_ValueToPrintable(cx, StringValue(prefix), &bytes);
2168 if (s)
2169 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, s);
2170 return NULL;
2173 /* Look for a matching namespace in inScopeNSes, if provided. */
2174 match = NULL;
2175 if (inScopeNSes) {
2176 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2177 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2178 if (!ns)
2179 continue;
2182 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2183 * If we preserve prefixes, we must match null prefix against
2184 * an empty prefix of ns, in order to avoid generating redundant
2185 * prefixed and default namespaces for cases such as:
2187 * x = <t xmlns="http://foo.com"/>
2188 * print(x.toXMLString());
2190 * Per 10.3.2.1, the namespace attribute in t has an empty string
2191 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2193 * 1. If the [local name] property of a is "xmlns"
2194 * a. Map ns.prefix to the empty string
2196 * But t's name has a null prefix in this implementation, meaning
2197 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2198 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2199 * saying how "no value" maps to an ECMA-357 value -- but it must
2200 * map to the *undefined* prefix value).
2202 * Since "" != undefined (or null, in the current implementation)
2203 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2204 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2205 * This spec bug leads to ToXMLString results that duplicate the
2206 * declared namespace.
2208 if (EqualStrings(ns->getNameURI(), uri)) {
2209 nsprefix = ns->getNamePrefix();
2210 if (nsprefix == prefix ||
2211 ((nsprefix && prefix)
2212 ? EqualStrings(nsprefix, prefix)
2213 : (nsprefix ? nsprefix : prefix)->empty())) {
2214 match = ns;
2215 break;
2221 /* If we didn't match, make a new namespace from qn. */
2222 if (!match) {
2223 argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2224 argv[1] = STRING_TO_JSVAL(uri);
2225 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL,
2226 2, Valueify(argv));
2227 if (!ns)
2228 return NULL;
2229 match = ns;
2231 return match;
2234 static JSLinearString *
2235 GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls)
2237 const jschar *cp, *start, *end;
2238 size_t length, newlength, offset;
2239 uint32 i, n, m, serial;
2240 jschar *bp, *dp;
2241 JSBool done;
2242 JSObject *ns;
2243 JSLinearString *nsprefix, *prefix;
2245 JS_ASSERT(!uri->empty());
2248 * If there are no *declared* namespaces, skip all collision detection and
2249 * return a short prefix quickly; an example of such a situation:
2251 * var x = <f/>;
2252 * var n = new Namespace("http://example.com/");
2253 * x.@n::att = "val";
2254 * x.toXMLString();
2256 * This is necessary for various log10 uses below to be valid.
2258 if (decls->length == 0)
2259 return js_NewStringCopyZ(cx, "a");
2262 * Try peeling off the last filename suffix or pathname component till
2263 * we have a valid XML name. This heuristic will prefer "xul" given
2264 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2265 * likely URI of the form ".../xbl2/2005".
2267 start = uri->chars();
2268 end = start + uri->length();
2269 cp = end;
2270 while (--cp > start) {
2271 if (*cp == '.' || *cp == '/' || *cp == ':') {
2272 ++cp;
2273 length = end - cp;
2274 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2275 break;
2276 end = --cp;
2279 length = end - cp;
2282 * If the namespace consisted only of non-XML names or names that begin
2283 * case-insensitively with "xml", arbitrarily create a prefix consisting
2284 * of 'a's of size length (allowing dp-calculating code to work with or
2285 * without this branch executing) plus the space for storing a hyphen and
2286 * the serial number (avoiding reallocation if a collision happens).
2288 bp = (jschar *) cp;
2289 newlength = length;
2290 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2291 newlength = length + 2 + (size_t) log10((double) decls->length);
2292 bp = (jschar *)
2293 cx->malloc((newlength + 1) * sizeof(jschar));
2294 if (!bp)
2295 return NULL;
2297 bp[newlength] = 0;
2298 for (i = 0; i < newlength; i++)
2299 bp[i] = 'a';
2303 * Now search through decls looking for a collision. If we collide with
2304 * an existing prefix, start tacking on a hyphen and a serial number.
2306 serial = 0;
2307 do {
2308 done = JS_TRUE;
2309 for (i = 0, n = decls->length; i < n; i++) {
2310 ns = XMLARRAY_MEMBER(decls, i, JSObject);
2311 if (ns && (nsprefix = ns->getNamePrefix()) &&
2312 nsprefix->length() == newlength &&
2313 !memcmp(nsprefix->chars(), bp,
2314 newlength * sizeof(jschar))) {
2315 if (bp == cp) {
2316 newlength = length + 2 + (size_t) log10((double) n);
2317 bp = (jschar *)
2318 cx->malloc((newlength + 1) * sizeof(jschar));
2319 if (!bp)
2320 return NULL;
2321 js_strncpy(bp, cp, length);
2324 ++serial;
2325 JS_ASSERT(serial <= n);
2326 dp = bp + length + 2 + (size_t) log10((double) serial);
2327 *dp = 0;
2328 for (m = serial; m != 0; m /= 10)
2329 *--dp = (jschar)('0' + m % 10);
2330 *--dp = '-';
2331 JS_ASSERT(dp == bp + length);
2333 done = JS_FALSE;
2334 break;
2337 } while (!done);
2339 if (bp == cp) {
2340 offset = cp - start;
2341 prefix = js_NewDependentString(cx, uri, offset, length);
2342 } else {
2343 prefix = js_NewString(cx, bp, newlength);
2344 if (!prefix)
2345 cx->free(bp);
2347 return prefix;
2350 static JSBool
2351 namespace_match(const void *a, const void *b)
2353 const JSObject *nsa = (const JSObject *) a;
2354 const JSObject *nsb = (const JSObject *) b;
2355 JSLinearString *prefixa, *prefixb = nsb->getNamePrefix();
2357 if (prefixb) {
2358 prefixa = nsa->getNamePrefix();
2359 return prefixa && EqualStrings(prefixa, prefixb);
2361 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
2364 /* ECMA-357 10.2.1 and 10.2.2 */
2365 #define TO_SOURCE_FLAG 0x80000000
2367 static JSString *
2368 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2369 uint32 indentLevel)
2371 JSBool pretty, indentKids;
2372 StringBuffer sb(cx);
2373 JSString *str;
2374 JSLinearString *prefix, *nsuri;
2375 uint32 i, n, nextIndentLevel;
2376 JSObject *ns, *ns2;
2377 AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx);
2379 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2380 return NULL;
2382 if (pretty) {
2383 if (!sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2384 return NULL;
2387 str = NULL;
2389 switch (xml->xml_class) {
2390 case JSXML_CLASS_TEXT:
2391 /* Step 4. */
2392 if (pretty) {
2393 str = ChompXMLWhitespace(cx, xml->xml_value);
2394 if (!str)
2395 return NULL;
2396 } else {
2397 str = xml->xml_value;
2399 return EscapeElementValue(cx, sb, str, indentLevel & TO_SOURCE_FLAG);
2401 case JSXML_CLASS_ATTRIBUTE:
2402 /* Step 5. */
2403 return EscapeAttributeValue(cx, sb, xml->xml_value,
2404 (indentLevel & TO_SOURCE_FLAG) != 0);
2406 case JSXML_CLASS_COMMENT:
2407 /* Step 6. */
2408 return MakeXMLCommentString(cx, sb, xml->xml_value);
2410 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2411 /* Step 7. */
2412 return MakeXMLPIString(cx, sb, xml->name->getQNameLocalName(),
2413 xml->xml_value);
2415 case JSXML_CLASS_LIST:
2416 /* ECMA-357 10.2.2. */
2418 JSXMLArrayCursor cursor(&xml->xml_kids);
2419 i = 0;
2420 while (JSXML *kid = (JSXML *) cursor.getNext()) {
2421 if (pretty && i != 0) {
2422 if (!sb.append('\n'))
2423 return NULL;
2426 JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2427 if (!kidstr || !sb.append(kidstr))
2428 return NULL;
2429 ++i;
2433 if (sb.empty())
2434 return cx->runtime->emptyString;
2435 return sb.finishString();
2437 default:;
2440 /* After this point, control must flow through label out: to exit. */
2441 if (!js_EnterLocalRootScope(cx))
2442 return NULL;
2444 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2445 if (!ancestorNSes)
2446 ancestorNSes = &empty.array;
2448 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2450 JSXMLArrayCursor cursor(&xml->xml_namespaces);
2451 while ((ns = (JSObject *) cursor.getNext()) != NULL) {
2452 if (!IsDeclared(ns))
2453 continue;
2454 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2455 /* NOTE: may want to exclude unused namespaces here. */
2456 ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), JS_TRUE);
2457 if (!ns2 || !XMLARRAY_APPEND(cx, &decls.array, ns2))
2458 goto out;
2464 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2465 * not own its member references. In the spec, ancdecls has no name, but
2466 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2469 if (!ancdecls.array.setCapacity(cx, ancestorNSes->length + decls.length()))
2470 goto out;
2471 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2472 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2473 if (!ns2)
2474 continue;
2475 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls.array, ns2, namespace_identity));
2476 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2477 goto out;
2479 for (i = 0, n = decls.length(); i < n; i++) {
2480 ns2 = XMLARRAY_MEMBER(&decls.array, i, JSObject);
2481 if (!ns2)
2482 continue;
2483 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls.array, ns2, namespace_identity));
2484 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2485 goto out;
2488 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2489 ns = GetNamespace(cx, xml->name, &ancdecls.array);
2490 if (!ns)
2491 goto out;
2493 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2494 prefix = ns->getNamePrefix();
2495 if (!prefix) {
2497 * Create a namespace prefix that isn't used by any member of decls.
2498 * Assign the new prefix to a copy of ns. Flag this namespace as if
2499 * it were declared, for assertion-testing's sake later below.
2501 * Erratum: if prefix and xml->name are both null (*undefined* in
2502 * ECMA-357), we know that xml was named using the default namespace
2503 * (proof: see GetNamespace and the Namespace constructor called with
2504 * two arguments). So we ought not generate a new prefix here, when
2505 * we can declare ns as the default namespace for xml.
2507 * This helps descendants inherit the namespace instead of redundantly
2508 * redeclaring it with generated prefixes in each descendant.
2510 nsuri = ns->getNameURI();
2511 if (!xml->name->getNamePrefix()) {
2512 prefix = cx->runtime->emptyString;
2513 } else {
2514 prefix = GeneratePrefix(cx, nsuri, &ancdecls.array);
2515 if (!prefix)
2516 goto out;
2518 ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2519 if (!ns)
2520 goto out;
2523 * If the xml->name was unprefixed, we must remove any declared default
2524 * namespace from decls before appending ns. How can you get a default
2525 * namespace in decls that doesn't match the one from name? Apparently
2526 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2527 * to fix this is to update x's in-scope namespaces when setNamespace
2528 * is called, but that's not specified by ECMA-357.
2530 * Likely Erratum here, depending on whether the lack of update to x's
2531 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2532 * erratum or not. Note that changing setNamespace to update the list
2533 * of in-scope namespaces will change x.namespaceDeclarations().
2535 if (prefix->empty()) {
2536 i = XMLArrayFindMember(&decls.array, ns, namespace_match);
2537 if (i != XML_NOT_FOUND)
2538 XMLArrayDelete(cx, &decls.array, i, JS_TRUE);
2542 * In the spec, ancdecls has no name, but is always written out as
2543 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2544 * that union in ancdecls, any time we append a namespace strong
2545 * ref to decls, we must also append a weak ref to ancdecls. Order
2546 * matters here: code at label out: releases strong refs in decls.
2548 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns) ||
2549 !XMLARRAY_APPEND(cx, &decls.array, ns)) {
2550 goto out;
2554 /* Format the element or point-tag into sb. */
2555 if (!sb.append('<'))
2556 goto out;
2558 if (!prefix->empty()) {
2559 if (!sb.append(prefix) || !sb.append(':'))
2560 goto out;
2562 if (!sb.append(xml->name->getQNameLocalName()))
2563 goto out;
2566 * Step 16 makes a union to avoid writing two loops in step 17, to share
2567 * common attribute value appending spec-code. We prefer two loops for
2568 * faster code and less data overhead.
2571 /* Step 17(b): append attributes. */
2573 JSXMLArrayCursor cursor(&xml->xml_attrs);
2574 while (JSXML *attr = (JSXML *) cursor.getNext()) {
2575 if (!sb.append(' '))
2576 goto out;
2577 ns2 = GetNamespace(cx, attr->name, &ancdecls.array);
2578 if (!ns2)
2579 goto out;
2581 /* 17(b)(ii): NULL means *undefined* here. */
2582 prefix = ns2->getNamePrefix();
2583 if (!prefix) {
2584 prefix = GeneratePrefix(cx, ns2->getNameURI(), &ancdecls.array);
2585 if (!prefix)
2586 goto out;
2588 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2589 ns2 = NewXMLNamespace(cx, prefix, ns2->getNameURI(), JS_TRUE);
2590 if (!ns2)
2591 goto out;
2594 * In the spec, ancdecls has no name, but is always written out as
2595 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2596 * that union in ancdecls, any time we append a namespace strong
2597 * ref to decls, we must also append a weak ref to ancdecls. Order
2598 * matters here: code at label out: releases strong refs in decls.
2600 if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2) ||
2601 !XMLARRAY_APPEND(cx, &decls.array, ns2)) {
2602 goto out;
2606 /* 17(b)(iii). */
2607 if (!prefix->empty()) {
2608 if (!sb.append(prefix) || !sb.append(':'))
2609 goto out;
2612 /* 17(b)(iv). */
2613 if (!sb.append(attr->name->getQNameLocalName()))
2614 goto out;
2616 /* 17(d-g). */
2617 if (!AppendAttributeValue(cx, sb, attr->xml_value))
2618 goto out;
2622 /* Step 17(c): append XML namespace declarations. */
2624 JSXMLArrayCursor cursor(&decls.array);
2625 while (JSObject *ns3 = (JSObject *) cursor.getNext()) {
2626 JS_ASSERT(IsDeclared(ns3));
2628 if (!sb.append(" xmlns"))
2629 goto out;
2631 /* 17(c)(ii): NULL means *undefined* here. */
2632 prefix = ns3->getNamePrefix();
2633 if (!prefix) {
2634 prefix = GeneratePrefix(cx, ns3->getNameURI(), &ancdecls.array);
2635 if (!prefix)
2636 goto out;
2637 ns3->setNamePrefix(prefix);
2640 /* 17(c)(iii). */
2641 if (!prefix->empty()) {
2642 if (!sb.append(':') || !sb.append(prefix))
2643 goto out;
2646 /* 17(d-g). */
2647 if (!AppendAttributeValue(cx, sb, ns3->getNameURI()))
2648 goto out;
2652 /* Step 18: handle point tags. */
2653 n = xml->xml_kids.length;
2654 if (n == 0) {
2655 if (!sb.append("/>"))
2656 goto out;
2657 } else {
2658 /* Steps 19 through 25: handle element content, and open the end-tag. */
2659 if (!sb.append('>'))
2660 goto out;
2662 JSXML *kid;
2663 indentKids = n > 1 ||
2664 (n == 1 &&
2665 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2666 kid->xml_class != JSXML_CLASS_TEXT);
2669 if (pretty && indentKids) {
2670 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2671 goto out;
2672 nextIndentLevel = indentLevel + i;
2673 } else {
2674 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2678 JSXMLArrayCursor cursor(&xml->xml_kids);
2679 while (JSXML *kid = (JSXML *) cursor.getNext()) {
2680 if (pretty && indentKids) {
2681 if (!sb.append('\n'))
2682 goto out;
2685 JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls.array, nextIndentLevel);
2686 if (!kidstr)
2687 goto out;
2689 if (!sb.append(kidstr))
2690 goto out;
2694 if (pretty && indentKids) {
2695 if (!sb.append('\n') ||
2696 !sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2697 goto out;
2699 if (!sb.append("</"))
2700 goto out;
2702 /* Step 26. */
2703 prefix = ns->getNamePrefix();
2704 if (prefix && !prefix->empty()) {
2705 if (!sb.append(prefix) || !sb.append(':'))
2706 goto out;
2709 /* Step 27. */
2710 if (!sb.append(xml->name->getQNameLocalName()) || !sb.append('>'))
2711 goto out;
2714 str = sb.finishString();
2715 out:
2716 js_LeaveLocalRootScopeWithResult(cx, str);
2717 return str;
2720 /* ECMA-357 10.2 */
2721 static JSString *
2722 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
2724 JSObject *obj;
2725 JSString *str;
2726 JSXML *xml;
2728 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2729 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2730 JSMSG_BAD_XML_CONVERSION,
2731 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2732 return NULL;
2735 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2736 return js_ValueToString(cx, Valueify(v));
2738 if (JSVAL_IS_STRING(v)) {
2739 StringBuffer sb(cx);
2740 return EscapeElementValue(cx, sb, JSVAL_TO_STRING(v), toSourceFlag);
2743 obj = JSVAL_TO_OBJECT(v);
2744 if (!obj->isXML()) {
2745 if (!DefaultValue(cx, obj, JSTYPE_STRING, Valueify(&v)))
2746 return NULL;
2747 str = js_ValueToString(cx, Valueify(v));
2748 if (!str)
2749 return NULL;
2750 StringBuffer sb(cx);
2751 return EscapeElementValue(cx, sb, str, toSourceFlag);
2754 /* Handle non-element cases in this switch, returning from each case. */
2755 xml = (JSXML *) obj->getPrivate();
2756 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
2759 static JSObject *
2760 ToAttributeName(JSContext *cx, jsval v)
2762 JSLinearString *name, *uri, *prefix;
2763 JSObject *obj;
2764 Class *clasp;
2765 JSObject *qn;
2767 if (JSVAL_IS_STRING(v)) {
2768 name = JSVAL_TO_STRING(v)->ensureLinear(cx);
2769 if (!name)
2770 return NULL;
2771 uri = prefix = cx->runtime->emptyString;
2772 } else {
2773 if (JSVAL_IS_PRIMITIVE(v)) {
2774 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2775 JSDVG_IGNORE_STACK, Valueify(v), NULL);
2776 return NULL;
2779 obj = JSVAL_TO_OBJECT(v);
2780 clasp = obj->getClass();
2781 if (clasp == &js_AttributeNameClass)
2782 return obj;
2784 if (clasp == &js_QNameClass) {
2785 qn = obj;
2786 uri = qn->getNameURI();
2787 prefix = qn->getNamePrefix();
2788 name = qn->getQNameLocalName();
2789 } else {
2790 if (clasp == &js_AnyNameClass) {
2791 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2792 } else {
2793 JSString *str = js_ValueToString(cx, Valueify(v));
2794 if (!str)
2795 return NULL;
2796 name = str->ensureLinear(cx);
2797 if (!name)
2798 return NULL;
2800 uri = prefix = cx->runtime->emptyString;
2804 qn = NewXMLAttributeName(cx, uri, prefix, name);
2805 if (!qn)
2806 return NULL;
2807 return qn;
2810 static void
2811 ReportBadXMLName(JSContext *cx, const Value &idval)
2813 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL);
2816 static JSBool
2817 IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
2819 JSAtom *atom;
2820 JSLinearString *uri;
2822 atom = cx->runtime->atomState.functionNamespaceURIAtom;
2823 uri = qn->getNameURI();
2824 if (uri &&
2825 (uri == ATOM_TO_STRING(atom) ||
2826 EqualStrings(uri, ATOM_TO_STRING(atom)))) {
2827 return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp);
2829 *funidp = JSID_VOID;
2830 return JS_TRUE;
2833 JSBool
2834 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
2836 if (obj->getClass() == &js_QNameClass)
2837 return IsFunctionQName(cx, obj, funidp);
2838 *funidp = JSID_VOID;
2839 return JS_TRUE;
2842 static JSObject *
2843 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
2845 JSAtom *atomizedName;
2846 JSString *name;
2847 JSObject *obj;
2848 Class *clasp;
2849 uint32 index;
2851 if (JSVAL_IS_STRING(v)) {
2852 name = JSVAL_TO_STRING(v);
2853 } else {
2854 if (JSVAL_IS_PRIMITIVE(v)) {
2855 ReportBadXMLName(cx, Valueify(v));
2856 return NULL;
2859 obj = JSVAL_TO_OBJECT(v);
2860 clasp = obj->getClass();
2861 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass)
2862 goto out;
2863 if (clasp == &js_AnyNameClass) {
2864 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2865 goto construct;
2867 name = js_ValueToString(cx, Valueify(v));
2868 if (!name)
2869 return NULL;
2872 atomizedName = js_AtomizeString(cx, name, 0);
2873 if (!atomizedName)
2874 return NULL;
2877 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
2879 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
2881 * First, _P_ should be _s_, to refer to the given string.
2883 * Second, why does ToXMLName applied to the string type throw TypeError
2884 * only for numeric literals without any leading or trailing whitespace?
2886 * If the idea is to reject uint32 property names, then the check needs to
2887 * be stricter, to exclude hexadecimal and floating point literals.
2889 if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index))
2890 goto bad;
2892 if (*atomizedName->chars() == '@') {
2893 name = js_NewDependentString(cx, name, 1, name->length() - 1);
2894 if (!name)
2895 return NULL;
2896 *funidp = JSID_VOID;
2897 return ToAttributeName(cx, STRING_TO_JSVAL(name));
2900 construct:
2901 v = STRING_TO_JSVAL(name);
2902 obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v));
2903 if (!obj)
2904 return NULL;
2906 out:
2907 if (!IsFunctionQName(cx, obj, funidp))
2908 return NULL;
2909 return obj;
2911 bad:
2912 JSAutoByteString bytes;
2913 if (js_ValueToPrintable(cx, StringValue(name), &bytes))
2914 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, bytes.ptr());
2915 return NULL;
2918 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
2919 static JSBool
2920 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
2922 JSLinearString *prefix, *prefix2;
2923 JSObject *match, *ns2;
2924 uint32 i, n, m;
2926 if (xml->xml_class != JSXML_CLASS_ELEMENT)
2927 return JS_TRUE;
2929 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
2930 prefix = ns->getNamePrefix();
2931 if (!prefix) {
2932 match = NULL;
2933 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2934 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2935 if (ns2 && EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
2936 match = ns2;
2937 break;
2940 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
2941 return JS_FALSE;
2942 } else {
2943 if (prefix->empty() && xml->name->getNameURI()->empty())
2944 return JS_TRUE;
2945 match = NULL;
2946 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2947 m = XML_NOT_FOUND;
2948 #endif
2949 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2950 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2951 if (ns2 && (prefix2 = ns2->getNamePrefix()) &&
2952 EqualStrings(prefix2, prefix)) {
2953 match = ns2;
2954 m = i;
2955 break;
2958 if (match && !EqualStrings(match->getNameURI(), ns->getNameURI())) {
2959 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
2960 JSObject);
2961 JS_ASSERT(ns2 == match);
2962 match->clearNamePrefix();
2963 if (!AddInScopeNamespace(cx, xml, match))
2964 return JS_FALSE;
2966 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
2967 return JS_FALSE;
2970 /* OPTION: enforce that descendants have superset namespaces. */
2971 return JS_TRUE;
2974 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
2975 static JSBool
2976 Append(JSContext *cx, JSXML *list, JSXML *xml)
2978 uint32 i, j, k, n;
2979 JSXML *kid;
2981 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
2982 i = list->xml_kids.length;
2983 n = 1;
2984 if (xml->xml_class == JSXML_CLASS_LIST) {
2985 list->xml_target = xml->xml_target;
2986 list->xml_targetprop = xml->xml_targetprop;
2987 n = JSXML_LENGTH(xml);
2988 k = i + n;
2989 if (!list->xml_kids.setCapacity(cx, k))
2990 return JS_FALSE;
2991 for (j = 0; j < n; j++) {
2992 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
2993 if (kid)
2994 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
2996 return JS_TRUE;
2999 list->xml_target = xml->parent;
3000 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3001 list->xml_targetprop = NULL;
3002 else
3003 list->xml_targetprop = xml->name;
3004 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3005 return JS_FALSE;
3006 return JS_TRUE;
3009 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3010 static JSXML *
3011 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3013 static JSXML *
3014 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3016 JSXML *copy;
3018 /* Our caller may not be protecting newborns with a local root scope. */
3019 if (!js_EnterLocalRootScope(cx))
3020 return NULL;
3021 copy = DeepCopyInLRS(cx, xml, flags);
3022 if (copy) {
3023 if (obj) {
3024 /* Caller provided the object for this copy, hook 'em up. */
3025 obj->setPrivate(copy);
3026 copy->object = obj;
3027 } else if (!js_GetXMLObject(cx, copy)) {
3028 copy = NULL;
3031 js_LeaveLocalRootScopeWithResult(cx, copy);
3032 return copy;
3036 * (i) We must be in a local root scope (InLRS).
3037 * (ii) parent must have a rooted object.
3038 * (iii) from's owning object must be locked if not thread-local.
3040 static JSBool
3041 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3042 uintN flags)
3044 uint32 j, n;
3045 JSXML *kid2;
3046 JSString *str;
3048 n = from->length;
3049 if (!to->setCapacity(cx, n))
3050 return JS_FALSE;
3052 JSXMLArrayCursor cursor(from);
3053 j = 0;
3054 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3055 if ((flags & XSF_IGNORE_COMMENTS) &&
3056 kid->xml_class == JSXML_CLASS_COMMENT) {
3057 continue;
3059 if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3060 kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3061 continue;
3063 if ((flags & XSF_IGNORE_WHITESPACE) &&
3064 (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3065 continue;
3067 kid2 = DeepCopyInLRS(cx, kid, flags);
3068 if (!kid2) {
3069 to->length = j;
3070 return JS_FALSE;
3073 if ((flags & XSF_IGNORE_WHITESPACE) &&
3074 n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3075 str = ChompXMLWhitespace(cx, kid2->xml_value);
3076 if (!str) {
3077 to->length = j;
3078 return JS_FALSE;
3080 kid2->xml_value = str;
3083 XMLARRAY_SET_MEMBER(to, j, kid2);
3084 ++j;
3085 if (parent->xml_class != JSXML_CLASS_LIST)
3086 kid2->parent = parent;
3089 if (j < n)
3090 to->trim();
3091 return JS_TRUE;
3094 static JSXML *
3095 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
3097 JSXML *copy;
3098 JSObject *qn;
3099 JSBool ok;
3100 uint32 i, n;
3101 JSObject *ns, *ns2;
3103 JS_CHECK_RECURSION(cx, return NULL);
3105 copy = js_NewXML(cx, JSXMLClass(xml->xml_class));
3106 if (!copy)
3107 return NULL;
3108 qn = xml->name;
3109 if (qn) {
3110 qn = NewXMLQName(cx, qn->getNameURI(), qn->getNamePrefix(), qn->getQNameLocalName());
3111 if (!qn) {
3112 ok = JS_FALSE;
3113 goto out;
3116 copy->name = qn;
3117 copy->xml_flags = xml->xml_flags;
3119 if (JSXML_HAS_VALUE(xml)) {
3120 copy->xml_value = xml->xml_value;
3121 ok = JS_TRUE;
3122 } else {
3123 ok = DeepCopySetInLRS(cx, &xml->xml_kids, &copy->xml_kids, copy, flags);
3124 if (!ok)
3125 goto out;
3127 if (xml->xml_class == JSXML_CLASS_LIST) {
3128 copy->xml_target = xml->xml_target;
3129 copy->xml_targetprop = xml->xml_targetprop;
3130 } else {
3131 n = xml->xml_namespaces.length;
3132 ok = copy->xml_namespaces.setCapacity(cx, n);
3133 if (!ok)
3134 goto out;
3135 for (i = 0; i < n; i++) {
3136 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3137 if (!ns)
3138 continue;
3139 ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(),
3140 IsDeclared(ns));
3141 if (!ns2) {
3142 copy->xml_namespaces.length = i;
3143 ok = JS_FALSE;
3144 goto out;
3146 XMLARRAY_SET_MEMBER(&copy->xml_namespaces, i, ns2);
3149 ok = DeepCopySetInLRS(cx, &xml->xml_attrs, &copy->xml_attrs, copy,
3151 if (!ok)
3152 goto out;
3156 out:
3157 if (!ok)
3158 return NULL;
3159 return copy;
3162 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3163 static void
3164 DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index)
3166 JSXML *kid;
3168 if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3169 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3170 if (kid)
3171 kid->parent = NULL;
3172 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3176 typedef JSBool (*JSXMLNameMatcher)(JSObject *nameqn, JSXML *xml);
3178 static JSBool
3179 MatchAttrName(JSObject *nameqn, JSXML *attr)
3181 JSObject *attrqn = attr->name;
3182 JSLinearString *localName = nameqn->getQNameLocalName();
3183 JSLinearString *uri;
3185 return (IS_STAR(localName) ||
3186 EqualStrings(attrqn->getQNameLocalName(), localName)) &&
3187 (!(uri = nameqn->getNameURI()) ||
3188 EqualStrings(attrqn->getNameURI(), uri));
3191 static JSBool
3192 MatchElemName(JSObject *nameqn, JSXML *elem)
3194 JSLinearString *localName = nameqn->getQNameLocalName();
3195 JSLinearString *uri;
3197 return (IS_STAR(localName) ||
3198 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3199 EqualStrings(elem->name->getQNameLocalName(), localName))) &&
3200 (!(uri = nameqn->getNameURI()) ||
3201 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3202 EqualStrings(elem->name->getNameURI(), uri)));
3205 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3206 static JSBool
3207 DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list)
3209 uint32 i, n;
3210 JSXML *attr, *kid;
3212 JS_CHECK_RECURSION(cx, return JS_FALSE);
3214 if (xml->xml_class == JSXML_CLASS_ELEMENT &&
3215 nameqn->getClass() == &js_AttributeNameClass) {
3216 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
3217 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3218 if (attr && MatchAttrName(nameqn, attr)) {
3219 if (!Append(cx, list, attr))
3220 return JS_FALSE;
3225 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3226 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3227 if (!kid)
3228 continue;
3229 if (nameqn->getClass() != &js_AttributeNameClass &&
3230 MatchElemName(nameqn, kid)) {
3231 if (!Append(cx, list, kid))
3232 return JS_FALSE;
3234 if (!DescendantsHelper(cx, kid, nameqn, list))
3235 return JS_FALSE;
3237 return JS_TRUE;
3240 static JSXML *
3241 Descendants(JSContext *cx, JSXML *xml, jsval id)
3243 jsid funid;
3244 JSObject *nameqn;
3245 JSObject *listobj;
3246 JSXML *list, *kid;
3247 uint32 i, n;
3248 JSBool ok;
3250 nameqn = ToXMLName(cx, id, &funid);
3251 if (!nameqn)
3252 return NULL;
3254 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3255 if (!listobj)
3256 return NULL;
3257 list = (JSXML *) listobj->getPrivate();
3258 if (!JSID_IS_VOID(funid))
3259 return list;
3262 * Protect nameqn's object and strings from GC by linking list to it
3263 * temporarily. The newborn GC root for the last allocated object
3264 * protects listobj, which protects list. Any other object allocations
3265 * occurring beneath DescendantsHelper use local roots.
3267 list->name = nameqn;
3268 if (!js_EnterLocalRootScope(cx))
3269 return NULL;
3270 if (xml->xml_class == JSXML_CLASS_LIST) {
3271 ok = JS_TRUE;
3272 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
3273 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3274 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
3275 ok = DescendantsHelper(cx, kid, nameqn, list);
3276 if (!ok)
3277 break;
3280 } else {
3281 ok = DescendantsHelper(cx, xml, nameqn, list);
3283 js_LeaveLocalRootScopeWithResult(cx, list);
3284 if (!ok)
3285 return NULL;
3286 list->name = NULL;
3287 return list;
3290 /* Recursive (JSXML *) parameterized version of Equals. */
3291 static JSBool
3292 XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3294 JSObject *qn, *vqn;
3295 uint32 i, j, n;
3296 JSXML *kid, *vkid, *attr, *vattr;
3297 JSObject *xobj, *vobj;
3299 retry:
3300 if (xml->xml_class != vxml->xml_class) {
3301 if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
3302 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3303 if (xml)
3304 goto retry;
3306 if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3307 vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3308 if (vxml)
3309 goto retry;
3311 *bp = JS_FALSE;
3312 return JS_TRUE;
3315 qn = xml->name;
3316 vqn = vxml->name;
3317 if (qn) {
3318 *bp = vqn &&
3319 EqualStrings(qn->getQNameLocalName(), vqn->getQNameLocalName()) &&
3320 EqualStrings(qn->getNameURI(), vqn->getNameURI());
3321 } else {
3322 *bp = vqn == NULL;
3324 if (!*bp)
3325 return JS_TRUE;
3327 if (JSXML_HAS_VALUE(xml)) {
3328 if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, bp))
3329 return JS_FALSE;
3330 } else if (xml->xml_kids.length != vxml->xml_kids.length) {
3331 *bp = JS_FALSE;
3332 } else {
3334 JSXMLArrayCursor cursor(&xml->xml_kids);
3335 JSXMLArrayCursor vcursor(&vxml->xml_kids);
3336 for (;;) {
3337 kid = (JSXML *) cursor.getNext();
3338 vkid = (JSXML *) vcursor.getNext();
3339 if (!kid || !vkid) {
3340 *bp = !kid && !vkid;
3341 break;
3343 xobj = js_GetXMLObject(cx, kid);
3344 vobj = js_GetXMLObject(cx, vkid);
3345 if (!xobj || !vobj ||
3346 !js_TestXMLEquality(cx, ObjectValue(*xobj), ObjectValue(*vobj), bp))
3347 return JS_FALSE;
3348 if (!*bp)
3349 break;
3353 if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3354 n = xml->xml_attrs.length;
3355 if (n != vxml->xml_attrs.length)
3356 *bp = JS_FALSE;
3357 for (i = 0; *bp && i < n; i++) {
3358 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3359 if (!attr)
3360 continue;
3361 j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3362 if (j == XML_NOT_FOUND) {
3363 *bp = JS_FALSE;
3364 break;
3366 vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3367 if (!vattr)
3368 continue;
3369 if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, bp))
3370 return JS_FALSE;
3375 return JS_TRUE;
3378 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3379 static JSBool
3380 Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3382 JSObject *vobj;
3383 JSXML *vxml;
3385 if (JSVAL_IS_PRIMITIVE(v)) {
3386 *bp = JS_FALSE;
3387 if (xml->xml_class == JSXML_CLASS_LIST) {
3388 if (xml->xml_kids.length == 1) {
3389 vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3390 if (!vxml)
3391 return JS_TRUE;
3392 vobj = js_GetXMLObject(cx, vxml);
3393 if (!vobj)
3394 return JS_FALSE;
3395 return js_TestXMLEquality(cx, ObjectValue(*vobj), Valueify(v), bp);
3397 if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3398 *bp = JS_TRUE;
3400 } else {
3401 vobj = JSVAL_TO_OBJECT(v);
3402 if (!vobj->isXML()) {
3403 *bp = JS_FALSE;
3404 } else {
3405 vxml = (JSXML *) vobj->getPrivate();
3406 if (!XMLEquals(cx, xml, vxml, bp))
3407 return JS_FALSE;
3410 return JS_TRUE;
3413 static JSBool
3414 CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3416 JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3418 do {
3419 if (xml == kid) {
3420 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3421 JSMSG_CYCLIC_VALUE, js_XML_str);
3422 return JS_FALSE;
3424 } while ((xml = xml->parent) != NULL);
3426 return JS_TRUE;
3429 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3430 static JSBool
3431 Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3433 uint32 j, n;
3434 JSXML *vxml, *kid;
3435 JSObject *vobj;
3436 JSString *str;
3438 if (!JSXML_HAS_KIDS(xml))
3439 return JS_TRUE;
3441 n = 1;
3442 vxml = NULL;
3443 if (!JSVAL_IS_PRIMITIVE(v)) {
3444 vobj = JSVAL_TO_OBJECT(v);
3445 if (vobj->isXML()) {
3446 vxml = (JSXML *) vobj->getPrivate();
3447 if (vxml->xml_class == JSXML_CLASS_LIST) {
3448 n = vxml->xml_kids.length;
3449 if (n == 0)
3450 return JS_TRUE;
3451 for (j = 0; j < n; j++) {
3452 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3453 if (!kid)
3454 continue;
3455 if (!CheckCycle(cx, xml, kid))
3456 return JS_FALSE;
3458 } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3459 /* OPTION: enforce that descendants have superset namespaces. */
3460 if (!CheckCycle(cx, xml, vxml))
3461 return JS_FALSE;
3465 if (!vxml) {
3466 str = js_ValueToString(cx, Valueify(v));
3467 if (!str)
3468 return JS_FALSE;
3470 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3471 if (!vxml)
3472 return JS_FALSE;
3473 vxml->xml_value = str;
3476 if (i > xml->xml_kids.length)
3477 i = xml->xml_kids.length;
3479 if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3480 return JS_FALSE;
3482 if (vxml->xml_class == JSXML_CLASS_LIST) {
3483 for (j = 0; j < n; j++) {
3484 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3485 if (!kid)
3486 continue;
3487 kid->parent = xml;
3488 XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3490 /* OPTION: enforce that descendants have superset namespaces. */
3492 } else {
3493 vxml->parent = xml;
3494 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3496 return JS_TRUE;
3499 static JSBool
3500 IndexToId(JSContext *cx, uint32 index, jsid *idp)
3502 JSAtom *atom;
3503 JSString *str;
3505 if (index <= JSID_INT_MAX) {
3506 *idp = INT_TO_JSID(index);
3507 } else {
3508 str = js_NumberToString(cx, (jsdouble) index);
3509 if (!str)
3510 return JS_FALSE;
3511 atom = js_AtomizeString(cx, str, 0);
3512 if (!atom)
3513 return JS_FALSE;
3514 *idp = ATOM_TO_JSID(atom);
3516 return JS_TRUE;
3519 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3520 static JSBool
3521 Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3523 uint32 n;
3524 JSXML *vxml, *kid;
3525 JSObject *vobj;
3526 JSString *str;
3528 if (!JSXML_HAS_KIDS(xml))
3529 return JS_TRUE;
3532 * 9.1.1.12
3533 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3534 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3536 n = xml->xml_kids.length;
3537 if (i > n)
3538 i = n;
3540 vxml = NULL;
3541 if (!JSVAL_IS_PRIMITIVE(v)) {
3542 vobj = JSVAL_TO_OBJECT(v);
3543 if (vobj->isXML())
3544 vxml = (JSXML *) vobj->getPrivate();
3547 switch (vxml ? JSXMLClass(vxml->xml_class) : JSXML_CLASS_LIMIT) {
3548 case JSXML_CLASS_ELEMENT:
3549 /* OPTION: enforce that descendants have superset namespaces. */
3550 if (!CheckCycle(cx, xml, vxml))
3551 return JS_FALSE;
3552 case JSXML_CLASS_COMMENT:
3553 case JSXML_CLASS_PROCESSING_INSTRUCTION:
3554 case JSXML_CLASS_TEXT:
3555 goto do_replace;
3557 case JSXML_CLASS_LIST:
3558 if (i < n)
3559 DeleteByIndex(cx, xml, i);
3560 if (!Insert(cx, xml, i, v))
3561 return JS_FALSE;
3562 break;
3564 default:
3565 str = js_ValueToString(cx, Valueify(v));
3566 if (!str)
3567 return JS_FALSE;
3569 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3570 if (!vxml)
3571 return JS_FALSE;
3572 vxml->xml_value = str;
3574 do_replace:
3575 vxml->parent = xml;
3576 if (i < n) {
3577 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3578 if (kid)
3579 kid->parent = NULL;
3581 if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3582 return JS_FALSE;
3583 break;
3586 return JS_TRUE;
3589 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3590 static void
3591 DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn,
3592 JSBool attributes)
3594 JSXMLArray *array;
3595 uint32 index, deleteCount;
3596 JSXML *kid;
3597 JSXMLNameMatcher matcher;
3599 if (xml->xml_class == JSXML_CLASS_LIST) {
3600 array = &xml->xml_kids;
3601 for (index = 0; index < array->length; index++) {
3602 kid = XMLARRAY_MEMBER(array, index, JSXML);
3603 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT)
3604 DeleteNamedProperty(cx, kid, nameqn, attributes);
3606 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3607 if (attributes) {
3608 array = &xml->xml_attrs;
3609 matcher = MatchAttrName;
3610 } else {
3611 array = &xml->xml_kids;
3612 matcher = MatchElemName;
3614 deleteCount = 0;
3615 for (index = 0; index < array->length; index++) {
3616 kid = XMLARRAY_MEMBER(array, index, JSXML);
3617 if (kid && matcher(nameqn, kid)) {
3618 kid->parent = NULL;
3619 XMLArrayDelete(cx, array, index, JS_FALSE);
3620 ++deleteCount;
3621 } else if (deleteCount != 0) {
3622 XMLARRAY_SET_MEMBER(array,
3623 index - deleteCount,
3624 array->vector[index]);
3627 array->length -= deleteCount;
3631 /* ECMA-357 9.2.1.3 index case. */
3632 static void
3633 DeleteListElement(JSContext *cx, JSXML *xml, uint32 index)
3635 JSXML *kid, *parent;
3636 uint32 kidIndex;
3638 JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST);
3640 if (index < xml->xml_kids.length) {
3641 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3642 if (kid) {
3643 parent = kid->parent;
3644 if (parent) {
3645 JS_ASSERT(parent != xml);
3646 JS_ASSERT(JSXML_HAS_KIDS(parent));
3648 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
3649 DeleteNamedProperty(cx, parent, kid->name, JS_TRUE);
3650 } else {
3651 kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
3652 NULL);
3653 JS_ASSERT(kidIndex != XML_NOT_FOUND);
3654 DeleteByIndex(cx, parent, kidIndex);
3657 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3662 static JSBool
3663 SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
3665 JSXMLArray *nsarray;
3666 uint32 i, n;
3667 JSObject *ns;
3669 nsarray = &xml->xml_namespaces;
3670 while ((xml = xml->parent) != NULL) {
3671 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3672 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3673 if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
3674 if (!XMLARRAY_APPEND(cx, nsarray, ns))
3675 return JS_FALSE;
3679 return JS_TRUE;
3682 static JSBool
3683 GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list)
3685 JSXMLArray *array;
3686 JSXMLNameMatcher matcher;
3687 JSBool attrs;
3689 if (xml->xml_class == JSXML_CLASS_LIST) {
3690 JSXMLArrayCursor cursor(&xml->xml_kids);
3691 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3692 if (kid->xml_class == JSXML_CLASS_ELEMENT &&
3693 !GetNamedProperty(cx, kid, nameqn, list)) {
3694 return JS_FALSE;
3697 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3698 attrs = (nameqn->getClass() == &js_AttributeNameClass);
3699 if (attrs) {
3700 array = &xml->xml_attrs;
3701 matcher = MatchAttrName;
3702 } else {
3703 array = &xml->xml_kids;
3704 matcher = MatchElemName;
3707 JSXMLArrayCursor cursor(array);
3708 while (JSXML *kid = (JSXML *) cursor.getNext()) {
3709 if (matcher(nameqn, kid)) {
3710 if (!attrs &&
3711 kid->xml_class == JSXML_CLASS_ELEMENT &&
3712 !SyncInScopeNamespaces(cx, kid)) {
3713 return JS_FALSE;
3715 if (!Append(cx, list, kid))
3716 return JS_FALSE;
3721 return JS_TRUE;
3724 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3725 static JSBool
3726 GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3728 JSXML *xml, *list, *kid;
3729 uint32 index;
3730 JSObject *kidobj, *listobj;
3731 JSObject *nameqn;
3732 jsid funid;
3734 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3735 if (!xml)
3736 return true;
3738 if (js_IdIsIndex(id, &index)) {
3739 if (!JSXML_HAS_KIDS(xml)) {
3740 *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
3741 } else {
3743 * ECMA-357 9.2.1.1 starts here.
3745 * Erratum: 9.2 is not completely clear that indexed properties
3746 * correspond to kids, but that's what it seems to say, and it's
3747 * what any sane user would want.
3749 if (index < xml->xml_kids.length) {
3750 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3751 if (!kid) {
3752 *vp = JSVAL_VOID;
3753 return true;
3755 kidobj = js_GetXMLObject(cx, kid);
3756 if (!kidobj)
3757 return false;
3759 *vp = OBJECT_TO_JSVAL(kidobj);
3760 } else {
3761 *vp = JSVAL_VOID;
3764 return true;
3768 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3770 nameqn = ToXMLName(cx, IdToJsval(id), &funid);
3771 if (!nameqn)
3772 return false;
3773 if (!JSID_IS_VOID(funid))
3774 return GetXMLFunction(cx, obj, funid, vp);
3776 jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL };
3777 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
3779 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3780 if (!listobj)
3781 return false;
3783 roots[1] = OBJECT_TO_JSVAL(listobj);
3785 list = (JSXML *) listobj->getPrivate();
3786 if (!GetNamedProperty(cx, xml, nameqn, list))
3787 return false;
3790 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
3791 * given list's [[TargetProperty]] to the property that is being
3792 * appended. This means that any use of the internal [[Get]]
3793 * property returns a list which, when used by e.g. [[Insert]]
3794 * duplicates the last element matched by id. See bug 336921.
3796 list->xml_target = xml;
3797 list->xml_targetprop = nameqn;
3798 *vp = OBJECT_TO_JSVAL(listobj);
3799 return true;
3802 static JSXML *
3803 CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
3805 JS_ASSERT(xml->object != obj);
3807 xml = DeepCopy(cx, xml, obj, 0);
3808 if (!xml)
3809 return NULL;
3811 JS_ASSERT(xml->object == obj);
3812 return xml;
3815 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
3816 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
3818 static JSString *
3819 KidToString(JSContext *cx, JSXML *xml, uint32 index)
3821 JSXML *kid;
3822 JSObject *kidobj;
3824 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3825 if (!kid)
3826 return cx->runtime->emptyString;
3827 kidobj = js_GetXMLObject(cx, kid);
3828 if (!kidobj)
3829 return NULL;
3830 return js_ValueToString(cx, ObjectValue(*kidobj));
3833 /* Forward declared -- its implementation uses other statics that call it. */
3834 static JSBool
3835 ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
3837 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
3838 static JSBool
3839 PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3841 JSBool ok, primitiveAssign;
3842 enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
3843 JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
3844 JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
3845 JSObject *targetprop, *nameqn, *attrqn;
3846 uint32 index, i, j, k, n, q, matchIndex;
3847 jsval attrval, nsval;
3848 jsid funid;
3849 JSObject *ns;
3851 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3852 if (!xml)
3853 return JS_TRUE;
3855 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
3856 if (!xml)
3857 return JS_FALSE;
3859 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
3860 vxml = NULL;
3861 if (!JSVAL_IS_PRIMITIVE(*vp)) {
3862 vobj = JSVAL_TO_OBJECT(*vp);
3863 if (vobj->isXML())
3864 vxml = (JSXML *) vobj->getPrivate();
3867 ok = js_EnterLocalRootScope(cx);
3868 if (!ok)
3869 return JS_FALSE;
3871 MUST_FLOW_THROUGH("out");
3872 jsval roots[3];
3873 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
3874 roots[ID_ROOT] = IdToJsval(id);
3875 roots[VAL_ROOT] = *vp;
3876 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots));
3878 if (js_IdIsIndex(id, &index)) {
3879 if (xml->xml_class != JSXML_CLASS_LIST) {
3880 /* See NOTE in spec: this variation is reserved for future use. */
3881 ReportBadXMLName(cx, IdToValue(id));
3882 goto bad;
3886 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
3888 i = index;
3890 /* 2(a-b). */
3891 if (xml->xml_target) {
3892 ok = ResolveValue(cx, xml->xml_target, &rxml);
3893 if (!ok)
3894 goto out;
3895 if (!rxml)
3896 goto out;
3897 JS_ASSERT(rxml->object);
3898 } else {
3899 rxml = NULL;
3902 /* 2(c). */
3903 if (index >= xml->xml_kids.length) {
3904 /* 2(c)(i). */
3905 if (rxml) {
3906 if (rxml->xml_class == JSXML_CLASS_LIST) {
3907 if (rxml->xml_kids.length != 1)
3908 goto out;
3909 rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
3910 if (!rxml)
3911 goto out;
3912 ok = js_GetXMLObject(cx, rxml) != NULL;
3913 if (!ok)
3914 goto out;
3918 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
3919 * _y.[[Parent]] = r_ where _r_ is the result of
3920 * [[ResolveValue]] called on _x.[[TargetObject]] in
3921 * 2(a)(i). This can result in text parenting text:
3923 * var MYXML = new XML();
3924 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
3926 * (testcase from Werner Sharp <wsharp@macromedia.com>).
3928 * To match insertChildAfter, insertChildBefore,
3929 * prependChild, and setChildren, we should silently
3930 * do nothing in this case.
3932 if (!JSXML_HAS_KIDS(rxml))
3933 goto out;
3936 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
3937 targetprop = xml->xml_targetprop;
3938 if (!targetprop || IS_STAR(targetprop->getQNameLocalName())) {
3939 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
3940 kid = js_NewXML(cx, JSXML_CLASS_TEXT);
3941 if (!kid)
3942 goto bad;
3943 } else {
3944 nameobj = targetprop;
3945 if (nameobj->getClass() == &js_AttributeNameClass) {
3947 * 2(c)(iii)(1-3).
3948 * Note that rxml can't be null here, because target
3949 * and targetprop are non-null.
3951 ok = GetProperty(cx, rxml->object, id, &attrval);
3952 if (!ok)
3953 goto out;
3954 if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
3955 goto out;
3956 attrobj = JSVAL_TO_OBJECT(attrval);
3957 attr = (JSXML *) attrobj->getPrivate();
3958 if (JSXML_LENGTH(attr) != 0)
3959 goto out;
3961 kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
3962 } else {
3963 /* 2(c)(v). */
3964 kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
3966 if (!kid)
3967 goto bad;
3969 /* An important bit of 2(c)(ii). */
3970 kid->name = targetprop;
3973 /* Final important bit of 2(c)(ii). */
3974 kid->parent = rxml;
3976 /* 2(c)(vi-vii). */
3977 i = xml->xml_kids.length;
3978 if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
3980 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
3981 * y.[[Parent]] is here called kid->parent, which we know
3982 * from 2(c)(ii) is _r_, here called rxml. So let's just
3983 * test that! Erratum, the spec should be simpler here.
3985 if (rxml) {
3986 JS_ASSERT(JSXML_HAS_KIDS(rxml));
3987 n = rxml->xml_kids.length;
3988 j = n - 1;
3989 if (n != 0 && i != 0) {
3990 for (n = j, j = 0; j < n; j++) {
3991 if (rxml->xml_kids.vector[j] ==
3992 xml->xml_kids.vector[i-1]) {
3993 break;
3998 kidobj = js_GetXMLObject(cx, kid);
3999 if (!kidobj)
4000 goto bad;
4001 ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4002 if (!ok)
4003 goto out;
4007 * 2(c)(vii)(2-3).
4008 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4009 * typo for [[TargetProperty]].
4011 if (vxml) {
4012 kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4013 ? vxml->xml_targetprop
4014 : vxml->name;
4018 /* 2(c)(viii). */
4019 ok = Append(cx, xml, kid);
4020 if (!ok)
4021 goto out;
4024 /* 2(d). */
4025 if (!vxml ||
4026 vxml->xml_class == JSXML_CLASS_TEXT ||
4027 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4028 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4029 if (!ok)
4030 goto out;
4031 roots[VAL_ROOT] = *vp;
4034 /* 2(e). */
4035 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4036 if (!kid)
4037 goto out;
4038 parent = kid->parent;
4039 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4040 nameobj = kid->name;
4041 if (nameobj->getClass() != &js_AttributeNameClass) {
4042 nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(),
4043 nameobj->getQNameLocalName());
4044 if (!nameobj)
4045 goto bad;
4047 id = OBJECT_TO_JSID(nameobj);
4049 if (parent) {
4050 /* 2(e)(i). */
4051 parentobj = js_GetXMLObject(cx, parent);
4052 if (!parentobj)
4053 goto bad;
4054 ok = PutProperty(cx, parentobj, id, vp);
4055 if (!ok)
4056 goto out;
4058 /* 2(e)(ii). */
4059 ok = GetProperty(cx, parentobj, id, vp);
4060 if (!ok)
4061 goto out;
4062 attr = (JSXML *) JSVAL_TO_OBJECT(*vp)->getPrivate();
4064 /* 2(e)(iii) - the length check comes from the bug 375406. */
4065 if (attr->xml_kids.length != 0)
4066 xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
4070 /* 2(f). */
4071 else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4073 * 2(f)(i)
4075 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4076 * if we do that we never change the parent of each child in the
4077 * list. Since [[Put]] when called on an XML object deeply copies
4078 * the provided list _V_, we also do so here. Perhaps the shallow
4079 * copy was a misguided optimization?
4081 copy = DeepCopyInLRS(cx, vxml, 0);
4082 if (!copy)
4083 goto bad;
4084 copyobj = js_GetXMLObject(cx, copy);
4085 if (!copyobj)
4086 goto bad;
4088 JS_ASSERT(parent != xml);
4089 if (parent) {
4090 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4091 JS_ASSERT(q != XML_NOT_FOUND);
4092 ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj));
4093 if (!ok)
4094 goto out;
4096 #ifdef DEBUG
4097 /* Erratum: this loop in the spec is useless. */
4098 for (j = 0, n = copy->xml_kids.length; j < n; j++) {
4099 kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
4100 JS_ASSERT(XMLARRAY_MEMBER(&copy->xml_kids, j, JSXML)
4101 == kid2);
4103 #endif
4107 * 2(f)(iv-vi).
4108 * Erratum: notice the unhandled zero-length V basis case and
4109 * the off-by-one errors for the n != 0 cases in the spec.
4111 n = copy->xml_kids.length;
4112 if (n == 0) {
4113 XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4114 } else {
4115 ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4116 if (!ok)
4117 goto out;
4119 for (j = 0; j < n; j++)
4120 xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4124 /* 2(g). */
4125 else if (vxml || JSXML_HAS_VALUE(kid)) {
4126 if (parent) {
4127 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4128 JS_ASSERT(q != XML_NOT_FOUND);
4129 ok = Replace(cx, parent, q, *vp);
4130 if (!ok)
4131 goto out;
4133 vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4134 if (!vxml)
4135 goto out;
4136 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
4140 * 2(g)(iii).
4141 * Erratum: _V_ may not be of type XML, but all index-named
4142 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4143 * according to 9.2.1.1 Overview and other places in the spec.
4145 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4146 * or an XML/XMLList object. If *vp is a string, call ToXML
4147 * on it to satisfy the constraint.
4149 if (!vxml) {
4150 JS_ASSERT(JSVAL_IS_STRING(*vp));
4151 vobj = ToXML(cx, *vp);
4152 if (!vobj)
4153 goto bad;
4154 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4155 vxml = (JSXML *) vobj->getPrivate();
4157 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4160 /* 2(h). */
4161 else {
4162 kidobj = js_GetXMLObject(cx, kid);
4163 if (!kidobj)
4164 goto bad;
4165 id = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
4166 ok = PutProperty(cx, kidobj, id, vp);
4167 if (!ok)
4168 goto out;
4170 } else {
4172 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4174 nameqn = ToXMLName(cx, IdToJsval(id), &funid);
4175 if (!nameqn)
4176 goto bad;
4177 if (!JSID_IS_VOID(funid)) {
4178 ok = js_SetProperty(cx, obj, funid, Valueify(vp), false);
4179 goto out;
4181 nameobj = nameqn;
4182 roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
4184 if (xml->xml_class == JSXML_CLASS_LIST) {
4186 * Step 3 of 9.2.1.2.
4187 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4188 * or an r with r.[[Length]] != 1, throw TypeError.
4190 n = JSXML_LENGTH(xml);
4191 if (n > 1)
4192 goto type_error;
4193 if (n == 0) {
4194 ok = ResolveValue(cx, xml, &rxml);
4195 if (!ok)
4196 goto out;
4197 if (!rxml || JSXML_LENGTH(rxml) != 1)
4198 goto type_error;
4199 ok = Append(cx, xml, rxml);
4200 if (!ok)
4201 goto out;
4203 JS_ASSERT(JSXML_LENGTH(xml) == 1);
4204 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4205 if (!xml)
4206 goto out;
4207 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
4208 obj = js_GetXMLObject(cx, xml);
4209 if (!obj)
4210 goto bad;
4211 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4213 /* FALL THROUGH to non-list case */
4217 * ECMA-357 9.1.1.2.
4218 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4219 * effort in ToString or [[DeepCopy]].
4222 if (JSXML_HAS_VALUE(xml))
4223 goto out;
4225 if (!vxml ||
4226 vxml->xml_class == JSXML_CLASS_TEXT ||
4227 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4228 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4229 if (!ok)
4230 goto out;
4231 } else {
4232 rxml = DeepCopyInLRS(cx, vxml, 0);
4233 if (!rxml || !js_GetXMLObject(cx, rxml))
4234 goto bad;
4235 vxml = rxml;
4236 *vp = OBJECT_TO_JSVAL(vxml->object);
4238 roots[VAL_ROOT] = *vp;
4241 * 6.
4242 * Erratum: why is this done here, so early? use is way later....
4244 ok = js_GetDefaultXMLNamespace(cx, &nsval);
4245 if (!ok)
4246 goto out;
4248 if (nameobj->getClass() == &js_AttributeNameClass) {
4249 /* 7(a). */
4250 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4251 goto out;
4253 /* 7(b-c). */
4254 if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4255 n = vxml->xml_kids.length;
4256 if (n == 0) {
4257 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4258 } else {
4259 JSString *left = KidToString(cx, vxml, 0);
4260 if (!left)
4261 goto bad;
4263 JSString *space = cx->runtime->atomState.spaceAtom;
4264 for (i = 1; i < n; i++) {
4265 left = js_ConcatStrings(cx, left, space);
4266 if (!left)
4267 goto bad;
4268 JSString *right = KidToString(cx, vxml, i);
4269 if (!right)
4270 goto bad;
4271 left = js_ConcatStrings(cx, left, right);
4272 if (!left)
4273 goto bad;
4276 roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4278 } else {
4279 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4280 if (!ok)
4281 goto out;
4282 roots[VAL_ROOT] = *vp;
4285 /* 7(d-e). */
4286 match = NULL;
4287 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4288 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4289 if (!attr)
4290 continue;
4291 attrqn = attr->name;
4292 if (EqualStrings(attrqn->getQNameLocalName(), nameqn->getQNameLocalName())) {
4293 JSLinearString *uri = nameqn->getNameURI();
4294 if (!uri || EqualStrings(attrqn->getNameURI(), uri)) {
4295 if (!match) {
4296 match = attr;
4297 } else {
4298 DeleteNamedProperty(cx, xml, attrqn, JS_TRUE);
4299 --i;
4305 /* 7(f). */
4306 attr = match;
4307 if (!attr) {
4308 /* 7(f)(i-ii). */
4309 JSLinearString *uri = nameqn->getNameURI();
4310 JSLinearString *left, *right;
4311 if (!uri) {
4312 left = right = cx->runtime->emptyString;
4313 } else {
4314 left = uri;
4315 right = nameqn->getNamePrefix();
4317 nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4318 if (!nameqn)
4319 goto bad;
4321 /* 7(f)(iii). */
4322 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4323 if (!attr)
4324 goto bad;
4325 attr->parent = xml;
4326 attr->name = nameqn;
4328 /* 7(f)(iv). */
4329 ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4330 if (!ok)
4331 goto out;
4333 /* 7(f)(v-vi). */
4334 ns = GetNamespace(cx, nameqn, NULL);
4335 if (!ns)
4336 goto bad;
4337 ok = AddInScopeNamespace(cx, xml, ns);
4338 if (!ok)
4339 goto out;
4342 /* 7(g). */
4343 attr->xml_value = JSVAL_TO_STRING(*vp);
4344 goto out;
4347 /* 8-9. */
4348 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4349 !IS_STAR(nameqn->getQNameLocalName())) {
4350 goto out;
4353 /* 10-11. */
4354 id = JSID_VOID;
4355 primitiveAssign = !vxml && !IS_STAR(nameqn->getQNameLocalName());
4357 /* 12. */
4358 k = n = xml->xml_kids.length;
4359 matchIndex = XML_NOT_FOUND;
4360 kid2 = NULL;
4361 while (k != 0) {
4362 --k;
4363 kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
4364 if (kid && MatchElemName(nameqn, kid)) {
4365 if (matchIndex != XML_NOT_FOUND)
4366 DeleteByIndex(cx, xml, matchIndex);
4367 matchIndex = k;
4368 kid2 = kid;
4373 * Erratum: ECMA-357 specified child insertion inconsistently:
4374 * insertChildBefore and insertChildAfter insert an arbitrary XML
4375 * instance, and therefore can create cycles, but appendChild as
4376 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4377 * its argument. But the "Semantics" in 13.4.4.3 do not include
4378 * any [[DeepCopy]] call.
4380 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4381 * required adding cycle detection, and allowing duplicate kids to
4382 * be created (see comment 6 in the bug). Allowing duplicate kid
4383 * references means the loop above will delete all but the lowest
4384 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4385 * parent. Thus the need to restore parent here. This is covered
4386 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4388 if (kid2) {
4389 JS_ASSERT(kid2->parent == xml || !kid2->parent);
4390 if (!kid2->parent)
4391 kid2->parent = xml;
4394 /* 13. */
4395 if (matchIndex == XML_NOT_FOUND) {
4396 /* 13(a). */
4397 matchIndex = n;
4399 /* 13(b). */
4400 if (primitiveAssign) {
4401 JSLinearString *uri = nameqn->getNameURI();
4402 JSLinearString *left, *right;
4403 if (!uri) {
4404 ns = JSVAL_TO_OBJECT(nsval);
4405 left = ns->getNameURI();
4406 right = ns->getNamePrefix();
4407 } else {
4408 left = uri;
4409 right = nameqn->getNamePrefix();
4411 nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4412 if (!nameqn)
4413 goto bad;
4415 /* 13(b)(iii). */
4416 vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4417 if (!vobj)
4418 goto bad;
4419 vxml = (JSXML *) vobj->getPrivate();
4420 vxml->parent = xml;
4421 vxml->name = nameqn;
4423 /* 13(b)(iv-vi). */
4424 ns = GetNamespace(cx, nameqn, NULL);
4425 if (!ns)
4426 goto bad;
4427 ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj));
4428 if (!ok)
4429 goto out;
4430 ok = AddInScopeNamespace(cx, vxml, ns);
4431 if (!ok)
4432 goto out;
4436 /* 14. */
4437 if (primitiveAssign) {
4438 JSXMLArrayCursor cursor(&xml->xml_kids);
4439 cursor.index = matchIndex;
4440 kid = (JSXML *) cursor.getCurrent();
4441 if (JSXML_HAS_KIDS(kid)) {
4442 kid->xml_kids.finish(cx);
4443 kid->xml_kids.init();
4444 ok = kid->xml_kids.setCapacity(cx, 1);
4447 /* 14(b-c). */
4448 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4449 if (ok) {
4450 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4451 if (ok && !JSVAL_TO_STRING(*vp)->empty()) {
4452 roots[VAL_ROOT] = *vp;
4453 if ((JSXML *) cursor.getCurrent() == kid)
4454 ok = Replace(cx, kid, 0, *vp);
4457 } else {
4458 /* 15(a). */
4459 ok = Replace(cx, xml, matchIndex, *vp);
4463 out:
4464 js_LeaveLocalRootScope(cx);
4465 return ok;
4467 type_error:
4469 JSAutoByteString bytes;
4470 if (js_ValueToPrintable(cx, IdToValue(id), &bytes))
4471 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XMLLIST_PUT, bytes.ptr());
4473 bad:
4474 ok = JS_FALSE;
4475 goto out;
4478 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4479 static JSBool
4480 ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4482 JSXML *target, *base;
4483 JSObject *targetprop;
4484 jsid id;
4485 jsval tv;
4487 if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4488 if (!js_GetXMLObject(cx, list))
4489 return JS_FALSE;
4490 *result = list;
4491 return JS_TRUE;
4494 target = list->xml_target;
4495 targetprop = list->xml_targetprop;
4496 if (!target || !targetprop || IS_STAR(targetprop->getQNameLocalName())) {
4497 *result = NULL;
4498 return JS_TRUE;
4501 if (targetprop->getClass() == &js_AttributeNameClass) {
4502 *result = NULL;
4503 return JS_TRUE;
4506 if (!ResolveValue(cx, target, &base))
4507 return JS_FALSE;
4508 if (!base) {
4509 *result = NULL;
4510 return JS_TRUE;
4512 if (!js_GetXMLObject(cx, base))
4513 return JS_FALSE;
4515 id = OBJECT_TO_JSID(targetprop);
4516 if (!GetProperty(cx, base->object, id, &tv))
4517 return JS_FALSE;
4518 target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4520 if (JSXML_LENGTH(target) == 0) {
4521 if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4522 *result = NULL;
4523 return JS_TRUE;
4525 tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4526 if (!PutProperty(cx, base->object, id, &tv))
4527 return JS_FALSE;
4528 if (!GetProperty(cx, base->object, id, &tv))
4529 return JS_FALSE;
4530 target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4533 *result = target;
4534 return JS_TRUE;
4537 static JSBool
4538 HasNamedProperty(JSXML *xml, JSObject *nameqn)
4540 JSBool found;
4541 JSXMLArray *array;
4542 JSXMLNameMatcher matcher;
4543 uint32 i, n;
4545 if (xml->xml_class == JSXML_CLASS_LIST) {
4546 found = JS_FALSE;
4547 JSXMLArrayCursor cursor(&xml->xml_kids);
4548 while (JSXML *kid = (JSXML *) cursor.getNext()) {
4549 found = HasNamedProperty(kid, nameqn);
4550 if (found)
4551 break;
4553 return found;
4556 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4557 if (nameqn->getClass() == &js_AttributeNameClass) {
4558 array = &xml->xml_attrs;
4559 matcher = MatchAttrName;
4560 } else {
4561 array = &xml->xml_kids;
4562 matcher = MatchElemName;
4564 for (i = 0, n = array->length; i < n; i++) {
4565 JSXML *kid = XMLARRAY_MEMBER(array, i, JSXML);
4566 if (kid && matcher(nameqn, kid))
4567 return JS_TRUE;
4571 return JS_FALSE;
4574 static JSBool
4575 HasIndexedProperty(JSXML *xml, uint32 i)
4577 if (xml->xml_class == JSXML_CLASS_LIST)
4578 return i < JSXML_LENGTH(xml);
4580 if (xml->xml_class == JSXML_CLASS_ELEMENT)
4581 return i == 0;
4583 return JS_FALSE;
4586 static JSBool
4587 HasSimpleContent(JSXML *xml);
4589 static JSBool
4590 HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
4592 JSObject *pobj;
4593 JSProperty *prop;
4594 JSXML *xml;
4596 JS_ASSERT(obj->getClass() == &js_XMLClass);
4598 if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
4599 return false;
4600 if (!prop) {
4601 xml = (JSXML *) obj->getPrivate();
4602 if (HasSimpleContent(xml)) {
4603 AutoObjectRooter tvr(cx);
4606 * Search in String.prototype to set found whenever
4607 * GetXMLFunction returns existing function.
4609 if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
4610 return false;
4612 JS_ASSERT(tvr.object());
4613 if (!js_LookupProperty(cx, tvr.object(), funid, &pobj, &prop))
4614 return false;
4617 *found = (prop != NULL);
4618 return true;
4621 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4622 static JSBool
4623 HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found)
4625 JSXML *xml;
4626 bool isIndex;
4627 uint32 i;
4628 JSObject *qn;
4629 jsid funid;
4631 xml = (JSXML *) obj->getPrivate();
4632 if (!js_IdValIsIndex(cx, id, &i, &isIndex))
4633 return JS_FALSE;
4635 if (isIndex) {
4636 *found = HasIndexedProperty(xml, i);
4637 } else {
4638 qn = ToXMLName(cx, id, &funid);
4639 if (!qn)
4640 return JS_FALSE;
4641 if (!JSID_IS_VOID(funid)) {
4642 if (!HasFunctionProperty(cx, obj, funid, found))
4643 return JS_FALSE;
4644 } else {
4645 *found = HasNamedProperty(xml, qn);
4648 return JS_TRUE;
4651 static void
4652 xml_finalize(JSContext *cx, JSObject *obj)
4654 JSXML *xml = (JSXML *) obj->getPrivate();
4655 if (!xml)
4656 return;
4657 if (xml->object == obj)
4658 xml->object = NULL;
4661 static void
4662 xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
4664 uint32 i;
4665 JSXML *xml;
4667 for (i = 0; i < len; i++) {
4668 xml = vec[i];
4669 if (xml) {
4670 JS_SET_TRACING_INDEX(trc, "xml_vector", i);
4671 Mark(trc, xml);
4677 * XML objects are native. Thus xml_lookupProperty must return a valid
4678 * Shape pointer parameter via *propp to signify "property found". Since the
4679 * only call to xml_lookupProperty is via JSObject::lookupProperty, and then
4680 * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
4681 * JSOP_IN case in the interpreter, the only time we add a Shape here is when
4682 * an unqualified name is being accessed or when "name in xml" is called.
4684 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4685 * giving it an shape with (getter, setter) == (GetProperty, PutProperty).
4687 * NB: xml_deleteProperty must take care to remove any property added here.
4689 * FIXME This clashes with the function namespace implementation which also
4690 * uses native properties. Effectively after xml_lookupProperty any property
4691 * stored previously using assignments to xml.function::name will be removed.
4692 * We partially workaround the problem in GetXMLFunction. There we take
4693 * advantage of the fact that typically function:: is used to access the
4694 * functions from XML.prototype. So when js_GetProperty returns a non-function
4695 * property, we assume that it represents the result of GetProperty setter
4696 * hiding the function and use an extra prototype chain lookup to recover it.
4697 * For a proper solution see bug 355257.
4699 static JSBool
4700 xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4701 JSProperty **propp)
4703 JSBool found;
4704 JSXML *xml;
4705 uint32 i;
4706 JSObject *qn;
4707 jsid funid;
4709 xml = (JSXML *) obj->getPrivate();
4710 if (js_IdIsIndex(id, &i)) {
4711 found = HasIndexedProperty(xml, i);
4712 } else {
4713 qn = ToXMLName(cx, IdToJsval(id), &funid);
4714 if (!qn)
4715 return JS_FALSE;
4716 if (!JSID_IS_VOID(funid))
4717 return js_LookupProperty(cx, obj, funid, objp, propp);
4718 found = HasNamedProperty(xml, qn);
4720 if (!found) {
4721 *objp = NULL;
4722 *propp = NULL;
4723 } else {
4724 const Shape *shape =
4725 js_AddNativeProperty(cx, obj, id,
4726 Valueify(GetProperty), Valueify(PutProperty),
4727 SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
4728 0, 0);
4729 if (!shape)
4730 return JS_FALSE;
4732 *objp = obj;
4733 *propp = (JSProperty *) shape;
4735 return JS_TRUE;
4738 static JSBool
4739 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
4740 PropertyOp getter, PropertyOp setter, uintN attrs)
4742 if (IsFunctionObject(*v) || getter || setter ||
4743 (attrs & JSPROP_ENUMERATE) == 0 ||
4744 (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
4745 return js_DefineProperty(cx, obj, id, v, getter, setter, attrs);
4748 jsval tmp = Jsvalify(*v);
4749 return PutProperty(cx, obj, id, &tmp);
4752 static JSBool
4753 xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
4755 if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) {
4756 vp->setUndefined();
4757 return JS_TRUE;
4760 return GetProperty(cx, obj, id, Jsvalify(vp));
4763 static JSBool
4764 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
4766 return PutProperty(cx, obj, id, Jsvalify(vp));
4769 static JSBool
4770 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
4772 JSBool found;
4773 if (!HasProperty(cx, obj, IdToJsval(id), &found))
4774 return false;
4776 *attrsp = found ? JSPROP_ENUMERATE : 0;
4777 return JS_TRUE;
4780 static JSBool
4781 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
4783 JSBool found;
4784 if (!HasProperty(cx, obj, IdToJsval(id), &found))
4785 return false;
4787 if (found) {
4788 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4789 JSMSG_CANT_SET_XML_ATTRS);
4790 return false;
4792 return true;
4795 static JSBool
4796 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
4798 JSXML *xml;
4799 jsval idval;
4800 uint32 index;
4801 JSObject *nameqn;
4802 jsid funid;
4804 idval = IdToJsval(id);
4805 xml = (JSXML *) obj->getPrivate();
4806 if (js_IdIsIndex(id, &index)) {
4807 if (xml->xml_class != JSXML_CLASS_LIST) {
4808 /* See NOTE in spec: this variation is reserved for future use. */
4809 ReportBadXMLName(cx, IdToValue(id));
4810 return false;
4813 /* ECMA-357 9.2.1.3. */
4814 DeleteListElement(cx, xml, index);
4815 } else {
4816 nameqn = ToXMLName(cx, idval, &funid);
4817 if (!nameqn)
4818 return false;
4819 if (!JSID_IS_VOID(funid))
4820 return js_DeleteProperty(cx, obj, funid, rval, false);
4822 DeleteNamedProperty(cx, xml, nameqn,
4823 nameqn->getClass() == &js_AttributeNameClass);
4827 * If this object has its own (mutable) scope, then we may have added a
4828 * property to the scope in xml_lookupProperty for it to return to mean
4829 * "found" and to provide a handle for access operations to call the
4830 * property's getter or setter. But now it's time to remove any such
4831 * property, to purge the property cache and remove the scope entry.
4833 if (!obj->nativeEmpty() && !js_DeleteProperty(cx, obj, id, rval, false))
4834 return false;
4836 rval->setBoolean(true);
4837 return true;
4840 JSBool
4841 xml_convert(JSContext *cx, JSObject *obj, JSType type, Value *rval)
4843 return js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, rval);
4846 static JSBool
4847 xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
4849 JSXML *xml;
4850 uint32 length, index;
4851 JSXMLArrayCursor *cursor;
4853 xml = (JSXML *)obj->getPrivate();
4854 length = JSXML_LENGTH(xml);
4856 switch (enum_op) {
4857 case JSENUMERATE_INIT:
4858 case JSENUMERATE_INIT_ALL:
4859 if (length == 0) {
4860 statep->setInt32(0);
4861 } else {
4862 cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
4863 if (!cursor)
4864 return JS_FALSE;
4865 statep->setPrivate(cursor);
4867 if (idp)
4868 *idp = INT_TO_JSID(length);
4869 break;
4871 case JSENUMERATE_NEXT:
4872 if (statep->isInt32(0)) {
4873 statep->setNull();
4874 break;
4876 cursor = (JSXMLArrayCursor *) statep->toPrivate();
4877 if (cursor && cursor->array && (index = cursor->index) < length) {
4878 *idp = INT_TO_JSID(index);
4879 cursor->index = index + 1;
4880 break;
4882 /* FALL THROUGH */
4884 case JSENUMERATE_DESTROY:
4885 if (!statep->isInt32(0)) {
4886 cursor = (JSXMLArrayCursor *) statep->toPrivate();
4887 if (cursor)
4888 cx->destroy(cursor);
4890 statep->setNull();
4891 break;
4893 return JS_TRUE;
4896 static JSType
4897 xml_typeOf(JSContext *cx, JSObject *obj)
4899 return JSTYPE_XML;
4902 static JSBool
4903 xml_hasInstance(JSContext *cx, JSObject *obj, const Value *, JSBool *bp)
4905 return JS_TRUE;
4908 static void
4909 xml_trace(JSTracer *trc, JSObject *obj)
4911 JSXML *xml = (JSXML *) obj->getPrivate();
4912 if (xml)
4913 JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private");
4916 static JSBool
4917 xml_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
4919 JS_ASSERT(obj->isExtensible());
4920 *success = false;
4921 return true;
4924 static void
4925 xml_clear(JSContext *cx, JSObject *obj)
4929 static JSBool
4930 HasSimpleContent(JSXML *xml)
4932 JSXML *kid;
4933 JSBool simple;
4934 uint32 i, n;
4936 again:
4937 switch (xml->xml_class) {
4938 case JSXML_CLASS_COMMENT:
4939 case JSXML_CLASS_PROCESSING_INSTRUCTION:
4940 return JS_FALSE;
4941 case JSXML_CLASS_LIST:
4942 if (xml->xml_kids.length == 0)
4943 return JS_TRUE;
4944 if (xml->xml_kids.length == 1) {
4945 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4946 if (kid) {
4947 xml = kid;
4948 goto again;
4951 /* FALL THROUGH */
4952 default:
4953 simple = JS_TRUE;
4954 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
4955 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4956 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
4957 simple = JS_FALSE;
4958 break;
4961 return simple;
4966 * 11.2.2.1 Step 3(d) onward.
4968 JSBool
4969 js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
4971 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
4973 if (JSID_IS_OBJECT(id)) {
4974 jsid funid;
4976 if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid))
4977 return JS_FALSE;
4978 if (!JSID_IS_VOID(funid))
4979 id = funid;
4983 * As our callers have a bad habit of passing a pointer to an unrooted
4984 * local value as vp, we use a proper root here.
4986 AutoValueRooter tvr(cx);
4987 JSBool ok = GetXMLFunction(cx, obj, id, Jsvalify(tvr.addr()));
4988 *vp = tvr.value();
4989 return ok;
4992 JSBool
4993 js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp)
4995 JSXML *xml, *vxml;
4996 JSObject *vobj;
4997 JSBool ok;
4998 JSString *str, *vstr;
4999 jsdouble d, d2;
5001 JSObject *obj;
5002 jsval v;
5003 if (v1.isObject() && v1.toObject().isXML()) {
5004 obj = &v1.toObject();
5005 v = Jsvalify(v2);
5006 } else {
5007 v = Jsvalify(v1);
5008 obj = &v2.toObject();
5011 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
5013 xml = (JSXML *) obj->getPrivate();
5014 vxml = NULL;
5015 if (!JSVAL_IS_PRIMITIVE(v)) {
5016 vobj = JSVAL_TO_OBJECT(v);
5017 if (vobj->isXML())
5018 vxml = (JSXML *) vobj->getPrivate();
5021 if (xml->xml_class == JSXML_CLASS_LIST) {
5022 ok = Equals(cx, xml, v, bp);
5023 } else if (vxml) {
5024 if (vxml->xml_class == JSXML_CLASS_LIST) {
5025 ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
5026 } else {
5027 if (((xml->xml_class == JSXML_CLASS_TEXT ||
5028 xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5029 HasSimpleContent(vxml)) ||
5030 ((vxml->xml_class == JSXML_CLASS_TEXT ||
5031 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5032 HasSimpleContent(xml))) {
5033 ok = js_EnterLocalRootScope(cx);
5034 if (ok) {
5035 ok = (str = js_ValueToString(cx, ObjectValue(*obj))) &&
5036 (vstr = js_ValueToString(cx, Valueify(v)));
5037 if (ok)
5038 ok = EqualStrings(cx, str, vstr, bp);
5039 js_LeaveLocalRootScope(cx);
5041 } else {
5042 ok = XMLEquals(cx, xml, vxml, bp);
5045 } else {
5046 ok = js_EnterLocalRootScope(cx);
5047 if (ok) {
5048 if (HasSimpleContent(xml)) {
5049 ok = (str = js_ValueToString(cx, ObjectValue(*obj))) &&
5050 (vstr = js_ValueToString(cx, Valueify(v)));
5051 if (ok)
5052 ok = EqualStrings(cx, str, vstr, bp);
5053 } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
5054 str = js_ValueToString(cx, ObjectValue(*obj));
5055 if (!str) {
5056 ok = JS_FALSE;
5057 } else if (JSVAL_IS_STRING(v)) {
5058 ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), bp);
5059 } else {
5060 ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5061 if (ok) {
5062 d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5063 : JSVAL_TO_DOUBLE(v);
5064 *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
5067 } else {
5068 *bp = JS_FALSE;
5070 js_LeaveLocalRootScope(cx);
5073 return ok;
5076 JSBool
5077 js_ConcatenateXML(JSContext *cx, JSObject *obj, JSObject *robj, Value *vp)
5079 JSBool ok;
5080 JSObject *listobj;
5081 JSXML *list, *lxml, *rxml;
5083 JS_ASSERT(JS_InstanceOf(cx, obj, Jsvalify(&js_XMLClass), NULL));
5084 ok = js_EnterLocalRootScope(cx);
5085 if (!ok)
5086 return JS_FALSE;
5088 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5089 if (!listobj) {
5090 ok = JS_FALSE;
5091 goto out;
5094 list = (JSXML *) listobj->getPrivate();
5095 lxml = (JSXML *) obj->getPrivate();
5096 ok = Append(cx, list, lxml);
5097 if (!ok)
5098 goto out;
5100 JS_ASSERT(robj->isXML());
5101 rxml = (JSXML *) robj->getPrivate();
5102 ok = Append(cx, list, rxml);
5103 if (!ok)
5104 goto out;
5106 vp->setObject(*listobj);
5107 out:
5108 js_LeaveLocalRootScopeWithResult(cx, *vp);
5109 return ok;
5112 JS_FRIEND_DATA(Class) js_XMLClass = {
5113 js_XML_str,
5114 JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
5115 JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
5116 PropertyStub, /* addProperty */
5117 PropertyStub, /* delProperty */
5118 PropertyStub, /* getProperty */
5119 PropertyStub, /* setProperty */
5120 EnumerateStub,
5121 ResolveStub,
5122 xml_convert,
5123 xml_finalize,
5124 NULL, /* reserved0 */
5125 NULL, /* checkAccess */
5126 NULL, /* call */
5127 NULL, /* construct */
5128 NULL, /* xdrObject */
5129 xml_hasInstance,
5130 JS_CLASS_TRACE(xml_trace),
5131 JS_NULL_CLASS_EXT,
5133 xml_lookupProperty,
5134 xml_defineProperty,
5135 xml_getProperty,
5136 xml_setProperty,
5137 xml_getAttributes,
5138 xml_setAttributes,
5139 xml_deleteProperty,
5140 xml_enumerate,
5141 xml_typeOf,
5142 NULL, /* trace */
5143 xml_fix,
5144 NULL, /* thisObject */
5145 xml_clear
5149 static JSXML *
5150 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
5152 JSXML *xml;
5153 JSFunction *fun;
5154 char numBuf[12];
5156 JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
5158 *objp = JS_THIS_OBJECT(cx, vp);
5159 xml = (JSXML *) GetInstancePrivate(cx, *objp, &js_XMLClass, Valueify(vp + 2));
5160 if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5161 return xml;
5163 if (xml->xml_kids.length == 1) {
5164 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5165 if (xml) {
5166 *objp = js_GetXMLObject(cx, xml);
5167 if (!*objp)
5168 return NULL;
5169 vp[1] = OBJECT_TO_JSVAL(*objp);
5170 return xml;
5174 fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp));
5175 JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
5176 JSAutoByteString funNameBytes;
5177 if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
5178 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_LIST_XML_METHOD,
5179 funName, numBuf);
5181 return NULL;
5184 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5185 #define XML_METHOD_PROLOG \
5186 JSObject *obj = JS_THIS_OBJECT(cx, vp); \
5187 JSXML *xml = (JSXML *)GetInstancePrivate(cx, obj, &js_XMLClass, Valueify(vp+2)); \
5188 if (!xml) \
5189 return JS_FALSE
5191 #define NON_LIST_XML_METHOD_PROLOG \
5192 JSObject *obj; \
5193 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5194 if (!xml) \
5195 return JS_FALSE; \
5196 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5198 static JSBool
5199 xml_addNamespace(JSContext *cx, uintN argc, jsval *vp)
5201 JSObject *ns;
5203 NON_LIST_XML_METHOD_PROLOG;
5204 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5205 goto done;
5206 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5207 if (!xml)
5208 return JS_FALSE;
5210 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
5211 return JS_FALSE;
5212 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5214 ns = JSVAL_TO_OBJECT(*vp);
5215 if (!AddInScopeNamespace(cx, xml, ns))
5216 return JS_FALSE;
5217 ns->setNamespaceDeclared(JSVAL_TRUE);
5219 done:
5220 *vp = OBJECT_TO_JSVAL(obj);
5221 return JS_TRUE;
5224 static JSBool
5225 xml_appendChild(JSContext *cx, uintN argc, jsval *vp)
5227 jsval v;
5228 JSObject *vobj;
5229 JSXML *vxml;
5231 NON_LIST_XML_METHOD_PROLOG;
5232 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5233 if (!xml)
5234 return JS_FALSE;
5236 jsid name;
5237 if (!js_GetAnyName(cx, &name))
5238 return JS_FALSE;
5240 if (!GetProperty(cx, obj, name, &v))
5241 return JS_FALSE;
5243 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5244 vobj = JSVAL_TO_OBJECT(v);
5245 JS_ASSERT(vobj->isXML());
5246 vxml = (JSXML *) vobj->getPrivate();
5247 JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
5249 if (!IndexToId(cx, vxml->xml_kids.length, &name))
5250 return JS_FALSE;
5251 *vp = (argc != 0) ? vp[2] : JSVAL_VOID;
5253 if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, vp))
5254 return JS_FALSE;
5256 *vp = OBJECT_TO_JSVAL(obj);
5257 return JS_TRUE;
5260 /* XML and XMLList */
5261 static JSBool
5262 xml_attribute(JSContext *cx, uintN argc, jsval *vp)
5264 JSObject *qn;
5266 if (argc == 0) {
5267 js_ReportMissingArg(cx, Valueify(*vp), 0);
5268 return JS_FALSE;
5271 qn = ToAttributeName(cx, vp[2]);
5272 if (!qn)
5273 return JS_FALSE;
5274 vp[2] = OBJECT_TO_JSVAL(qn); /* local root */
5276 jsid id = OBJECT_TO_JSID(qn);
5277 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), id, vp);
5280 /* XML and XMLList */
5281 static JSBool
5282 xml_attributes(JSContext *cx, uintN argc, jsval *vp)
5284 jsval name = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
5285 JSObject *qn = ToAttributeName(cx, name);
5286 if (!qn)
5287 return JS_FALSE;
5289 AutoObjectRooter tvr(cx, qn);
5290 jsid id = OBJECT_TO_JSID(qn);
5291 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), id, vp);
5294 static JSXML *
5295 xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5297 JSObject *listobj;
5298 JSXML *list;
5300 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5301 if (!listobj)
5302 return NULL;
5304 *rval = OBJECT_TO_JSVAL(listobj);
5305 list = (JSXML *) listobj->getPrivate();
5306 list->xml_target = xml;
5307 return list;
5310 static JSBool
5311 ValueToId(JSContext *cx, jsval v, AutoIdRooter *idr)
5313 if (JSVAL_IS_INT(v)) {
5314 jsint i = JSVAL_TO_INT(v);
5315 if (INT_FITS_IN_JSID(i))
5316 *idr->addr() = INT_TO_JSID(i);
5317 else if (!js_ValueToStringId(cx, Valueify(v), idr->addr()))
5318 return JS_FALSE;
5319 } else if (JSVAL_IS_STRING(v)) {
5320 JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
5321 if (!atom)
5322 return JS_FALSE;
5323 *idr->addr() = ATOM_TO_JSID(atom);
5324 } else if (!JSVAL_IS_PRIMITIVE(v)) {
5325 *idr->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v));
5326 } else {
5327 ReportBadXMLName(cx, Valueify(v));
5328 return JS_FALSE;
5330 return JS_TRUE;
5333 static JSBool
5334 xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5335 jsval *rval)
5337 bool isIndex;
5338 uint32 index;
5339 JSXML *kid;
5340 JSObject *kidobj;
5342 /* ECMA-357 13.4.4.6 */
5343 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5345 if (!js_IdValIsIndex(cx, name, &index, &isIndex))
5346 return JS_FALSE;
5348 if (isIndex) {
5349 if (index >= JSXML_LENGTH(xml)) {
5350 *rval = JSVAL_VOID;
5351 } else {
5352 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5353 if (!kid) {
5354 *rval = JSVAL_VOID;
5355 } else {
5356 kidobj = js_GetXMLObject(cx, kid);
5357 if (!kidobj)
5358 return JS_FALSE;
5359 *rval = OBJECT_TO_JSVAL(kidobj);
5362 return JS_TRUE;
5365 AutoIdRooter idr(cx);
5366 if (!ValueToId(cx, name, &idr))
5367 return JS_FALSE;
5369 return GetProperty(cx, obj, idr.id(), rval);
5372 /* XML and XMLList */
5373 static JSBool
5374 xml_child(JSContext *cx, uintN argc, jsval *vp)
5376 jsval v;
5377 JSXML *list, *vxml;
5378 JSObject *kidobj;
5380 XML_METHOD_PROLOG;
5381 jsval name = argc != 0 ? vp[2] : JSVAL_VOID;
5382 if (xml->xml_class == JSXML_CLASS_LIST) {
5383 /* ECMA-357 13.5.4.4 */
5384 list = xml_list_helper(cx, xml, vp);
5385 if (!list)
5386 return JS_FALSE;
5388 JSXMLArrayCursor cursor(&xml->xml_kids);
5389 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5390 kidobj = js_GetXMLObject(cx, kid);
5391 if (!kidobj)
5392 return JS_FALSE;
5393 if (!xml_child_helper(cx, kidobj, kid, name, &v))
5394 return JS_FALSE;
5395 if (JSVAL_IS_VOID(v)) {
5396 /* The property didn't exist in this kid. */
5397 continue;
5400 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5401 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5402 if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
5403 !Append(cx, list, vxml)) {
5404 return JS_FALSE;
5407 return JS_TRUE;
5410 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5411 if (!xml_child_helper(cx, obj, xml, name, vp))
5412 return JS_FALSE;
5413 if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp))
5414 return JS_FALSE;
5415 return JS_TRUE;
5418 static JSBool
5419 xml_childIndex(JSContext *cx, uintN argc, jsval *vp)
5421 JSXML *parent;
5422 uint32 i, n;
5424 NON_LIST_XML_METHOD_PROLOG;
5425 parent = xml->parent;
5426 if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
5427 *vp = DOUBLE_TO_JSVAL(js_NaN);
5428 return JS_TRUE;
5430 for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5431 if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5432 break;
5434 JS_ASSERT(i < n);
5435 if (i <= JSVAL_INT_MAX)
5436 *vp = INT_TO_JSVAL(i);
5437 else
5438 *vp = DOUBLE_TO_JSVAL(i);
5439 return JS_TRUE;
5442 /* XML and XMLList */
5443 static JSBool
5444 xml_children(JSContext *cx, uintN argc, jsval *vp)
5446 jsid name = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
5447 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
5450 /* XML and XMLList */
5451 static JSBool
5452 xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
5454 JSXML *list, *kid, *vxml;
5455 JSBool ok;
5456 uint32 i, n;
5457 JSObject *kidobj;
5458 jsval v;
5460 list = xml_list_helper(cx, xml, vp);
5461 if (!list)
5462 return JS_FALSE;
5464 ok = JS_TRUE;
5466 if (xml->xml_class == JSXML_CLASS_LIST) {
5467 /* 13.5.4.6 Step 2. */
5468 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5469 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5470 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5471 ok = js_EnterLocalRootScope(cx);
5472 if (!ok)
5473 break;
5474 kidobj = js_GetXMLObject(cx, kid);
5475 if (kidobj) {
5476 ok = xml_comments_helper(cx, kidobj, kid, &v);
5477 } else {
5478 ok = JS_FALSE;
5479 v = JSVAL_NULL;
5481 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
5482 if (!ok)
5483 break;
5484 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5485 if (JSXML_LENGTH(vxml) != 0) {
5486 ok = Append(cx, list, vxml);
5487 if (!ok)
5488 break;
5492 } else {
5493 /* 13.4.4.9 Step 2. */
5494 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5495 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5496 if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
5497 ok = Append(cx, list, kid);
5498 if (!ok)
5499 break;
5504 return ok;
5507 static JSBool
5508 xml_comments(JSContext *cx, uintN argc, jsval *vp)
5510 XML_METHOD_PROLOG;
5511 return xml_comments_helper(cx, obj, xml, vp);
5514 /* XML and XMLList */
5515 static JSBool
5516 xml_contains(JSContext *cx, uintN argc, jsval *vp)
5518 jsval value;
5519 JSBool eq;
5520 JSObject *kidobj;
5522 XML_METHOD_PROLOG;
5523 value = argc != 0 ? vp[2] : JSVAL_VOID;
5524 if (xml->xml_class == JSXML_CLASS_LIST) {
5525 eq = JS_FALSE;
5526 JSXMLArrayCursor cursor(&xml->xml_kids);
5527 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5528 kidobj = js_GetXMLObject(cx, kid);
5529 if (!kidobj || !js_TestXMLEquality(cx, ObjectValue(*kidobj), Valueify(value), &eq))
5530 return JS_FALSE;
5531 if (eq)
5532 break;
5534 } else {
5535 if (!js_TestXMLEquality(cx, ObjectValue(*obj), Valueify(value), &eq))
5536 return JS_FALSE;
5538 *vp = BOOLEAN_TO_JSVAL(eq);
5539 return JS_TRUE;
5542 /* XML and XMLList */
5543 static JSBool
5544 xml_copy(JSContext *cx, uintN argc, jsval *vp)
5546 JSXML *copy;
5548 XML_METHOD_PROLOG;
5549 copy = DeepCopy(cx, xml, NULL, 0);
5550 if (!copy)
5551 return JS_FALSE;
5552 *vp = OBJECT_TO_JSVAL(copy->object);
5553 return JS_TRUE;
5556 /* XML and XMLList */
5557 static JSBool
5558 xml_descendants(JSContext *cx, uintN argc, jsval *vp)
5560 jsval name;
5561 JSXML *list;
5563 XML_METHOD_PROLOG;
5564 name = argc == 0 ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5565 list = Descendants(cx, xml, name);
5566 if (!list)
5567 return JS_FALSE;
5568 *vp = OBJECT_TO_JSVAL(list->object);
5569 return JS_TRUE;
5572 /* XML and XMLList */
5573 static JSBool
5574 xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml,
5575 JSObject *nameqn, jsval *vp)
5577 JSXML *list, *vxml;
5578 jsval v;
5579 JSBool ok;
5580 JSObject *kidobj;
5581 uint32 i, n;
5583 list = xml_list_helper(cx, xml, vp);
5584 if (!list)
5585 return JS_FALSE;
5587 list->xml_targetprop = nameqn;
5588 ok = JS_TRUE;
5590 if (xml->xml_class == JSXML_CLASS_LIST) {
5591 /* 13.5.4.6 */
5592 JSXMLArrayCursor cursor(&xml->xml_kids);
5593 while (JSXML *kid = (JSXML *) cursor.getNext()) {
5594 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
5595 ok = js_EnterLocalRootScope(cx);
5596 if (!ok)
5597 break;
5598 kidobj = js_GetXMLObject(cx, kid);
5599 if (kidobj) {
5600 ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v);
5601 } else {
5602 ok = JS_FALSE;
5603 v = JSVAL_NULL;
5605 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
5606 if (!ok)
5607 break;
5608 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5609 if (JSXML_LENGTH(vxml) != 0) {
5610 ok = Append(cx, list, vxml);
5611 if (!ok)
5612 break;
5616 } else {
5617 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5618 JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5619 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
5620 MatchElemName(nameqn, kid)) {
5621 ok = Append(cx, list, kid);
5622 if (!ok)
5623 break;
5628 return ok;
5631 static JSBool
5632 xml_elements(JSContext *cx, uintN argc, jsval *vp)
5634 jsval name;
5635 JSObject *nameqn;
5636 jsid funid;
5638 XML_METHOD_PROLOG;
5640 name = (argc == 0) ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5641 nameqn = ToXMLName(cx, name, &funid);
5642 if (!nameqn)
5643 return JS_FALSE;
5644 vp[2] = OBJECT_TO_JSVAL(nameqn);
5646 if (!JSID_IS_VOID(funid))
5647 return xml_list_helper(cx, xml, vp) != NULL;
5649 return xml_elements_helper(cx, obj, xml, nameqn, vp);
5652 /* XML and XMLList */
5653 static JSBool
5654 xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
5656 JSObject *obj;
5657 jsval name;
5658 JSBool found;
5660 obj = JS_THIS_OBJECT(cx, vp);
5661 if (!InstanceOf(cx, obj, &js_XMLClass, Valueify(vp + 2)))
5662 return JS_FALSE;
5664 name = argc != 0 ? vp[2] : JSVAL_VOID;
5665 if (!HasProperty(cx, obj, name, &found))
5666 return JS_FALSE;
5667 if (found) {
5668 *vp = JSVAL_TRUE;
5669 return JS_TRUE;
5671 return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, Valueify(vp));
5674 /* XML and XMLList */
5675 static JSBool
5676 xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp)
5678 JSXML *kid;
5679 JSObject *kidobj;
5680 uint32 i, n;
5682 XML_METHOD_PROLOG;
5683 again:
5684 switch (xml->xml_class) {
5685 case JSXML_CLASS_ATTRIBUTE:
5686 case JSXML_CLASS_COMMENT:
5687 case JSXML_CLASS_PROCESSING_INSTRUCTION:
5688 case JSXML_CLASS_TEXT:
5689 *vp = JSVAL_FALSE;
5690 break;
5691 case JSXML_CLASS_LIST:
5692 if (xml->xml_kids.length == 0) {
5693 *vp = JSVAL_TRUE;
5694 } else if (xml->xml_kids.length == 1) {
5695 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5696 if (kid) {
5697 kidobj = js_GetXMLObject(cx, kid);
5698 if (!kidobj)
5699 return JS_FALSE;
5700 obj = kidobj;
5701 xml = (JSXML *) obj->getPrivate();
5702 goto again;
5705 /* FALL THROUGH */
5706 default:
5707 *vp = JSVAL_FALSE;
5708 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
5709 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5710 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5711 *vp = JSVAL_TRUE;
5712 break;
5715 break;
5717 return JS_TRUE;
5720 /* XML and XMLList */
5721 static JSBool
5722 xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp)
5724 XML_METHOD_PROLOG;
5725 *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
5726 return JS_TRUE;
5729 static JSBool
5730 FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
5732 uint32 length, i, j, n;
5733 JSObject *ns, *ns2;
5734 JSLinearString *prefix, *prefix2;
5736 length = nsarray->length;
5737 do {
5738 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5739 continue;
5740 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
5741 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
5742 if (!ns)
5743 continue;
5745 prefix = ns->getNamePrefix();
5746 for (j = 0; j < length; j++) {
5747 ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject);
5748 if (ns2) {
5749 prefix2 = ns2->getNamePrefix();
5750 if ((prefix2 && prefix)
5751 ? EqualStrings(prefix2, prefix)
5752 : EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
5753 break;
5758 if (j == length) {
5759 if (!XMLARRAY_APPEND(cx, nsarray, ns))
5760 return JS_FALSE;
5761 ++length;
5764 } while ((xml = xml->parent) != NULL);
5765 JS_ASSERT(length == nsarray->length);
5767 return JS_TRUE;
5771 * Populate a new JS array with elements of array and place the result into
5772 * rval. rval must point to a rooted location.
5774 static bool
5775 NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
5777 JSObject *arrayobj = NewDenseEmptyArray(cx);
5778 if (!arrayobj)
5779 return false;
5780 *rval = OBJECT_TO_JSVAL(arrayobj);
5782 AutoValueRooter tvr(cx);
5783 for (uint32 i = 0, n = array->length; i < n; i++) {
5784 JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
5785 if (!ns)
5786 continue;
5787 tvr.set(ObjectValue(*ns));
5788 if (!arrayobj->setProperty(cx, INT_TO_JSID(i), tvr.addr(), false))
5789 return false;
5791 return true;
5794 static JSBool
5795 xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp)
5797 NON_LIST_XML_METHOD_PROLOG;
5799 AutoNamespaceArray namespaces(cx);
5800 return FindInScopeNamespaces(cx, xml, &namespaces.array) &&
5801 NamespacesToJSArray(cx, &namespaces.array, vp);
5804 static JSBool
5805 xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp)
5807 jsval arg;
5808 JSXML *kid;
5809 uint32 i;
5811 NON_LIST_XML_METHOD_PROLOG;
5812 *vp = OBJECT_TO_JSVAL(obj);
5813 if (!JSXML_HAS_KIDS(xml) || argc == 0)
5814 return JS_TRUE;
5816 arg = vp[2];
5817 if (JSVAL_IS_NULL(arg)) {
5818 kid = NULL;
5819 i = 0;
5820 } else {
5821 if (!VALUE_IS_XML(arg))
5822 return JS_TRUE;
5823 kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
5824 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
5825 if (i == XML_NOT_FOUND)
5826 return JS_TRUE;
5827 ++i;
5830 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5831 if (!xml)
5832 return JS_FALSE;
5833 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
5836 static JSBool
5837 xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp)
5839 jsval arg;
5840 JSXML *kid;
5841 uint32 i;
5843 NON_LIST_XML_METHOD_PROLOG;
5844 *vp = OBJECT_TO_JSVAL(obj);
5845 if (!JSXML_HAS_KIDS(xml) || argc == 0)
5846 return JS_TRUE;
5848 arg = vp[2];
5849 if (JSVAL_IS_NULL(arg)) {
5850 kid = NULL;
5851 i = xml->xml_kids.length;
5852 } else {
5853 if (!VALUE_IS_XML(arg))
5854 return JS_TRUE;
5855 kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
5856 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
5857 if (i == XML_NOT_FOUND)
5858 return JS_TRUE;
5861 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5862 if (!xml)
5863 return JS_FALSE;
5864 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
5867 /* XML and XMLList */
5868 static JSBool
5869 xml_length(JSContext *cx, uintN argc, jsval *vp)
5871 XML_METHOD_PROLOG;
5872 if (xml->xml_class != JSXML_CLASS_LIST) {
5873 *vp = JSVAL_ONE;
5874 } else {
5875 uint32 l = xml->xml_kids.length;
5876 if (l <= JSVAL_INT_MAX)
5877 *vp = INT_TO_JSVAL(l);
5878 else
5879 *vp = DOUBLE_TO_JSVAL(l);
5881 return JS_TRUE;
5884 static JSBool
5885 xml_localName(JSContext *cx, uintN argc, jsval *vp)
5887 NON_LIST_XML_METHOD_PROLOG;
5888 *vp = xml->name ? xml->name->getQNameLocalNameVal() : JSVAL_NULL;
5889 return JS_TRUE;
5892 static JSBool
5893 xml_name(JSContext *cx, uintN argc, jsval *vp)
5895 NON_LIST_XML_METHOD_PROLOG;
5896 *vp = OBJECT_TO_JSVAL(xml->name);
5897 return JS_TRUE;
5900 static JSBool
5901 xml_namespace(JSContext *cx, uintN argc, jsval *vp)
5903 JSLinearString *prefix, *nsprefix;
5904 jsuint i, length;
5905 JSObject *ns;
5907 NON_LIST_XML_METHOD_PROLOG;
5908 if (argc == 0 && !JSXML_HAS_NAME(xml)) {
5909 *vp = JSVAL_NULL;
5910 return true;
5913 if (argc == 0) {
5914 prefix = NULL;
5915 } else {
5916 JSString *str = js_ValueToString(cx, Valueify(vp[2]));
5917 if (!str)
5918 return false;
5919 prefix = str->ensureLinear(cx);
5920 if (!prefix)
5921 return false;
5922 vp[2] = STRING_TO_JSVAL(prefix); /* local root */
5925 AutoNamespaceArray inScopeNSes(cx);
5926 if (!FindInScopeNamespaces(cx, xml, &inScopeNSes.array))
5927 return false;
5929 if (!prefix) {
5930 ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
5931 if (!ns)
5932 return false;
5933 } else {
5934 ns = NULL;
5935 for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
5936 ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject);
5937 if (ns) {
5938 nsprefix = ns->getNamePrefix();
5939 if (nsprefix && EqualStrings(nsprefix, prefix))
5940 break;
5941 ns = NULL;
5946 *vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns);
5947 return true;
5950 static JSBool
5951 xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp)
5953 NON_LIST_XML_METHOD_PROLOG;
5954 if (JSXML_HAS_VALUE(xml))
5955 return true;
5957 AutoNamespaceArray ancestors(cx);
5958 AutoNamespaceArray declared(cx);
5960 JSXML *yml = xml;
5961 while ((yml = yml->parent) != NULL) {
5962 JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
5963 for (uint32 i = 0, n = yml->xml_namespaces.length; i < n; i++) {
5964 JSObject *ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
5965 if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
5966 if (!XMLARRAY_APPEND(cx, &ancestors.array, ns))
5967 return false;
5972 for (uint32 i = 0, n = xml->xml_namespaces.length; i < n; i++) {
5973 JSObject *ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
5974 if (!ns)
5975 continue;
5976 if (!IsDeclared(ns))
5977 continue;
5978 if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
5979 if (!XMLARRAY_APPEND(cx, &declared.array, ns))
5980 return false;
5984 return NamespacesToJSArray(cx, &declared.array, vp);
5987 static const char js_attribute_str[] = "attribute";
5988 static const char js_text_str[] = "text";
5990 /* Exported to jsgc.c #ifdef DEBUG. */
5991 const char *js_xml_class_str[] = {
5992 "list",
5993 "element",
5994 js_attribute_str,
5995 "processing-instruction",
5996 js_text_str,
5997 "comment"
6000 static JSBool
6001 xml_nodeKind(JSContext *cx, uintN argc, jsval *vp)
6003 JSString *str;
6005 NON_LIST_XML_METHOD_PROLOG;
6006 str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6007 if (!str)
6008 return JS_FALSE;
6009 *vp = STRING_TO_JSVAL(str);
6010 return JS_TRUE;
6013 static void
6014 NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index)
6016 if (xml->xml_class == JSXML_CLASS_LIST)
6017 DeleteListElement(cx, xml, index);
6018 else
6019 DeleteByIndex(cx, xml, index);
6022 /* XML and XMLList */
6023 static JSBool
6024 xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml)
6026 JSXML *kid, *kid2;
6027 uint32 i, n;
6028 JSObject *kidobj;
6029 JSString *str;
6031 if (!JSXML_HAS_KIDS(xml))
6032 return JS_TRUE;
6034 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6035 if (!xml)
6036 return JS_FALSE;
6038 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6039 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6040 if (!kid)
6041 continue;
6042 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6043 kidobj = js_GetXMLObject(cx, kid);
6044 if (!kidobj || !xml_normalize_helper(cx, kidobj, kid))
6045 return JS_FALSE;
6046 } else if (kid->xml_class == JSXML_CLASS_TEXT) {
6047 while (i + 1 < n &&
6048 (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
6049 kid2->xml_class == JSXML_CLASS_TEXT) {
6050 str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
6051 if (!str)
6052 return JS_FALSE;
6053 NormalizingDelete(cx, xml, i + 1);
6054 n = xml->xml_kids.length;
6055 kid->xml_value = str;
6057 if (kid->xml_value->empty()) {
6058 NormalizingDelete(cx, xml, i);
6059 n = xml->xml_kids.length;
6060 --i;
6065 return JS_TRUE;
6068 static JSBool
6069 xml_normalize(JSContext *cx, uintN argc, jsval *vp)
6071 XML_METHOD_PROLOG;
6072 *vp = OBJECT_TO_JSVAL(obj);
6073 return xml_normalize_helper(cx, obj, xml);
6076 /* XML and XMLList */
6077 static JSBool
6078 xml_parent(JSContext *cx, uintN argc, jsval *vp)
6080 JSXML *parent, *kid;
6081 uint32 i, n;
6082 JSObject *parentobj;
6084 XML_METHOD_PROLOG;
6085 parent = xml->parent;
6086 if (xml->xml_class == JSXML_CLASS_LIST) {
6087 *vp = JSVAL_VOID;
6088 n = xml->xml_kids.length;
6089 if (n == 0)
6090 return JS_TRUE;
6092 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6093 if (!kid)
6094 return JS_TRUE;
6095 parent = kid->parent;
6096 for (i = 1; i < n; i++) {
6097 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6098 if (kid && kid->parent != parent)
6099 return JS_TRUE;
6103 if (!parent) {
6104 *vp = JSVAL_NULL;
6105 return JS_TRUE;
6108 parentobj = js_GetXMLObject(cx, parent);
6109 if (!parentobj)
6110 return JS_FALSE;
6111 *vp = OBJECT_TO_JSVAL(parentobj);
6112 return JS_TRUE;
6115 /* XML and XMLList */
6116 static JSBool
6117 xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6118 JSObject *nameqn, jsval *vp)
6120 JSXML *list, *vxml;
6121 JSBool ok;
6122 JSObject *kidobj;
6123 jsval v;
6124 uint32 i, n;
6126 list = xml_list_helper(cx, xml, vp);
6127 if (!list)
6128 return JS_FALSE;
6130 list->xml_targetprop = nameqn;
6131 ok = JS_TRUE;
6133 if (xml->xml_class == JSXML_CLASS_LIST) {
6134 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6135 JSXMLArrayCursor cursor(&xml->xml_kids);
6136 while (JSXML *kid = (JSXML *) cursor.getNext()) {
6137 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6138 ok = js_EnterLocalRootScope(cx);
6139 if (!ok)
6140 break;
6141 kidobj = js_GetXMLObject(cx, kid);
6142 if (kidobj) {
6143 ok = xml_processingInstructions_helper(cx, kidobj, kid,
6144 nameqn, &v);
6145 } else {
6146 ok = JS_FALSE;
6147 v = JSVAL_NULL;
6149 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
6150 if (!ok)
6151 break;
6152 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6153 if (JSXML_LENGTH(vxml) != 0) {
6154 ok = Append(cx, list, vxml);
6155 if (!ok)
6156 break;
6160 } else {
6161 /* 13.4.4.28 Step 4. */
6162 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6163 JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6164 if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
6165 JSLinearString *localName = nameqn->getQNameLocalName();
6166 if (IS_STAR(localName) ||
6167 EqualStrings(localName, kid->name->getQNameLocalName())) {
6168 ok = Append(cx, list, kid);
6169 if (!ok)
6170 break;
6176 return ok;
6179 static JSBool
6180 xml_processingInstructions(JSContext *cx, uintN argc, jsval *vp)
6182 jsval name;
6183 JSObject *nameqn;
6184 jsid funid;
6186 XML_METHOD_PROLOG;
6188 name = (argc == 0) ? ATOM_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
6189 nameqn = ToXMLName(cx, name, &funid);
6190 if (!nameqn)
6191 return JS_FALSE;
6192 vp[2] = OBJECT_TO_JSVAL(nameqn);
6194 if (!JSID_IS_VOID(funid))
6195 return xml_list_helper(cx, xml, vp) != NULL;
6197 return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp);
6200 static JSBool
6201 xml_prependChild(JSContext *cx, uintN argc, jsval *vp)
6203 NON_LIST_XML_METHOD_PROLOG;
6204 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6205 if (!xml)
6206 return JS_FALSE;
6207 *vp = OBJECT_TO_JSVAL(obj);
6208 return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID);
6211 /* XML and XMLList */
6212 static JSBool
6213 xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
6215 bool isIndex;
6216 uint32 index;
6218 XML_METHOD_PROLOG;
6219 *vp = JSVAL_FALSE;
6220 if (argc != 0) {
6221 if (!js_IdValIsIndex(cx, vp[2], &index, &isIndex))
6222 return JS_FALSE;
6224 if (isIndex) {
6225 if (xml->xml_class == JSXML_CLASS_LIST) {
6226 /* 13.5.4.18. */
6227 *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6228 } else {
6229 /* 13.4.4.30. */
6230 *vp = BOOLEAN_TO_JSVAL(index == 0);
6234 return JS_TRUE;
6237 static JSBool
6238 namespace_full_match(const void *a, const void *b)
6240 const JSObject *nsa = (const JSObject *) a;
6241 const JSObject *nsb = (const JSObject *) b;
6242 JSLinearString *prefixa = nsa->getNamePrefix();
6243 JSLinearString *prefixb;
6245 if (prefixa) {
6246 prefixb = nsb->getNamePrefix();
6247 if (prefixb && !EqualStrings(prefixa, prefixb))
6248 return JS_FALSE;
6250 return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
6253 static JSBool
6254 xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns)
6256 JSObject *thisns, *attrns;
6257 uint32 i, n;
6258 JSXML *attr, *kid;
6260 thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6261 JS_ASSERT(thisns);
6262 if (thisns == ns)
6263 return JS_TRUE;
6265 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6266 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6267 if (!attr)
6268 continue;
6269 attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
6270 JS_ASSERT(attrns);
6271 if (attrns == ns)
6272 return JS_TRUE;
6275 i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
6276 if (i != XML_NOT_FOUND)
6277 XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
6279 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6280 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6281 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6282 if (!xml_removeNamespace_helper(cx, kid, ns))
6283 return JS_FALSE;
6286 return JS_TRUE;
6289 static JSBool
6290 xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp)
6292 JSObject *ns;
6294 NON_LIST_XML_METHOD_PROLOG;
6295 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6296 goto done;
6297 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6298 if (!xml)
6299 return JS_FALSE;
6301 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6302 return JS_FALSE;
6303 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6304 ns = JSVAL_TO_OBJECT(*vp);
6306 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6307 if (!xml_removeNamespace_helper(cx, xml, ns))
6308 return JS_FALSE;
6309 done:
6310 *vp = OBJECT_TO_JSVAL(obj);
6311 return JS_TRUE;
6314 static JSBool
6315 xml_replace(JSContext *cx, uintN argc, jsval *vp)
6317 jsval value;
6318 JSXML *vxml, *kid;
6319 uint32 index, i;
6320 JSObject *nameqn;
6322 NON_LIST_XML_METHOD_PROLOG;
6323 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6324 goto done;
6326 if (argc <= 1) {
6327 value = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6328 } else {
6329 value = vp[3];
6330 vxml = VALUE_IS_XML(value)
6331 ? (JSXML *) JSVAL_TO_OBJECT(value)->getPrivate()
6332 : NULL;
6333 if (!vxml) {
6334 if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
6335 return JS_FALSE;
6336 value = vp[3];
6337 } else {
6338 vxml = DeepCopy(cx, vxml, NULL, 0);
6339 if (!vxml)
6340 return JS_FALSE;
6341 value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
6345 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6346 if (!xml)
6347 return JS_FALSE;
6349 bool haveIndex;
6350 if (argc == 0) {
6351 haveIndex = false;
6352 } else {
6353 if (!js_IdValIsIndex(cx, vp[2], &index, &haveIndex))
6354 return JS_FALSE;
6357 if (!haveIndex) {
6359 * Call function QName per spec, not ToXMLName, to avoid attribute
6360 * names.
6362 if (!QNameHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6363 return JS_FALSE;
6364 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6365 nameqn = JSVAL_TO_OBJECT(*vp);
6367 i = xml->xml_kids.length;
6368 index = XML_NOT_FOUND;
6369 while (i != 0) {
6370 --i;
6371 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6372 if (kid && MatchElemName(nameqn, kid)) {
6373 if (i != XML_NOT_FOUND)
6374 DeleteByIndex(cx, xml, i);
6375 index = i;
6379 if (index == XML_NOT_FOUND)
6380 goto done;
6383 if (!Replace(cx, xml, index, value))
6384 return JS_FALSE;
6386 done:
6387 *vp = OBJECT_TO_JSVAL(obj);
6388 return JS_TRUE;
6391 static JSBool
6392 xml_setChildren(JSContext *cx, uintN argc, jsval *vp)
6394 JSObject *obj;
6396 if (!StartNonListXMLMethod(cx, vp, &obj))
6397 return JS_FALSE;
6399 *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */
6400 if (!PutProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.starAtom), vp))
6401 return JS_FALSE;
6403 *vp = OBJECT_TO_JSVAL(obj);
6404 return JS_TRUE;
6407 static JSBool
6408 xml_setLocalName(JSContext *cx, uintN argc, jsval *vp)
6410 jsval name;
6411 JSObject *nameqn;
6412 JSLinearString *namestr;
6414 NON_LIST_XML_METHOD_PROLOG;
6415 if (!JSXML_HAS_NAME(xml))
6416 return JS_TRUE;
6418 if (argc == 0) {
6419 namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6420 } else {
6421 name = vp[2];
6422 if (!JSVAL_IS_PRIMITIVE(name) &&
6423 JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) {
6424 nameqn = JSVAL_TO_OBJECT(name);
6425 namestr = nameqn->getQNameLocalName();
6426 } else {
6427 if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
6428 return JS_FALSE;
6429 name = vp[2];
6430 namestr = JSVAL_TO_STRING(name)->ensureLinear(cx);
6431 if (!namestr)
6432 return JS_FALSE;
6436 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6437 if (!xml)
6438 return JS_FALSE;
6439 if (namestr)
6440 xml->name->setQNameLocalName(namestr);
6441 return JS_TRUE;
6444 static JSBool
6445 xml_setName(JSContext *cx, uintN argc, jsval *vp)
6447 jsval name;
6448 JSObject *nameqn;
6449 JSXML *nsowner;
6450 JSXMLArray *nsarray;
6451 uint32 i, n;
6452 JSObject *ns;
6454 NON_LIST_XML_METHOD_PROLOG;
6455 if (!JSXML_HAS_NAME(xml))
6456 return JS_TRUE;
6458 if (argc == 0) {
6459 name = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6460 } else {
6461 name = vp[2];
6462 if (!JSVAL_IS_PRIMITIVE(name) &&
6463 JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass &&
6464 !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) {
6465 name = vp[2] = nameqn->getQNameLocalNameVal();
6469 nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&name));
6470 if (!nameqn)
6471 return JS_FALSE;
6473 /* ECMA-357 13.4.4.35 Step 4. */
6474 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6475 nameqn->setNameURI(cx->runtime->emptyString);
6477 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6478 if (!xml)
6479 return JS_FALSE;
6480 xml->name = nameqn;
6483 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6484 * in-scope namespaces, either by finding an in-scope namespace with a
6485 * matching uri and setting the new name's prefix to that namespace's
6486 * prefix, or by extending the in-scope namespaces for xml (which are in
6487 * xml->parent if xml is an attribute or a PI).
6489 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6490 nsowner = xml;
6491 } else {
6492 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6493 return JS_TRUE;
6494 nsowner = xml->parent;
6497 if (nameqn->getNamePrefix()) {
6499 * The name being set has a prefix, which originally came from some
6500 * namespace object (which may be the null namespace, where both the
6501 * prefix and uri are the empty string). We must go through a full
6502 * GetNamespace in case that namespace is in-scope in nsowner.
6504 * If we find such an in-scope namespace, we return true right away,
6505 * in this block. Otherwise, we fall through to the final return of
6506 * AddInScopeNamespace(cx, nsowner, ns).
6508 ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
6509 if (!ns)
6510 return JS_FALSE;
6512 /* XXXbe have to test membership to see whether GetNamespace added */
6513 if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
6514 return JS_TRUE;
6515 } else {
6517 * At this point, we know prefix of nameqn is null, so its uri can't
6518 * be the empty string (the null namespace always uses the empty string
6519 * for both prefix and uri).
6521 * This means we must inline GetNamespace and specialize it to match
6522 * uri only, never prefix. If we find a namespace with nameqn's uri
6523 * already in nsowner->xml_namespaces, then all that we need do is set
6524 * prefix of nameqn to that namespace's prefix.
6526 * If no such namespace exists, we can create one without going through
6527 * the constructor, because we know uri of nameqn is non-empty (so
6528 * prefix does not need to be converted from null to empty by QName).
6530 JS_ASSERT(!nameqn->getNameURI()->empty());
6532 nsarray = &nsowner->xml_namespaces;
6533 for (i = 0, n = nsarray->length; i < n; i++) {
6534 ns = XMLARRAY_MEMBER(nsarray, i, JSObject);
6535 if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) {
6536 nameqn->setNamePrefix(ns->getNamePrefix());
6537 return JS_TRUE;
6541 ns = NewXMLNamespace(cx, NULL, nameqn->getNameURI(), JS_TRUE);
6542 if (!ns)
6543 return JS_FALSE;
6546 if (!AddInScopeNamespace(cx, nsowner, ns))
6547 return JS_FALSE;
6548 vp[0] = JSVAL_VOID;
6549 return JS_TRUE;
6552 static JSBool
6553 xml_setNamespace(JSContext *cx, uintN argc, jsval *vp)
6555 JSObject *qn;
6556 JSObject *ns;
6557 jsval qnargv[2];
6558 JSXML *nsowner;
6560 NON_LIST_XML_METHOD_PROLOG;
6561 if (!JSXML_HAS_NAME(xml))
6562 return JS_TRUE;
6564 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6565 if (!xml)
6566 return JS_FALSE;
6568 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj,
6569 argc == 0 ? 0 : 1, Valueify(vp + 2));
6570 if (!ns)
6571 return JS_FALSE;
6572 vp[0] = OBJECT_TO_JSVAL(ns);
6573 ns->setNamespaceDeclared(JSVAL_TRUE);
6575 qnargv[0] = OBJECT_TO_JSVAL(ns);
6576 qnargv[1] = OBJECT_TO_JSVAL(xml->name);
6577 qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv));
6578 if (!qn)
6579 return JS_FALSE;
6581 xml->name = qn;
6584 * Erratum: the spec fails to update the governing in-scope namespaces.
6585 * See the erratum noted in xml_setName, above.
6587 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6588 nsowner = xml;
6589 } else {
6590 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6591 return JS_TRUE;
6592 nsowner = xml->parent;
6594 if (!AddInScopeNamespace(cx, nsowner, ns))
6595 return JS_FALSE;
6596 vp[0] = JSVAL_VOID;
6597 return JS_TRUE;
6600 /* XML and XMLList */
6601 static JSBool
6602 xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
6604 JSXML *list, *kid, *vxml;
6605 uint32 i, n;
6606 JSBool ok;
6607 JSObject *kidobj;
6608 jsval v;
6610 list = xml_list_helper(cx, xml, vp);
6611 if (!list)
6612 return JS_FALSE;
6614 if (xml->xml_class == JSXML_CLASS_LIST) {
6615 ok = JS_TRUE;
6616 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6617 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6618 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6619 ok = js_EnterLocalRootScope(cx);
6620 if (!ok)
6621 break;
6622 kidobj = js_GetXMLObject(cx, kid);
6623 if (kidobj) {
6624 ok = xml_text_helper(cx, kidobj, kid, &v);
6625 } else {
6626 ok = JS_FALSE;
6627 v = JSVAL_NULL;
6629 js_LeaveLocalRootScopeWithResult(cx, Valueify(v));
6630 if (!ok)
6631 return JS_FALSE;
6632 vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6633 if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
6634 return JS_FALSE;
6637 } else {
6638 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6639 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6640 if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
6641 if (!Append(cx, list, kid))
6642 return JS_FALSE;
6646 return JS_TRUE;
6649 static JSBool
6650 xml_text(JSContext *cx, uintN argc, jsval *vp)
6652 XML_METHOD_PROLOG;
6653 return xml_text_helper(cx, obj, xml, vp);
6656 /* XML and XMLList */
6657 static JSString *
6658 xml_toString_helper(JSContext *cx, JSXML *xml)
6660 JSString *str, *kidstr;
6662 if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
6663 xml->xml_class == JSXML_CLASS_TEXT) {
6664 return xml->xml_value;
6667 if (!HasSimpleContent(xml))
6668 return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0);
6670 str = cx->runtime->emptyString;
6671 if (!js_EnterLocalRootScope(cx))
6672 return NULL;
6673 JSXMLArrayCursor cursor(&xml->xml_kids);
6674 while (JSXML *kid = (JSXML *) cursor.getNext()) {
6675 if (kid->xml_class != JSXML_CLASS_COMMENT &&
6676 kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
6677 kidstr = xml_toString_helper(cx, kid);
6678 if (!kidstr) {
6679 str = NULL;
6680 break;
6682 str = js_ConcatStrings(cx, str, kidstr);
6683 if (!str)
6684 break;
6687 js_LeaveLocalRootScopeWithResult(cx, str);
6688 return str;
6691 static JSBool
6692 xml_toSource(JSContext *cx, uintN argc, jsval *vp)
6694 jsval thisv;
6695 JSString *str;
6697 thisv = JS_THIS(cx, vp);
6698 if (JSVAL_IS_NULL(thisv))
6699 return JS_FALSE;
6700 str = ToXMLString(cx, thisv, TO_SOURCE_FLAG);
6701 if (!str)
6702 return JS_FALSE;
6703 *vp = STRING_TO_JSVAL(str);
6704 return JS_TRUE;
6707 static JSBool
6708 xml_toString(JSContext *cx, uintN argc, jsval *vp)
6710 JSString *str;
6712 XML_METHOD_PROLOG;
6713 str = xml_toString_helper(cx, xml);
6714 if (!str)
6715 return JS_FALSE;
6716 *vp = STRING_TO_JSVAL(str);
6717 return JS_TRUE;
6720 /* XML and XMLList */
6721 static JSBool
6722 xml_toXMLString(JSContext *cx, uintN argc, jsval *vp)
6724 jsval thisv;
6725 JSString *str;
6727 thisv = JS_THIS(cx, vp);
6728 if (JSVAL_IS_NULL(thisv))
6729 return JS_FALSE;
6730 str = ToXMLString(cx, thisv, 0);
6731 if (!str)
6732 return JS_FALSE;
6733 *vp = STRING_TO_JSVAL(str);
6734 return JS_TRUE;
6737 /* XML and XMLList */
6738 static JSBool
6739 xml_valueOf(JSContext *cx, uintN argc, jsval *vp)
6741 *vp = JS_THIS(cx, vp);
6742 return !JSVAL_IS_NULL(*vp);
6745 static JSFunctionSpec xml_methods[] = {
6746 JS_FN("addNamespace", xml_addNamespace, 1,0),
6747 JS_FN("appendChild", xml_appendChild, 1,0),
6748 JS_FN(js_attribute_str, xml_attribute, 1,0),
6749 JS_FN("attributes", xml_attributes, 0,0),
6750 JS_FN("child", xml_child, 1,0),
6751 JS_FN("childIndex", xml_childIndex, 0,0),
6752 JS_FN("children", xml_children, 0,0),
6753 JS_FN("comments", xml_comments, 0,0),
6754 JS_FN("contains", xml_contains, 1,0),
6755 JS_FN("copy", xml_copy, 0,0),
6756 JS_FN("descendants", xml_descendants, 1,0),
6757 JS_FN("elements", xml_elements, 1,0),
6758 JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0),
6759 JS_FN("hasComplexContent", xml_hasComplexContent, 1,0),
6760 JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0),
6761 JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0),
6762 JS_FN("insertChildAfter", xml_insertChildAfter, 2,0),
6763 JS_FN("insertChildBefore", xml_insertChildBefore, 2,0),
6764 JS_FN(js_length_str, xml_length, 0,0),
6765 JS_FN(js_localName_str, xml_localName, 0,0),
6766 JS_FN(js_name_str, xml_name, 0,0),
6767 JS_FN(js_namespace_str, xml_namespace, 1,0),
6768 JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0),
6769 JS_FN("nodeKind", xml_nodeKind, 0,0),
6770 JS_FN("normalize", xml_normalize, 0,0),
6771 JS_FN(js_xml_parent_str, xml_parent, 0,0),
6772 JS_FN("processingInstructions",xml_processingInstructions,1,0),
6773 JS_FN("prependChild", xml_prependChild, 1,0),
6774 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0),
6775 JS_FN("removeNamespace", xml_removeNamespace, 1,0),
6776 JS_FN("replace", xml_replace, 2,0),
6777 JS_FN("setChildren", xml_setChildren, 1,0),
6778 JS_FN("setLocalName", xml_setLocalName, 1,0),
6779 JS_FN("setName", xml_setName, 1,0),
6780 JS_FN("setNamespace", xml_setNamespace, 1,0),
6781 JS_FN(js_text_str, xml_text, 0,0),
6782 JS_FN(js_toSource_str, xml_toSource, 0,0),
6783 JS_FN(js_toString_str, xml_toString, 0,0),
6784 JS_FN(js_toXMLString_str, xml_toXMLString, 0,0),
6785 JS_FN(js_valueOf_str, xml_valueOf, 0,0),
6786 JS_FS_END
6789 static JSBool
6790 CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
6792 int i;
6793 const char *name;
6794 jsval v;
6796 /* Note: PRETTY_INDENT is not a boolean setting. */
6797 for (i = 0; xml_static_props[i].name; i++) {
6798 name = xml_static_props[i].name;
6799 if (!JS_GetProperty(cx, from, name, &v))
6800 return false;
6801 if (name == js_prettyIndent_str) {
6802 if (!JSVAL_IS_NUMBER(v))
6803 continue;
6804 } else {
6805 if (!JSVAL_IS_BOOLEAN(v))
6806 continue;
6808 if (!JS_SetProperty(cx, to, name, &v))
6809 return false;
6812 return true;
6815 static JSBool
6816 SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
6818 int i;
6819 jsval v;
6821 /* Note: PRETTY_INDENT is not a boolean setting. */
6822 for (i = 0; xml_static_props[i].name; i++) {
6823 v = (xml_static_props[i].name != js_prettyIndent_str)
6824 ? JSVAL_TRUE : INT_TO_JSVAL(2);
6825 if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
6826 return JS_FALSE;
6828 return true;
6831 static JSBool
6832 xml_settings(JSContext *cx, uintN argc, jsval *vp)
6834 JSObject *settings;
6835 JSObject *obj;
6837 settings = JS_NewObject(cx, NULL, NULL, NULL);
6838 if (!settings)
6839 return JS_FALSE;
6840 *vp = OBJECT_TO_JSVAL(settings);
6841 obj = JS_THIS_OBJECT(cx, vp);
6842 return obj && CopyXMLSettings(cx, obj, settings);
6845 static JSBool
6846 xml_setSettings(JSContext *cx, uintN argc, jsval *vp)
6848 JSObject *obj, *settings;
6849 jsval v;
6850 JSBool ok;
6852 obj = JS_THIS_OBJECT(cx, vp);
6853 if (!obj)
6854 return JS_FALSE;
6855 v = (argc == 0) ? JSVAL_VOID : vp[2];
6856 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
6857 ok = SetDefaultXMLSettings(cx, obj);
6858 } else {
6859 if (JSVAL_IS_PRIMITIVE(v))
6860 return JS_TRUE;
6861 settings = JSVAL_TO_OBJECT(v);
6862 ok = CopyXMLSettings(cx, settings, obj);
6864 return ok;
6867 static JSBool
6868 xml_defaultSettings(JSContext *cx, uintN argc, jsval *vp)
6870 JSObject *settings;
6872 settings = JS_NewObject(cx, NULL, NULL, NULL);
6873 if (!settings)
6874 return JS_FALSE;
6875 *vp = OBJECT_TO_JSVAL(settings);
6876 return SetDefaultXMLSettings(cx, settings);
6879 static JSFunctionSpec xml_static_methods[] = {
6880 JS_FN("settings", xml_settings, 0,0),
6881 JS_FN("setSettings", xml_setSettings, 1,0),
6882 JS_FN("defaultSettings", xml_defaultSettings, 0,0),
6883 JS_FS_END
6886 static JSBool
6887 XML(JSContext *cx, uintN argc, Value *vp)
6889 JSXML *xml, *copy;
6890 JSObject *xobj, *vobj;
6891 Class *clasp;
6893 jsval v = argc ? Jsvalify(vp[2]) : JSVAL_VOID;
6895 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
6896 v = STRING_TO_JSVAL(cx->runtime->emptyString);
6898 xobj = ToXML(cx, v);
6899 if (!xobj)
6900 return JS_FALSE;
6901 xml = (JSXML *) xobj->getPrivate();
6903 if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) {
6904 vobj = JSVAL_TO_OBJECT(v);
6905 clasp = vobj->getClass();
6906 if (clasp == &js_XMLClass ||
6907 (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
6908 copy = DeepCopy(cx, xml, NULL, 0);
6909 if (!copy)
6910 return JS_FALSE;
6911 vp->setObject(*copy->object);
6912 return JS_TRUE;
6916 vp->setObject(*xobj);
6917 return JS_TRUE;
6920 static JSBool
6921 XMLList(JSContext *cx, uintN argc, jsval *vp)
6923 JSObject *vobj, *listobj;
6924 JSXML *xml, *list;
6926 jsval v = argc ? vp[2] : JSVAL_VOID;
6928 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
6929 v = STRING_TO_JSVAL(cx->runtime->emptyString);
6931 if (IsConstructing(Valueify(vp)) && !JSVAL_IS_PRIMITIVE(v)) {
6932 vobj = JSVAL_TO_OBJECT(v);
6933 if (vobj->isXML()) {
6934 xml = (JSXML *) vobj->getPrivate();
6935 if (xml->xml_class == JSXML_CLASS_LIST) {
6936 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
6937 if (!listobj)
6938 return JS_FALSE;
6939 *vp = OBJECT_TO_JSVAL(listobj);
6941 list = (JSXML *) listobj->getPrivate();
6942 if (!Append(cx, list, xml))
6943 return JS_FALSE;
6944 return JS_TRUE;
6949 /* Toggle on XML support since the script has explicitly requested it. */
6950 listobj = ToXMLList(cx, v);
6951 if (!listobj)
6952 return JS_FALSE;
6954 *vp = OBJECT_TO_JSVAL(listobj);
6955 return JS_TRUE;
6958 #ifdef DEBUG_notme
6959 JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
6960 uint32 xml_serial;
6961 #endif
6963 JSXML *
6964 js_NewXML(JSContext *cx, JSXMLClass xml_class)
6966 JSXML *xml = js_NewGCXML(cx);
6967 if (!xml)
6968 return NULL;
6970 xml->object = NULL;
6971 xml->domnode = NULL;
6972 xml->parent = NULL;
6973 xml->name = NULL;
6974 xml->xml_class = xml_class;
6975 xml->xml_flags = 0;
6976 if (JSXML_CLASS_HAS_VALUE(xml_class)) {
6977 xml->xml_value = cx->runtime->emptyString;
6978 } else {
6979 xml->xml_kids.init();
6980 if (xml_class == JSXML_CLASS_LIST) {
6981 xml->xml_target = NULL;
6982 xml->xml_targetprop = NULL;
6983 } else {
6984 xml->xml_namespaces.init();
6985 xml->xml_attrs.init();
6989 #ifdef DEBUG_notme
6990 JS_APPEND_LINK(&xml->links, &xml_leaks);
6991 xml->serial = xml_serial++;
6992 #endif
6993 METER(xml_stats.xml);
6994 return xml;
6997 void
6998 js_TraceXML(JSTracer *trc, JSXML *xml)
7000 if (xml->object)
7001 MarkObject(trc, *xml->object, "object");
7002 if (xml->name)
7003 MarkObject(trc, *xml->name, "name");
7004 if (xml->parent)
7005 JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent");
7007 if (JSXML_HAS_VALUE(xml)) {
7008 if (xml->xml_value)
7009 MarkString(trc, xml->xml_value, "value");
7010 return;
7013 xml_trace_vector(trc,
7014 (JSXML **) xml->xml_kids.vector,
7015 xml->xml_kids.length);
7016 XMLArrayCursorTrace(trc, xml->xml_kids.cursors);
7017 if (IS_GC_MARKING_TRACER(trc))
7018 xml->xml_kids.trim();
7020 if (xml->xml_class == JSXML_CLASS_LIST) {
7021 if (xml->xml_target)
7022 JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target");
7023 if (xml->xml_targetprop)
7024 MarkObject(trc, *xml->xml_targetprop, "targetprop");
7025 } else {
7026 MarkObjectRange(trc, xml->xml_namespaces.length,
7027 (JSObject **) xml->xml_namespaces.vector,
7028 "xml_namespaces");
7029 XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
7030 if (IS_GC_MARKING_TRACER(trc))
7031 xml->xml_namespaces.trim();
7033 xml_trace_vector(trc,
7034 (JSXML **) xml->xml_attrs.vector,
7035 xml->xml_attrs.length);
7036 XMLArrayCursorTrace(trc, xml->xml_attrs.cursors);
7037 if (IS_GC_MARKING_TRACER(trc))
7038 xml->xml_attrs.trim();
7042 JSObject *
7043 js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7045 JSXML *xml = js_NewXML(cx, xml_class);
7046 if (!xml)
7047 return NULL;
7049 AutoXMLRooter root(cx, xml);
7050 return js_GetXMLObject(cx, xml);
7053 static JSObject *
7054 NewXMLObject(JSContext *cx, JSXML *xml)
7056 JSObject *obj;
7058 obj = NewNonFunction<WithProto::Class>(cx, &js_XMLClass, NULL, NULL);
7059 if (!obj)
7060 return NULL;
7061 obj->setPrivate(xml);
7062 METER(xml_stats.xmlobj);
7063 return obj;
7066 JSObject *
7067 js_GetXMLObject(JSContext *cx, JSXML *xml)
7069 JSObject *obj;
7071 obj = xml->object;
7072 if (obj) {
7073 JS_ASSERT(obj->getPrivate() == xml);
7074 return obj;
7077 obj = NewXMLObject(cx, xml);
7078 if (!obj)
7079 return NULL;
7080 xml->object = obj;
7081 return obj;
7084 JSObject *
7085 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7087 return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2,
7088 namespace_props, namespace_methods, NULL, NULL);
7091 JSObject *
7092 js_InitQNameClass(JSContext *cx, JSObject *obj)
7094 return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
7095 qname_props, qname_methods, NULL, NULL);
7098 JSObject *
7099 js_InitXMLClass(JSContext *cx, JSObject *obj)
7101 JSObject *proto, *pobj;
7102 JSFunction *fun;
7103 JSXML *xml;
7104 JSProperty *prop;
7105 Shape *shape;
7106 jsval cval, vp[3];
7108 /* Define the isXMLName function. */
7109 if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7110 return NULL;
7112 /* Define the XML class constructor and prototype. */
7113 proto = js_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
7114 NULL, xml_methods,
7115 xml_static_props, xml_static_methods);
7116 if (!proto)
7117 return NULL;
7119 xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7120 if (!xml)
7121 return NULL;
7122 proto->setPrivate(xml);
7123 xml->object = proto;
7124 METER(xml_stats.xmlobj);
7127 * Prepare to set default settings on the XML constructor we just made.
7128 * NB: We can't use JS_GetConstructor, because it calls
7129 * JSObject::getProperty, which is xml_getProperty, which creates a new
7130 * XMLList every time! We must instead call js_LookupProperty directly.
7132 if (!js_LookupProperty(cx, proto,
7133 ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
7134 &pobj, &prop)) {
7135 return NULL;
7137 JS_ASSERT(prop);
7138 shape = (Shape *) prop;
7139 cval = Jsvalify(pobj->nativeGetSlot(shape->slot));
7140 JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
7142 /* Set default settings. */
7143 vp[0] = JSVAL_NULL;
7144 vp[1] = cval;
7145 vp[2] = JSVAL_VOID;
7146 if (!xml_setSettings(cx, 1, vp))
7147 return NULL;
7149 /* Define the XMLList function and give it the same prototype as XML. */
7150 fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR);
7151 if (!fun)
7152 return NULL;
7153 if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
7154 JSPROP_READONLY | JSPROP_PERMANENT)) {
7155 return NULL;
7157 return proto;
7160 JSObject *
7161 js_InitXMLClasses(JSContext *cx, JSObject *obj)
7163 if (!js_InitNamespaceClass(cx, obj))
7164 return NULL;
7165 if (!js_InitQNameClass(cx, obj))
7166 return NULL;
7167 return js_InitXMLClass(cx, obj);
7170 JSBool
7171 js_GetFunctionNamespace(JSContext *cx, Value *vp)
7173 JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
7175 *vp = global->getReservedSlot(JSRESERVED_GLOBAL_FUNCTION_NS);
7176 if (vp->isUndefined()) {
7177 JSRuntime *rt = cx->runtime;
7178 JSLinearString *prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION];
7179 JSLinearString *uri = rt->atomState.functionNamespaceURIAtom;
7180 JSObject *obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE);
7181 if (!obj)
7182 return false;
7185 * Avoid entraining any in-scope Object.prototype. The loss of
7186 * Namespace.prototype is not detectable, as there is no way to
7187 * refer to this instance in scripts. When used to qualify method
7188 * names, its prefix and uri references are copied to the QName.
7189 * The parent remains set and links back to global.
7191 obj->clearProto();
7193 vp->setObject(*obj);
7194 if (!js_SetReservedSlot(cx, global, JSRESERVED_GLOBAL_FUNCTION_NS, *vp))
7195 return false;
7198 return true;
7202 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7203 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7204 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
7205 * requirement that fp->varobj lie directly on fp->scopeChain, although
7206 * it should be reachable using the prototype chain from a scope object (cf.
7207 * JSOPTION_VAROBJFIX in jsapi.h).
7209 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7210 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7211 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7212 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7213 * the default XML namespace will be set to ("", n.uri). So the uri string
7214 * is really the only usefully stored value of the default namespace.
7216 JSBool
7217 js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7219 JSObject *ns, *obj, *tmp;
7220 jsval v;
7222 JSObject *scopeChain = GetScopeChain(cx);
7224 obj = NULL;
7225 for (tmp = scopeChain; tmp; tmp = tmp->getParent()) {
7226 Class *clasp = tmp->getClass();
7227 if (clasp == &js_BlockClass || clasp == &js_WithClass)
7228 continue;
7229 if (!tmp->getProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(&v)))
7230 return JS_FALSE;
7231 if (!JSVAL_IS_PRIMITIVE(v)) {
7232 *vp = v;
7233 return JS_TRUE;
7235 obj = tmp;
7238 ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, 0, NULL);
7239 if (!ns)
7240 return JS_FALSE;
7241 v = OBJECT_TO_JSVAL(ns);
7242 if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(v),
7243 PropertyStub, PropertyStub, JSPROP_PERMANENT)) {
7244 return JS_FALSE;
7246 *vp = v;
7247 return JS_TRUE;
7250 JSBool
7251 js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
7253 Value argv[2];
7254 argv[0].setString(cx->runtime->emptyString);
7255 argv[1] = v;
7256 JSObject *ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, 2, argv);
7257 if (!ns)
7258 return JS_FALSE;
7260 JSStackFrame *fp = js_GetTopStackFrame(cx);
7261 JSObject &varobj = fp->varobj(cx);
7262 if (!varobj.defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns),
7263 PropertyStub, PropertyStub, JSPROP_PERMANENT)) {
7264 return JS_FALSE;
7266 return JS_TRUE;
7269 JSBool
7270 js_ToAttributeName(JSContext *cx, Value *vp)
7272 JSObject *qn;
7274 qn = ToAttributeName(cx, Jsvalify(*vp));
7275 if (!qn)
7276 return JS_FALSE;
7277 vp->setObject(*qn);
7278 return JS_TRUE;
7281 JSFlatString *
7282 js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote)
7284 StringBuffer sb(cx);
7285 return EscapeAttributeValue(cx, sb, str, quote);
7288 JSString *
7289 js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7291 size_t len = str->length();
7292 const jschar *chars = str->getChars(cx);
7293 if (!chars)
7294 return NULL;
7296 size_t len2 = str2->length();
7297 const jschar *chars2 = str2->getChars(cx);
7298 if (!chars2)
7299 return NULL;
7301 size_t newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
7302 jschar *newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar));
7303 if (!newchars)
7304 return NULL;
7306 js_strncpy(newchars, chars, len);
7307 newchars += len;
7308 if (isName) {
7309 *newchars++ = ' ';
7310 js_strncpy(newchars, chars2, len2);
7311 newchars += len2;
7312 } else {
7313 *newchars++ = '=';
7314 *newchars++ = '"';
7315 js_strncpy(newchars, chars2, len2);
7316 newchars += len2;
7317 *newchars++ = '"';
7319 *newchars = 0;
7320 return js_NewString(cx, newchars - newlen, newlen);
7323 JSFlatString *
7324 js_EscapeElementValue(JSContext *cx, JSString *str)
7326 StringBuffer sb(cx);
7327 return EscapeElementValue(cx, sb, str, 0);
7330 JSString *
7331 js_ValueToXMLString(JSContext *cx, const Value &v)
7333 return ToXMLString(cx, Jsvalify(v), 0);
7336 JSBool
7337 js_GetAnyName(JSContext *cx, jsid *idp)
7339 JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
7340 Value v = global->getReservedSlot(JSProto_AnyName);
7341 if (v.isUndefined()) {
7342 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AnyNameClass, NULL, global);
7343 if (!obj)
7344 return false;
7346 JS_ASSERT(!obj->getProto());
7348 JSRuntime *rt = cx->runtime;
7349 InitXMLQName(obj, rt->emptyString, rt->emptyString,
7350 ATOM_TO_STRING(rt->atomState.starAtom));
7351 METER(xml_stats.qname);
7353 v.setObject(*obj);
7354 if (!js_SetReservedSlot(cx, global, JSProto_AnyName, v))
7355 return false;
7357 *idp = OBJECT_TO_JSID(&v.toObject());
7358 return true;
7361 JSBool
7362 js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *idp)
7364 JSObject *nameobj;
7365 jsval v;
7366 JSObject *qn;
7367 jsid funid;
7368 JSObject *obj, *target, *proto, *pobj;
7369 JSXML *xml;
7370 JSBool found;
7371 JSProperty *prop;
7373 JS_ASSERT(nameval.isObject());
7374 nameobj = &nameval.toObject();
7375 if (nameobj->getClass() == &js_AnyNameClass) {
7376 v = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
7377 nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1,
7378 Valueify(&v));
7379 if (!nameobj)
7380 return JS_FALSE;
7381 } else {
7382 JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
7383 nameobj->getClass() == &js_QNameClass);
7386 qn = nameobj;
7387 if (!IsFunctionQName(cx, qn, &funid))
7388 return JS_FALSE;
7390 obj = &js_GetTopStackFrame(cx)->scopeChain();
7391 do {
7392 /* Skip any With object that can wrap XML. */
7393 target = obj;
7394 while (target->getClass() == &js_WithClass) {
7395 proto = target->getProto();
7396 if (!proto)
7397 break;
7398 target = proto;
7401 if (target->isXML()) {
7402 if (JSID_IS_VOID(funid)) {
7403 xml = (JSXML *) target->getPrivate();
7404 found = HasNamedProperty(xml, qn);
7405 } else {
7406 if (!HasFunctionProperty(cx, target, funid, &found))
7407 return JS_FALSE;
7409 if (found) {
7410 *idp = OBJECT_TO_JSID(nameobj);
7411 *objp = target;
7412 return JS_TRUE;
7414 } else if (!JSID_IS_VOID(funid)) {
7415 if (!target->lookupProperty(cx, funid, &pobj, &prop))
7416 return JS_FALSE;
7417 if (prop) {
7418 *idp = funid;
7419 *objp = target;
7420 return JS_TRUE;
7423 } while ((obj = obj->getParent()) != NULL);
7425 JSAutoByteString printable;
7426 JSString *str = ConvertQNameToString(cx, nameobj);
7427 if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
7428 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
7429 JSMSG_UNDEFINED_XML_NAME, printable.ptr());
7431 return JS_FALSE;
7434 static JSBool
7435 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
7437 JS_ASSERT(obj->isXML());
7440 * See comments before xml_lookupProperty about the need for the proto
7441 * chain lookup.
7443 JSObject *target = obj;
7444 AutoObjectRooter tvr(cx);
7445 for (;;) {
7446 if (!js_GetProperty(cx, target, id, Valueify(vp)))
7447 return false;
7448 if (VALUE_IS_FUNCTION(cx, *vp))
7449 return true;
7450 target = target->getProto();
7451 if (target == NULL || !target->isNative())
7452 break;
7453 tvr.setObject(target);
7456 JSXML *xml = (JSXML *) obj->getPrivate();
7457 if (!HasSimpleContent(xml))
7458 return true;
7460 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
7461 if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
7462 return false;
7464 JS_ASSERT(tvr.object());
7465 return tvr.object()->getProperty(cx, id, Valueify(vp));
7468 static JSXML *
7469 GetPrivate(JSContext *cx, JSObject *obj, const char *method)
7471 JSXML *xml;
7473 xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
7474 if (!xml) {
7475 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7476 JSMSG_INCOMPATIBLE_METHOD,
7477 js_XML_str, method, obj->getClass()->name);
7479 return xml;
7482 JSBool
7483 js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
7485 JSXML *xml, *list;
7487 xml = GetPrivate(cx, obj, "descendants internal method");
7488 if (!xml)
7489 return JS_FALSE;
7491 list = Descendants(cx, xml, id);
7492 if (!list)
7493 return JS_FALSE;
7494 *vp = OBJECT_TO_JSVAL(list->object);
7495 return JS_TRUE;
7498 JSBool
7499 js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
7501 JSXML *list;
7502 uint32 n;
7504 list = (JSXML *) listobj->getPrivate();
7505 for (n = list->xml_kids.length; n != 0; --n)
7506 DeleteListElement(cx, list, 0);
7508 return JS_TRUE;
7511 struct JSXMLFilter
7513 JSXML *list;
7514 JSXML *result;
7515 JSXML *kid;
7516 JSXMLArrayCursor cursor;
7518 JSXMLFilter(JSXML *list, JSXMLArray *array)
7519 : list(list), result(NULL), kid(NULL), cursor(array) {}
7521 ~JSXMLFilter() {}
7524 static void
7525 xmlfilter_trace(JSTracer *trc, JSObject *obj)
7527 JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7528 if (!filter)
7529 return;
7531 JS_ASSERT(filter->list);
7532 JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list");
7533 if (filter->result)
7534 JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result");
7535 if (filter->kid)
7536 JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid");
7539 * We do not need to trace the cursor as that would be done when
7540 * tracing the filter->list.
7544 static void
7545 xmlfilter_finalize(JSContext *cx, JSObject *obj)
7547 JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7548 if (!filter)
7549 return;
7551 cx->destroy(filter);
7554 Class js_XMLFilterClass = {
7555 "XMLFilter",
7556 JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
7557 PropertyStub, /* addProperty */
7558 PropertyStub, /* delProperty */
7559 PropertyStub, /* getProperty */
7560 PropertyStub, /* setProperty */
7561 EnumerateStub,
7562 ResolveStub,
7563 ConvertStub,
7564 xmlfilter_finalize,
7565 NULL, /* reserved0 */
7566 NULL, /* checkAccess */
7567 NULL, /* call */
7568 NULL, /* construct */
7569 NULL, /* xdrObject */
7570 NULL, /* hasInstance */
7571 JS_CLASS_TRACE(xmlfilter_trace)
7574 JSBool
7575 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
7577 jsval *sp;
7578 JSObject *obj, *filterobj, *resobj, *kidobj;
7579 JSXML *xml, *list;
7580 JSXMLFilter *filter;
7582 LeaveTrace(cx);
7583 sp = Jsvalify(cx->regs->sp);
7584 if (!initialized) {
7586 * We haven't iterated yet, so initialize the filter based on the
7587 * value stored in sp[-2].
7589 if (!VALUE_IS_XML(sp[-2])) {
7590 js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, Valueify(sp[-2]), NULL);
7591 return JS_FALSE;
7593 obj = JSVAL_TO_OBJECT(sp[-2]);
7594 xml = (JSXML *) obj->getPrivate();
7596 if (xml->xml_class == JSXML_CLASS_LIST) {
7597 list = xml;
7598 } else {
7599 obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7600 if (!obj)
7601 return JS_FALSE;
7604 * Root just-created obj. sp[-2] cannot be used yet for rooting
7605 * as it may be the only root holding xml.
7607 sp[-1] = OBJECT_TO_JSVAL(obj);
7608 list = (JSXML *) obj->getPrivate();
7609 if (!Append(cx, list, xml))
7610 return JS_FALSE;
7613 filterobj = NewNonFunction<WithProto::Given>(cx, &js_XMLFilterClass, NULL, NULL);
7614 if (!filterobj)
7615 return JS_FALSE;
7618 * Init all filter fields before setPrivate exposes it to
7619 * xmlfilter_trace or xmlfilter_finalize.
7621 filter = cx->create<JSXMLFilter>(list, &list->xml_kids);
7622 if (!filter)
7623 return JS_FALSE;
7624 filterobj->setPrivate(filter);
7626 /* Store filterobj to use in the later iterations. */
7627 sp[-2] = OBJECT_TO_JSVAL(filterobj);
7629 resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7630 if (!resobj)
7631 return JS_FALSE;
7633 /* This also roots resobj. */
7634 filter->result = (JSXML *) resobj->getPrivate();
7635 } else {
7636 /* We have iterated at least once. */
7637 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
7638 JS_ASSERT(JSVAL_TO_OBJECT(sp[-2])->getClass() == &js_XMLFilterClass);
7639 filter = (JSXMLFilter *) JSVAL_TO_OBJECT(sp[-2])->getPrivate();
7640 JS_ASSERT(filter->kid);
7642 /* Check if the filter expression wants to append the element. */
7643 if (js_ValueToBoolean(Valueify(sp[-1])) &&
7644 !Append(cx, filter->result, filter->kid)) {
7645 return JS_FALSE;
7649 /* Do the iteration. */
7650 filter->kid = (JSXML *) filter->cursor.getNext();
7651 if (!filter->kid) {
7653 * Do not defer finishing the cursor until the next GC cycle to avoid
7654 * accumulation of dead cursors associated with filter->list.
7656 filter->cursor.disconnect();
7657 JS_ASSERT(filter->result->object);
7658 sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
7659 kidobj = NULL;
7660 } else {
7661 kidobj = js_GetXMLObject(cx, filter->kid);
7662 if (!kidobj)
7663 return JS_FALSE;
7666 /* Null as kidobj at sp[-1] signals filter termination. */
7667 sp[-1] = OBJECT_TO_JSVAL(kidobj);
7668 return JS_TRUE;
7671 JSObject *
7672 js_ValueToXMLObject(JSContext *cx, const Value &v)
7674 return ToXML(cx, Jsvalify(v));
7677 JSObject *
7678 js_ValueToXMLListObject(JSContext *cx, const Value &v)
7680 return ToXMLList(cx, Jsvalify(v));
7683 JSObject *
7684 js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
7685 JSString *value)
7687 uintN flags;
7688 JSObject *obj;
7689 JSXML *xml;
7690 JSObject *qn;
7692 if (!GetXMLSettingFlags(cx, &flags))
7693 return NULL;
7695 if ((xml_class == JSXML_CLASS_COMMENT &&
7696 (flags & XSF_IGNORE_COMMENTS)) ||
7697 (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
7698 (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
7699 return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
7702 obj = js_NewXMLObject(cx, xml_class);
7703 if (!obj)
7704 return NULL;
7705 xml = (JSXML *) obj->getPrivate();
7706 if (name) {
7707 JSLinearString *linearName = name->ensureLinear(cx);
7708 if (!linearName)
7709 return NULL;
7710 qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName);
7711 if (!qn)
7712 return NULL;
7713 xml->name = qn;
7715 xml->xml_value = value;
7716 return obj;
7719 JSString *
7720 js_MakeXMLCDATAString(JSContext *cx, JSString *str)
7722 StringBuffer sb(cx);
7723 return MakeXMLCDATAString(cx, sb, str);
7726 JSString *
7727 js_MakeXMLCommentString(JSContext *cx, JSString *str)
7729 StringBuffer sb(cx);
7730 return MakeXMLCommentString(cx, sb, str);
7733 JSString *
7734 js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
7736 StringBuffer sb(cx);
7737 return MakeXMLPIString(cx, sb, name, str);
7740 #endif /* JS_HAS_XML_SUPPORT */