Bug 438861 Autocomplete does not pass back previous results to a second search. r...
[mozilla-central.git] / js / src / jsxml.cpp
blob7d4aa5f6dfe7687f11d2db99daed84d50e3e25d9
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 "jsstddef.h"
41 #include "jsconfig.h"
43 #if JS_HAS_XML_SUPPORT
45 #include <math.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include "jstypes.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"
71 #ifdef DEBUG
72 #include <string.h> /* for #ifdef DEBUG memset calls */
73 #endif
76 * NOTES
77 * - in the js shell, you must use the -x command line option, or call
78 * options('xml') before compiling anything that uses XML literals
80 * TODO
81 * - XXXbe patrol
82 * - Fuse objects and their JSXML* private data into single GC-things
83 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
84 * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
85 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
86 * - JS_TypeOfValue sure could use a cleaner interface to "types"
89 #ifdef XML_METERING
90 static struct {
91 jsrefcount qname;
92 jsrefcount qnameobj;
93 jsrefcount liveqname;
94 jsrefcount liveqnameobj;
95 jsrefcount namespace;
96 jsrefcount namespaceobj;
97 jsrefcount livenamespace;
98 jsrefcount livenamespaceobj;
99 jsrefcount xml;
100 jsrefcount xmlobj;
101 jsrefcount livexml;
102 jsrefcount livexmlobj;
103 } xml_stats;
105 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
106 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
107 #else
108 #define METER(x) /* nothing */
109 #define UNMETER(x) /* nothing */
110 #endif
113 * Random utilities and global functions.
115 const char js_isXMLName_str[] = "isXMLName";
116 const char js_XMLList_str[] = "XMLList";
117 const char js_localName_str[] = "localName";
118 const char js_xml_parent_str[] = "parent";
119 const char js_prefix_str[] = "prefix";
120 const char js_toXMLString_str[] = "toXMLString";
121 const char js_uri_str[] = "uri";
123 const char js_amp_entity_str[] = "&amp;";
124 const char js_gt_entity_str[] = "&gt;";
125 const char js_lt_entity_str[] = "&lt;";
126 const char js_quot_entity_str[] = "&quot;";
128 #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
129 #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
131 static JSBool
132 xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
133 jsval *rval)
135 *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
136 return JS_TRUE;
140 * Namespace class and library functions.
142 enum namespace_tinyid {
143 NAMESPACE_PREFIX = -1,
144 NAMESPACE_URI = -2
147 static JSBool
148 namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
150 JSXMLNamespace *ns;
152 if (!JSVAL_IS_INT(id))
153 return JS_TRUE;
155 ns = (JSXMLNamespace *)
156 JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL);
157 if (!ns)
158 return JS_TRUE;
160 switch (JSVAL_TO_INT(id)) {
161 case NAMESPACE_PREFIX:
162 *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID;
163 break;
164 case NAMESPACE_URI:
165 *vp = STRING_TO_JSVAL(ns->uri);
166 break;
168 return JS_TRUE;
171 static void
172 namespace_finalize(JSContext *cx, JSObject *obj)
174 JSXMLNamespace *ns;
175 JSRuntime *rt;
177 ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
178 if (!ns)
179 return;
180 JS_ASSERT(ns->object == obj);
181 ns->object = NULL;
182 UNMETER(xml_stats.livenamespaceobj);
184 rt = cx->runtime;
185 if (rt->functionNamespaceObject == obj)
186 rt->functionNamespaceObject = NULL;
189 static void
190 namespace_trace_vector(JSTracer *trc, JSXMLNamespace **vec,
191 uint32 len)
193 uint32 i;
194 JSXMLNamespace *ns;
196 for (i = 0; i < len; i++) {
197 ns = vec[i];
198 if (ns) {
199 JS_SET_TRACING_INDEX(trc, "namespace_vector", i);
200 JS_CallTracer(trc, ns, JSTRACE_NAMESPACE);
205 static void
206 namespace_trace(JSTracer *trc, JSObject *obj)
208 JSXMLNamespace *ns;
210 ns = (JSXMLNamespace *) JS_GetPrivate(trc->context, obj);
211 if (ns)
212 JS_CALL_TRACER(trc, ns, JSTRACE_NAMESPACE, "private");
215 static JSBool
216 namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
218 JSXMLNamespace *ns, *ns2;
219 JSObject *obj2;
221 ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
222 JS_ASSERT(JSVAL_IS_OBJECT(v));
223 obj2 = JSVAL_TO_OBJECT(v);
224 if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) {
225 *bp = JS_FALSE;
226 } else {
227 ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2);
228 *bp = js_EqualStrings(ns->uri, ns2->uri);
230 return JS_TRUE;
233 JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
234 { "Namespace",
235 JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
236 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
237 JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL,
238 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize,
239 NULL, NULL, NULL, NULL,
240 NULL, NULL, JS_CLASS_TRACE(namespace_trace), NULL },
241 namespace_equality,NULL, NULL, NULL,
242 NULL, NULL, NULL, NULL
245 #define NAMESPACE_ATTRS \
246 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
248 static JSPropertySpec namespace_props[] = {
249 {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0},
250 {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0},
251 {0,0,0,0,0}
254 static JSBool
255 namespace_toString(JSContext *cx, uintN argc, jsval *vp)
257 JSXMLNamespace *ns;
259 ns = (JSXMLNamespace *)
260 JS_GetInstancePrivate(cx,
261 JS_THIS_OBJECT(cx, vp),
262 &js_NamespaceClass.base,
263 vp + 2);
264 if (!ns)
265 return JS_FALSE;
267 *vp = STRING_TO_JSVAL(ns->uri);
268 return JS_TRUE;
271 static JSFunctionSpec namespace_methods[] = {
272 JS_FN(js_toString_str, namespace_toString, 0,0),
273 JS_FS_END
276 JSXMLNamespace *
277 js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri,
278 JSBool declared)
280 JSXMLNamespace *ns;
282 ns = (JSXMLNamespace *)
283 js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace));
284 if (!ns)
285 return NULL;
286 ns->object = NULL;
287 ns->prefix = prefix;
288 ns->uri = uri;
289 ns->declared = declared;
290 METER(xml_stats.namespace);
291 METER(xml_stats.livenamespace);
292 return ns;
295 void
296 js_TraceXMLNamespace(JSTracer *trc, JSXMLNamespace *ns)
298 if (ns->object)
299 JS_CALL_OBJECT_TRACER(trc, ns->object, "object");
300 if (ns->prefix)
301 JS_CALL_STRING_TRACER(trc, ns->prefix, "prefix");
302 if (ns->uri)
303 JS_CALL_STRING_TRACER(trc, ns->uri, "uri");
306 void
307 js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
309 UNMETER(xml_stats.livenamespace);
312 JSObject *
313 js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri,
314 JSBool declared)
316 JSXMLNamespace *ns;
317 JSTempValueRooter tvr;
318 JSObject *obj;
320 ns = js_NewXMLNamespace(cx, prefix, uri, declared);
321 if (!ns)
322 return NULL;
324 JS_PUSH_TEMP_ROOT_NAMESPACE(cx, ns, &tvr);
325 obj = js_GetXMLNamespaceObject(cx, ns);
326 JS_POP_TEMP_ROOT(cx, &tvr);
327 return obj;
330 JSObject *
331 js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns)
333 JSObject *obj;
335 obj = ns->object;
336 if (obj) {
337 JS_ASSERT(JS_GetPrivate(cx, obj) == ns);
338 return obj;
340 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
341 if (!obj || !JS_SetPrivate(cx, obj, ns)) {
342 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
343 return NULL;
345 ns->object = obj;
346 METER(xml_stats.namespaceobj);
347 METER(xml_stats.livenamespaceobj);
348 return obj;
352 * QName class and library functions.
354 enum qname_tinyid {
355 QNAME_URI = -1,
356 QNAME_LOCALNAME = -2
359 static JSBool
360 qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
362 JSXMLQName *qn;
364 if (!JSVAL_IS_INT(id))
365 return JS_TRUE;
367 qn = (JSXMLQName *)
368 JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL);
369 if (!qn)
370 return JS_TRUE;
372 switch (JSVAL_TO_INT(id)) {
373 case QNAME_URI:
374 *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL;
375 break;
376 case QNAME_LOCALNAME:
377 *vp = STRING_TO_JSVAL(qn->localName);
378 break;
380 return JS_TRUE;
383 static void
384 qname_finalize(JSContext *cx, JSObject *obj)
386 JSXMLQName *qn;
388 qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
389 if (!qn)
390 return;
391 JS_ASSERT(qn->object == obj);
392 qn->object = NULL;
393 UNMETER(xml_stats.liveqnameobj);
396 static void
397 anyname_finalize(JSContext* cx, JSObject* obj)
399 JSRuntime *rt;
401 /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
402 rt = cx->runtime;
403 if (rt->anynameObject == obj)
404 rt->anynameObject = NULL;
406 qname_finalize(cx, obj);
409 static void
410 qname_trace(JSTracer *trc, JSObject *obj)
412 JSXMLQName *qn;
414 qn = (JSXMLQName *) JS_GetPrivate(trc->context, obj);
415 if (qn)
416 JS_CALL_TRACER(trc, qn, JSTRACE_QNAME, "private");
419 static JSBool
420 qname_identity(JSXMLQName *qna, JSXMLQName *qnb)
422 if (!qna->uri ^ !qnb->uri)
423 return JS_FALSE;
424 if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri))
425 return JS_FALSE;
426 return js_EqualStrings(qna->localName, qnb->localName);
429 static JSBool
430 qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
432 JSXMLQName *qn, *qn2;
433 JSObject *obj2;
435 qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
436 JS_ASSERT(JSVAL_IS_OBJECT(v));
437 obj2 = JSVAL_TO_OBJECT(v);
438 if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) {
439 *bp = JS_FALSE;
440 } else {
441 qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2);
442 *bp = qname_identity(qn, qn2);
444 return JS_TRUE;
447 JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
448 { "QName",
449 JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
450 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
451 JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL,
452 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize,
453 NULL, NULL, NULL, NULL,
454 NULL, NULL, JS_CLASS_TRACE(qname_trace), NULL },
455 qname_equality, NULL, NULL, NULL,
456 NULL, NULL, NULL, NULL
460 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
461 * are like QName, except that they have no property getters. They share the
462 * qname_toString method, and therefore are exposed as constructable objects
463 * in this implementation.
465 JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
466 js_AttributeName_str,
467 JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE |
468 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
469 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
470 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize,
471 NULL, NULL, NULL, NULL,
472 NULL, NULL, JS_CLASS_TRACE(qname_trace), NULL
475 JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
476 js_AnyName_str,
477 JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE |
478 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
479 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
480 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize,
481 NULL, NULL, NULL, NULL,
482 NULL, NULL, JS_CLASS_TRACE(qname_trace), NULL
485 #define QNAME_ATTRS \
486 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
488 static JSPropertySpec qname_props[] = {
489 {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0},
490 {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0},
491 {0,0,0,0,0}
494 static JSBool
495 qname_toString(JSContext *cx, uintN argc, jsval *vp)
497 JSObject *obj;
498 JSClass *clasp;
499 JSXMLQName *qn;
500 JSString *str, *qualstr;
501 size_t length;
502 jschar *chars;
504 obj = JS_THIS_OBJECT(cx, vp);
505 if (!obj)
506 return JS_FALSE;
507 clasp = OBJ_GET_CLASS(cx, obj);
508 if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) {
509 qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
510 } else {
511 qn = (JSXMLQName *)
512 JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, vp + 2);
513 if (!qn)
514 return JS_FALSE;
517 if (!qn->uri) {
518 /* No uri means wildcard qualifier. */
519 str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
520 } else if (IS_EMPTY(qn->uri)) {
521 /* Empty string for uri means localName is in no namespace. */
522 str = cx->runtime->emptyString;
523 } else {
524 qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
525 str = js_ConcatStrings(cx, qn->uri, qualstr);
526 if (!str)
527 return JS_FALSE;
529 str = js_ConcatStrings(cx, str, qn->localName);
530 if (!str)
531 return JS_FALSE;
533 if (str && clasp == &js_AttributeNameClass) {
534 length = JSSTRING_LENGTH(str);
535 chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
536 if (!chars)
537 return JS_FALSE;
538 *chars = '@';
539 js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
540 chars[++length] = 0;
541 str = js_NewString(cx, chars, length);
542 if (!str) {
543 JS_free(cx, chars);
544 return JS_FALSE;
548 *vp = STRING_TO_JSVAL(str);
549 return JS_TRUE;
552 static JSFunctionSpec qname_methods[] = {
553 JS_FN(js_toString_str, qname_toString, 0,0),
554 JS_FS_END
557 JSXMLQName *
558 js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix,
559 JSString *localName)
561 JSXMLQName *qn;
563 qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName));
564 if (!qn)
565 return NULL;
566 qn->object = NULL;
567 qn->uri = uri;
568 qn->prefix = prefix;
569 qn->localName = localName;
570 METER(xml_stats.qname);
571 METER(xml_stats.liveqname);
572 return qn;
575 void
576 js_TraceXMLQName(JSTracer *trc, JSXMLQName *qn)
578 if (qn->object)
579 JS_CALL_OBJECT_TRACER(trc, qn->object, "object");
580 if (qn->uri)
581 JS_CALL_STRING_TRACER(trc, qn->uri, "uri");
582 if (qn->prefix)
583 JS_CALL_STRING_TRACER(trc, qn->prefix, "prefix");
584 if (qn->localName)
585 JS_CALL_STRING_TRACER(trc, qn->localName, "localName");
588 void
589 js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn)
591 UNMETER(xml_stats.liveqname);
594 JSObject *
595 js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix,
596 JSString *localName)
598 JSXMLQName *qn;
599 JSTempValueRooter tvr;
600 JSObject *obj;
602 qn = js_NewXMLQName(cx, uri, prefix, localName);
603 if (!qn)
604 return NULL;
605 JS_PUSH_TEMP_ROOT_QNAME(cx, qn, &tvr);
606 obj = js_GetXMLQNameObject(cx, qn);
607 JS_POP_TEMP_ROOT(cx, &tvr);
608 return obj;
611 JSObject *
612 js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn)
614 JSObject *obj;
616 obj = qn->object;
617 if (obj) {
618 JS_ASSERT(JS_GetPrivate(cx, obj) == qn);
619 return obj;
621 obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL, 0);
622 if (!obj || !JS_SetPrivate(cx, obj, qn)) {
623 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
624 return NULL;
626 qn->object = obj;
627 METER(xml_stats.qnameobj);
628 METER(xml_stats.liveqnameobj);
629 return obj;
632 JSObject *
633 js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn)
635 JSObject *obj;
637 obj = qn->object;
638 if (obj) {
639 if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass)
640 return obj;
641 qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
642 if (!qn)
643 return NULL;
646 obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL, 0);
647 if (!obj || !JS_SetPrivate(cx, obj, qn)) {
648 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
649 return NULL;
652 qn->object = obj;
653 METER(xml_stats.qnameobj);
654 METER(xml_stats.liveqnameobj);
655 return obj;
658 JSObject *
659 js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
661 jsval argv[2];
664 * ECMA-357 11.1.2,
665 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
666 * production, step 2.
668 if (!JSVAL_IS_PRIMITIVE(nsval) &&
669 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
670 nsval = JSVAL_NULL;
673 argv[0] = nsval;
674 argv[1] = lnval;
675 return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
678 static JSBool
679 IsXMLName(const jschar *cp, size_t n)
681 JSBool rv;
682 jschar c;
684 rv = JS_FALSE;
685 if (n != 0 && JS_ISXMLNSSTART(*cp)) {
686 while (--n != 0) {
687 c = *++cp;
688 if (!JS_ISXMLNS(c))
689 return rv;
691 rv = JS_TRUE;
693 return rv;
696 JSBool
697 js_IsXMLName(JSContext *cx, jsval v)
699 JSClass *clasp;
700 JSXMLQName *qn;
701 JSString *name;
702 JSErrorReporter older;
705 * Inline specialization of the QName constructor called with v passed as
706 * the only argument, to compute the localName for the constructed qname,
707 * without actually allocating the object or computing its uri and prefix.
708 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
710 if (!JSVAL_IS_PRIMITIVE(v) &&
711 (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)),
712 clasp == &js_QNameClass.base ||
713 clasp == &js_AttributeNameClass ||
714 clasp == &js_AnyNameClass)) {
715 qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
716 name = qn->localName;
717 } else {
718 older = JS_SetErrorReporter(cx, NULL);
719 name = js_ValueToString(cx, v);
720 JS_SetErrorReporter(cx, older);
721 if (!name) {
722 JS_ClearPendingException(cx);
723 return JS_FALSE;
727 return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
731 * When argc is -1, it indicates argv is empty but the code should behave as
732 * if argc is 1 and argv[0] is JSVAL_VOID.
734 static JSBool
735 NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
736 jsval *rval)
738 jsval urival, prefixval;
739 JSObject *uriobj;
740 JSBool isNamespace, isQName;
741 JSClass *clasp;
742 JSString *empty, *prefix;
743 JSXMLNamespace *ns, *ns2;
744 JSXMLQName *qn;
746 isNamespace = isQName = JS_FALSE;
747 #ifdef __GNUC__ /* suppress bogus gcc warnings */
748 uriobj = NULL;
749 #endif
750 if (argc <= 0) {
751 urival = JSVAL_VOID;
752 } else {
753 urival = argv[argc > 1];
754 if (!JSVAL_IS_PRIMITIVE(urival)) {
755 uriobj = JSVAL_TO_OBJECT(urival);
756 clasp = OBJ_GET_CLASS(cx, uriobj);
757 isNamespace = (clasp == &js_NamespaceClass.base);
758 isQName = (clasp == &js_QNameClass.base);
762 if (!obj) {
763 /* Namespace called as function. */
764 if (argc == 1 && isNamespace) {
765 /* Namespace called with one Namespace argument is identity. */
766 *rval = urival;
767 return JS_TRUE;
770 /* Create and return a new QName object exactly as if constructed. */
771 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
772 if (!obj)
773 return JS_FALSE;
774 *rval = OBJECT_TO_JSVAL(obj);
776 METER(xml_stats.namespaceobj);
777 METER(xml_stats.livenamespaceobj);
780 * Create and connect private data to rooted obj early, so we don't have
781 * to worry about rooting string newborns hanging off of the private data
782 * further below.
784 empty = cx->runtime->emptyString;
785 ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE);
786 if (!ns)
787 return JS_FALSE;
788 if (!JS_SetPrivate(cx, obj, ns))
789 return JS_FALSE;
790 ns->object = obj;
792 if (argc == 1 || argc == -1) {
793 if (isNamespace) {
794 ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj);
795 ns->uri = ns2->uri;
796 ns->prefix = ns2->prefix;
797 } else if (isQName &&
798 (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
799 ns->uri = qn->uri;
800 ns->prefix = qn->prefix;
801 } else {
802 ns->uri = js_ValueToString(cx, urival);
803 if (!ns->uri)
804 return JS_FALSE;
806 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
807 if (!IS_EMPTY(ns->uri))
808 ns->prefix = NULL;
810 } else if (argc == 2) {
811 if (isQName &&
812 (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
813 ns->uri = qn->uri;
814 } else {
815 ns->uri = js_ValueToString(cx, urival);
816 if (!ns->uri)
817 return JS_FALSE;
820 prefixval = argv[0];
821 if (IS_EMPTY(ns->uri)) {
822 if (!JSVAL_IS_VOID(prefixval)) {
823 prefix = js_ValueToString(cx, prefixval);
824 if (!prefix)
825 return JS_FALSE;
826 if (!IS_EMPTY(prefix)) {
827 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
828 JSMSG_BAD_XML_NAMESPACE,
829 js_ValueToPrintableString(cx,
830 STRING_TO_JSVAL(prefix)));
831 return JS_FALSE;
834 } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
835 /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */
836 ns->prefix = NULL;
837 } else {
838 prefix = js_ValueToString(cx, prefixval);
839 if (!prefix)
840 return JS_FALSE;
841 ns->prefix = prefix;
845 return JS_TRUE;
848 static JSBool
849 Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
851 return NamespaceHelper(cx,
852 (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
853 argc, argv, rval);
857 * When argc is -1, it indicates argv is empty but the code should behave as
858 * if argc is 1 and argv[0] is JSVAL_VOID.
860 static JSBool
861 QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
862 jsval *argv, jsval *rval)
864 jsval nameval, nsval;
865 JSBool isQName, isNamespace;
866 JSXMLQName *qn;
867 JSString *uri, *prefix, *name;
868 JSObject *nsobj;
869 JSXMLNamespace *ns;
871 JS_ASSERT(clasp == &js_QNameClass.base ||
872 clasp == &js_AttributeNameClass);
873 if (argc <= 0) {
874 nameval = JSVAL_VOID;
875 isQName = JS_FALSE;
876 } else {
877 nameval = argv[argc > 1];
878 isQName =
879 !JSVAL_IS_PRIMITIVE(nameval) &&
880 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
883 if (!obj) {
884 /* QName called as function. */
885 if (argc == 1 && isQName) {
886 /* QName called with one QName argument is identity. */
887 *rval = nameval;
888 return JS_TRUE;
892 * Create and return a new QName or AttributeName object exactly as if
893 * constructed.
895 obj = js_NewObject(cx, clasp, NULL, NULL, 0);
896 if (!obj)
897 return JS_FALSE;
898 *rval = OBJECT_TO_JSVAL(obj);
900 METER(xml_stats.qnameobj);
901 METER(xml_stats.liveqnameobj);
903 if (isQName) {
904 /* If namespace is not specified and name is a QName, clone it. */
905 qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval));
906 if (argc == 1) {
907 uri = qn->uri;
908 prefix = qn->prefix;
909 name = qn->localName;
910 goto out;
913 /* Namespace and qname were passed -- use the qname's localName. */
914 nameval = STRING_TO_JSVAL(qn->localName);
917 if (argc == 0) {
918 name = cx->runtime->emptyString;
919 } else if (argc < 0) {
920 name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
921 } else {
922 name = js_ValueToString(cx, nameval);
923 if (!name)
924 return JS_FALSE;
925 argv[argc > 1] = STRING_TO_JSVAL(name);
928 if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
929 nsval = argv[0];
930 } else if (IS_STAR(name)) {
931 nsval = JSVAL_NULL;
932 } else {
933 if (!js_GetDefaultXMLNamespace(cx, &nsval))
934 return JS_FALSE;
935 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
936 JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) ==
937 &js_NamespaceClass.base);
940 if (JSVAL_IS_NULL(nsval)) {
941 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
942 uri = prefix = NULL;
943 } else {
945 * Inline specialization of the Namespace constructor called with
946 * nsval passed as the only argument, to compute the uri and prefix
947 * for the constructed namespace, without actually allocating the
948 * object or computing other members. See ECMA-357 13.3.2 6(a) and
949 * 13.2.2.
951 isNamespace = isQName = JS_FALSE;
952 if (!JSVAL_IS_PRIMITIVE(nsval)) {
953 nsobj = JSVAL_TO_OBJECT(nsval);
954 clasp = OBJ_GET_CLASS(cx, nsobj);
955 isNamespace = (clasp == &js_NamespaceClass.base);
956 isQName = (clasp == &js_QNameClass.base);
958 #ifdef __GNUC__ /* suppress bogus gcc warnings */
959 else nsobj = NULL;
960 #endif
962 if (isNamespace) {
963 ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
964 uri = ns->uri;
965 prefix = ns->prefix;
966 } else if (isQName &&
967 (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) {
968 JS_ASSERT(argc > 1);
969 uri = qn->uri;
970 prefix = qn->prefix;
971 } else {
972 JS_ASSERT(argc > 1);
973 uri = js_ValueToString(cx, nsval);
974 if (!uri)
975 return JS_FALSE;
976 argv[0] = STRING_TO_JSVAL(uri); /* local root */
978 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
979 prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
983 out:
984 qn = js_NewXMLQName(cx, uri, prefix, name);
985 if (!qn)
986 return JS_FALSE;
987 obj->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(qn);
988 qn->object = obj;
989 return JS_TRUE;
992 static JSBool
993 QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
995 return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
996 &js_QNameClass.base, argc, argv, rval);
999 static JSBool
1000 AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1001 jsval *rval)
1003 return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
1004 &js_AttributeNameClass, argc, argv, rval);
1008 * XMLArray library functions.
1010 static JSBool
1011 namespace_identity(const void *a, const void *b)
1013 const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
1014 const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
1016 if (nsa->prefix && nsb->prefix) {
1017 if (!js_EqualStrings(nsa->prefix, nsb->prefix))
1018 return JS_FALSE;
1019 } else {
1020 if (nsa->prefix || nsb->prefix)
1021 return JS_FALSE;
1023 return js_EqualStrings(nsa->uri, nsb->uri);
1026 static JSBool
1027 attr_identity(const void *a, const void *b)
1029 const JSXML *xmla = (const JSXML *) a;
1030 const JSXML *xmlb = (const JSXML *) b;
1032 return qname_identity(xmla->name, xmlb->name);
1035 static void
1036 XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
1038 JSXMLArrayCursor *next;
1040 cursor->array = array;
1041 cursor->index = 0;
1042 next = cursor->next = array->cursors;
1043 if (next)
1044 next->prevp = &cursor->next;
1045 cursor->prevp = &array->cursors;
1046 array->cursors = cursor;
1047 cursor->root = NULL;
1050 static void
1051 XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
1053 JSXMLArrayCursor *next;
1055 if (!cursor->array)
1056 return;
1057 next = cursor->next;
1058 if (next)
1059 next->prevp = cursor->prevp;
1060 *cursor->prevp = next;
1061 cursor->array = NULL;
1064 static void *
1065 XMLArrayCursorNext(JSXMLArrayCursor *cursor)
1067 JSXMLArray *array;
1069 array = cursor->array;
1070 if (!array || cursor->index >= array->length)
1071 return NULL;
1072 return cursor->root = array->vector[cursor->index++];
1075 static void *
1076 XMLArrayCursorItem(JSXMLArrayCursor *cursor)
1078 JSXMLArray *array;
1080 array = cursor->array;
1081 if (!array || cursor->index >= array->length)
1082 return NULL;
1083 return cursor->root = array->vector[cursor->index];
1086 static void
1087 XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
1089 void *root;
1090 #ifdef DEBUG
1091 size_t index = 0;
1092 #endif
1094 for (; cursor; cursor = cursor->next) {
1095 root = cursor->root;
1096 JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
1097 js_CallValueTracerIfGCThing(trc, (jsval)root);
1101 /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
1102 static JSBool
1103 XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
1105 void **vector;
1107 if (capacity == 0) {
1108 /* We could let realloc(p, 0) free this, but purify gets confused. */
1109 if (array->vector)
1110 free(array->vector);
1111 vector = NULL;
1112 } else {
1113 if (
1114 #if JS_BITS_PER_WORD == 32
1115 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1116 #endif
1117 !(vector = (void **)
1118 realloc(array->vector, capacity * sizeof(void *)))) {
1119 if (cx)
1120 JS_ReportOutOfMemory(cx);
1121 return JS_FALSE;
1124 array->capacity = JSXML_PRESET_CAPACITY | capacity;
1125 array->vector = vector;
1126 return JS_TRUE;
1129 static void
1130 XMLArrayTrim(JSXMLArray *array)
1132 if (array->capacity & JSXML_PRESET_CAPACITY)
1133 return;
1134 if (array->length < array->capacity)
1135 XMLArraySetCapacity(NULL, array, array->length);
1138 static JSBool
1139 XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
1141 array->length = array->capacity = 0;
1142 array->vector = NULL;
1143 array->cursors = NULL;
1144 return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
1147 static void
1148 XMLArrayFinish(JSContext *cx, JSXMLArray *array)
1150 JSXMLArrayCursor *cursor;
1152 JS_free(cx, array->vector);
1154 while ((cursor = array->cursors) != NULL)
1155 XMLArrayCursorFinish(cursor);
1157 #ifdef DEBUG
1158 memset(array, 0xd5, sizeof *array);
1159 #endif
1162 #define XML_NOT_FOUND ((uint32) -1)
1164 static uint32
1165 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
1167 void **vector;
1168 uint32 i, n;
1170 /* The identity op must not reallocate array->vector. */
1171 vector = array->vector;
1172 if (identity) {
1173 for (i = 0, n = array->length; i < n; i++) {
1174 if (identity(vector[i], elt))
1175 return i;
1177 } else {
1178 for (i = 0, n = array->length; i < n; i++) {
1179 if (vector[i] == elt)
1180 return i;
1183 return XML_NOT_FOUND;
1187 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1188 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1189 * should be greater than increment.
1191 #define LINEAR_THRESHOLD 256
1192 #define LINEAR_INCREMENT 32
1194 static JSBool
1195 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
1197 uint32 capacity, i;
1198 int log2;
1199 void **vector;
1201 if (index >= array->length) {
1202 if (index >= JSXML_CAPACITY(array)) {
1203 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1204 capacity = index + 1;
1205 if (index >= LINEAR_THRESHOLD) {
1206 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
1207 } else {
1208 JS_CEILING_LOG2(log2, capacity);
1209 capacity = JS_BIT(log2);
1211 if (
1212 #if JS_BITS_PER_WORD == 32
1213 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1214 #endif
1215 !(vector = (void **)
1216 realloc(array->vector, capacity * sizeof(void *)))) {
1217 JS_ReportOutOfMemory(cx);
1218 return JS_FALSE;
1220 array->capacity = capacity;
1221 array->vector = vector;
1222 for (i = array->length; i < index; i++)
1223 vector[i] = NULL;
1225 array->length = index + 1;
1228 array->vector[index] = elt;
1229 return JS_TRUE;
1232 static JSBool
1233 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1235 uint32 j;
1236 JSXMLArrayCursor *cursor;
1238 j = array->length;
1239 JS_ASSERT(i <= j);
1240 if (!XMLArraySetCapacity(cx, array, j + n))
1241 return JS_FALSE;
1243 array->length = j + n;
1244 JS_ASSERT(n != (uint32)-1);
1245 while (j != i) {
1246 --j;
1247 array->vector[j + n] = array->vector[j];
1250 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1251 if (cursor->index > i)
1252 cursor->index += n;
1254 return JS_TRUE;
1257 static void *
1258 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1260 uint32 length;
1261 void **vector, *elt;
1262 JSXMLArrayCursor *cursor;
1264 length = array->length;
1265 if (index >= length)
1266 return NULL;
1268 vector = array->vector;
1269 elt = vector[index];
1270 if (compress) {
1271 while (++index < length)
1272 vector[index-1] = vector[index];
1273 array->length = length - 1;
1274 array->capacity = JSXML_CAPACITY(array);
1275 } else {
1276 vector[index] = NULL;
1279 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1280 if (cursor->index > index)
1281 --cursor->index;
1283 return elt;
1286 static void
1287 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1289 void **vector;
1291 JS_ASSERT(!array->cursors);
1292 if (length >= array->length)
1293 return;
1295 if (length == 0) {
1296 if (array->vector)
1297 free(array->vector);
1298 vector = NULL;
1299 } else {
1300 vector = (void **) realloc(array->vector, length * sizeof(void *));
1301 if (!vector)
1302 return;
1305 if (array->length > length)
1306 array->length = length;
1307 array->capacity = length;
1308 array->vector = vector;
1311 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1312 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1313 XML_NOT_FOUND)
1314 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1315 ? (t *) (a)->vector[i] \
1316 : NULL)
1317 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1318 if ((a)->length <= (i)) \
1319 (a)->length = (i) + 1; \
1320 ((a)->vector[i] = (void *)(e)); \
1321 JS_END_MACRO
1322 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1323 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1324 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1325 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1326 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1329 * Define XML setting property strings and constants early, so everyone can
1330 * use the same names and their magic numbers (tinyids, flags).
1332 static const char js_ignoreComments_str[] = "ignoreComments";
1333 static const char js_ignoreProcessingInstructions_str[]
1334 = "ignoreProcessingInstructions";
1335 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1336 static const char js_prettyPrinting_str[] = "prettyPrinting";
1337 static const char js_prettyIndent_str[] = "prettyIndent";
1340 * NB: These XML static property tinyids must
1341 * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1342 * (b) index their corresponding xml_static_props array elements.
1343 * Don't change 'em!
1345 enum xml_static_tinyid {
1346 XML_IGNORE_COMMENTS,
1347 XML_IGNORE_PROCESSING_INSTRUCTIONS,
1348 XML_IGNORE_WHITESPACE,
1349 XML_PRETTY_PRINTING,
1350 XML_PRETTY_INDENT
1353 static JSBool
1354 xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1356 return JS_TRUE;
1359 static JSBool
1360 xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1362 uint8 flag;
1364 JS_ASSERT(JSVAL_IS_INT(id));
1366 flag = JS_BIT(JSVAL_TO_INT(id));
1367 if (js_ValueToBoolean(*vp))
1368 cx->xmlSettingFlags |= flag;
1369 else
1370 cx->xmlSettingFlags &= ~flag;
1371 return JS_TRUE;
1374 static JSPropertySpec xml_static_props[] = {
1375 {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT,
1376 xml_setting_getter, xml_setting_setter},
1377 {js_ignoreProcessingInstructions_str,
1378 XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
1379 xml_setting_getter, xml_setting_setter},
1380 {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
1381 xml_setting_getter, xml_setting_setter},
1382 {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT,
1383 xml_setting_getter, xml_setting_setter},
1384 {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT,
1385 xml_setting_getter, NULL},
1386 {0,0,0,0,0}
1389 /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1390 #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1391 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1392 JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1393 #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1394 #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1395 #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1398 * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1399 * This flag means a couple of things:
1401 * - The top JSXML created for a parse tree must have an object owning it.
1403 * - That the default namespace normally inherited from the temporary
1404 * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1405 * string must, in the case of a precompiled XML object tree, inherit via
1406 * ad-hoc code in ParseNodeToXML.
1408 * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1410 #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1412 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1413 #define IS_XML(str) \
1414 (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
1416 #define IS_XMLNS(str) \
1417 (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1419 #define IS_XML_CHARS(chars) \
1420 (JS_TOLOWER((chars)[0]) == 'x' && \
1421 JS_TOLOWER((chars)[1]) == 'm' && \
1422 JS_TOLOWER((chars)[2]) == 'l')
1424 #define HAS_NS_AFTER_XML(chars) \
1425 (JS_TOLOWER((chars)[3]) == 'n' && \
1426 JS_TOLOWER((chars)[4]) == 's')
1428 #define IS_XMLNS_CHARS(chars) \
1429 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1431 #define STARTS_WITH_XML(chars,length) \
1432 (length >= 3 && IS_XML_CHARS(chars))
1434 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1435 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1437 static JSXMLQName *
1438 ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
1439 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1441 JSString *str, *uri, *prefix, *localName;
1442 size_t length, offset;
1443 const jschar *start, *limit, *colon;
1444 uint32 n;
1445 JSXMLNamespace *ns;
1447 JS_ASSERT(pn->pn_arity == PN_NULLARY);
1448 str = ATOM_TO_STRING(pn->pn_atom);
1449 JSSTRING_CHARS_AND_LENGTH(str, start, length);
1450 JS_ASSERT(length != 0 && *start != '@');
1451 JS_ASSERT(length != 1 || *start != '*');
1453 uri = cx->runtime->emptyString;
1454 limit = start + length;
1455 colon = js_strchr_limit(start, ':', limit);
1456 if (colon) {
1457 offset = PTRDIFF(colon, start, jschar);
1458 prefix = js_NewDependentString(cx, str, 0, offset);
1459 if (!prefix)
1460 return NULL;
1462 if (STARTS_WITH_XML(start, offset)) {
1463 if (offset == 3) {
1464 uri = JS_InternString(cx, xml_namespace_str);
1465 if (!uri)
1466 return NULL;
1467 } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1468 uri = JS_InternString(cx, xmlns_namespace_str);
1469 if (!uri)
1470 return NULL;
1471 } else {
1472 uri = NULL;
1474 } else {
1475 uri = NULL;
1476 n = inScopeNSes->length;
1477 while (n != 0) {
1478 --n;
1479 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
1480 if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) {
1481 uri = ns->uri;
1482 break;
1487 if (!uri) {
1488 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
1489 JSREPORT_ERROR,
1490 JSMSG_BAD_XML_NAMESPACE,
1491 js_ValueToPrintableString(cx,
1492 STRING_TO_JSVAL(prefix)));
1493 return NULL;
1496 localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1));
1497 if (!localName)
1498 return NULL;
1499 } else {
1500 if (isAttributeName) {
1502 * An unprefixed attribute is not in any namespace, so set prefix
1503 * as well as uri to the empty string.
1505 prefix = uri;
1506 } else {
1508 * Loop from back to front looking for the closest declared default
1509 * namespace.
1511 n = inScopeNSes->length;
1512 while (n != 0) {
1513 --n;
1514 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
1515 if (!ns->prefix || IS_EMPTY(ns->prefix)) {
1516 uri = ns->uri;
1517 break;
1520 prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
1522 localName = str;
1525 return js_NewXMLQName(cx, uri, prefix, localName);
1528 static JSString *
1529 ChompXMLWhitespace(JSContext *cx, JSString *str)
1531 size_t length, newlength, offset;
1532 const jschar *cp, *start, *end;
1533 jschar c;
1535 JSSTRING_CHARS_AND_LENGTH(str, start, length);
1536 for (cp = start, end = cp + length; cp < end; cp++) {
1537 c = *cp;
1538 if (!JS_ISXMLSPACE(c))
1539 break;
1541 while (end > cp) {
1542 c = end[-1];
1543 if (!JS_ISXMLSPACE(c))
1544 break;
1545 --end;
1547 newlength = PTRDIFF(end, cp, jschar);
1548 if (newlength == length)
1549 return str;
1550 offset = PTRDIFF(cp, start, jschar);
1551 return js_NewDependentString(cx, str, offset, newlength);
1554 static JSXML *
1555 ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
1556 JSXMLArray *inScopeNSes, uintN flags)
1558 JSXML *xml, *kid, *attr, *attrj;
1559 JSString *str;
1560 uint32 length, n, i, j;
1561 JSParseNode *pn2, *pn3, *head, **pnp;
1562 JSXMLNamespace *ns;
1563 JSXMLQName *qn, *attrjqn;
1564 JSXMLClass xml_class;
1565 int stackDummy;
1567 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1568 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1569 JSMSG_OVER_RECURSED);
1570 return NULL;
1573 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1576 * Cases return early to avoid common code that gets an outermost xml's
1577 * object, which protects GC-things owned by xml and its descendants from
1578 * garbage collection.
1580 xml = NULL;
1581 if (!js_EnterLocalRootScope(cx))
1582 return NULL;
1583 switch (pn->pn_type) {
1584 case TOK_XMLELEM:
1585 length = inScopeNSes->length;
1586 pn2 = pn->pn_head;
1587 xml = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1588 if (!xml)
1589 goto fail;
1591 flags &= ~XSF_PRECOMPILED_ROOT;
1592 n = pn->pn_count;
1593 JS_ASSERT(n >= 2);
1594 n -= 2;
1595 if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1596 goto fail;
1598 i = 0;
1599 while ((pn2 = pn2->pn_next) != NULL) {
1600 if (!pn2->pn_next) {
1601 /* Don't append the end tag! */
1602 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1603 break;
1606 if ((flags & XSF_IGNORE_WHITESPACE) &&
1607 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1608 --n;
1609 continue;
1612 kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1613 if (kid == PN2X_SKIP_CHILD) {
1614 --n;
1615 continue;
1618 if (!kid)
1619 goto fail;
1621 /* Store kid in xml right away, to protect it from GC. */
1622 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1623 kid->parent = xml;
1624 ++i;
1626 /* XXX where is this documented in an XML spec, or in E4X? */
1627 if ((flags & XSF_IGNORE_WHITESPACE) &&
1628 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1629 str = ChompXMLWhitespace(cx, kid->xml_value);
1630 if (!str)
1631 goto fail;
1632 kid->xml_value = str;
1636 JS_ASSERT(i == n);
1637 if (n < pn->pn_count - 2)
1638 XMLArrayTrim(&xml->xml_kids);
1639 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1640 break;
1642 case TOK_XMLLIST:
1643 xml = js_NewXML(cx, JSXML_CLASS_LIST);
1644 if (!xml)
1645 goto fail;
1647 n = pn->pn_count;
1648 if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1649 goto fail;
1651 i = 0;
1652 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1654 * Always ignore insignificant whitespace in lists -- we shouldn't
1655 * condition this on an XML.ignoreWhitespace setting when the list
1656 * constructor is XMLList (note XML/XMLList unification hazard).
1658 if (pn2->pn_type == TOK_XMLSPACE) {
1659 --n;
1660 continue;
1663 kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1664 if (kid == PN2X_SKIP_CHILD) {
1665 --n;
1666 continue;
1669 if (!kid)
1670 goto fail;
1672 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1673 ++i;
1676 if (n < pn->pn_count)
1677 XMLArrayTrim(&xml->xml_kids);
1678 break;
1680 case TOK_XMLSTAGO:
1681 case TOK_XMLPTAGC:
1682 length = inScopeNSes->length;
1683 pn2 = pn->pn_head;
1684 JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1685 if (pn2->pn_arity == PN_LIST)
1686 goto syntax;
1688 xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1689 if (!xml)
1690 goto fail;
1692 /* First pass: check syntax and process namespace declarations. */
1693 JS_ASSERT(pn->pn_count >= 1);
1694 n = pn->pn_count - 1;
1695 pnp = &pn2->pn_next;
1696 head = *pnp;
1697 while ((pn2 = *pnp) != NULL) {
1698 size_t length;
1699 const jschar *chars;
1701 if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1702 goto syntax;
1704 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1705 for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1706 if (pn3->pn_atom == pn2->pn_atom) {
1707 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
1708 JSREPORT_ERROR,
1709 JSMSG_DUPLICATE_XML_ATTR,
1710 js_ValueToPrintableString(cx,
1711 ATOM_KEY(pn2->pn_atom)));
1712 goto fail;
1716 str = ATOM_TO_STRING(pn2->pn_atom);
1717 pn2 = pn2->pn_next;
1718 JS_ASSERT(pn2);
1719 if (pn2->pn_type != TOK_XMLATTR)
1720 goto syntax;
1722 JSSTRING_CHARS_AND_LENGTH(str, chars, length);
1723 if (length >= 5 &&
1724 IS_XMLNS_CHARS(chars) &&
1725 (length == 5 || chars[5] == ':')) {
1726 JSString *uri, *prefix;
1728 uri = ATOM_TO_STRING(pn2->pn_atom);
1729 if (length == 5) {
1730 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1731 prefix = cx->runtime->emptyString;
1732 } else {
1733 prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1734 if (!prefix)
1735 goto fail;
1739 * Once the new ns is appended to xml->xml_namespaces, it is
1740 * protected from GC by the object that owns xml -- which is
1741 * either xml->object if outermost, or the object owning xml's
1742 * oldest ancestor if !outermost.
1744 ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1745 if (!ns)
1746 goto fail;
1749 * Don't add a namespace that's already in scope. If someone
1750 * extracts a child property from its parent via [[Get]], then
1751 * we enforce the invariant, noted many times in ECMA-357, that
1752 * the child's namespaces form a possibly-improper superset of
1753 * its ancestors' namespaces.
1755 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1756 if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1757 !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1758 goto fail;
1762 JS_ASSERT(n >= 2);
1763 n -= 2;
1764 *pnp = pn2->pn_next;
1765 /* XXXbe recycle pn2 */
1766 continue;
1769 pnp = &pn2->pn_next;
1773 * If called from js_ParseNodeToXMLObject, emulate the effect of the
1774 * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1775 * the String Type" (ECMA-357 10.3.1).
1777 if (flags & XSF_PRECOMPILED_ROOT) {
1778 JS_ASSERT(length >= 1);
1779 ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace);
1780 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
1781 namespace_identity));
1782 ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE);
1783 if (!ns)
1784 goto fail;
1785 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1786 goto fail;
1788 XMLArrayTrim(&xml->xml_namespaces);
1790 /* Second pass: process tag name and attributes, using namespaces. */
1791 pn2 = pn->pn_head;
1792 qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_FALSE);
1793 if (!qn)
1794 goto fail;
1795 xml->name = qn;
1797 JS_ASSERT((n & 1) == 0);
1798 n >>= 1;
1799 if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
1800 goto fail;
1802 for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1803 qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_TRUE);
1804 if (!qn) {
1805 xml->xml_attrs.length = i;
1806 goto fail;
1810 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1811 * this time checking local name and namespace URI.
1813 for (j = 0; j < i; j++) {
1814 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1815 attrjqn = attrj->name;
1816 if (js_EqualStrings(attrjqn->uri, qn->uri) &&
1817 js_EqualStrings(attrjqn->localName, qn->localName)) {
1818 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
1819 JSREPORT_ERROR,
1820 JSMSG_DUPLICATE_XML_ATTR,
1821 js_ValueToPrintableString(cx,
1822 ATOM_KEY(pn2->pn_atom)));
1823 goto fail;
1827 pn2 = pn2->pn_next;
1828 JS_ASSERT(pn2);
1829 JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1831 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1832 if (!attr)
1833 goto fail;
1835 XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1836 attr->parent = xml;
1837 attr->name = qn;
1838 attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1841 /* Point tag closes its own namespace scope. */
1842 if (pn->pn_type == TOK_XMLPTAGC)
1843 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1844 break;
1846 case TOK_XMLSPACE:
1847 case TOK_XMLTEXT:
1848 case TOK_XMLCDATA:
1849 case TOK_XMLCOMMENT:
1850 case TOK_XMLPI:
1851 str = ATOM_TO_STRING(pn->pn_atom);
1852 qn = NULL;
1853 if (pn->pn_type == TOK_XMLCOMMENT) {
1854 if (flags & XSF_IGNORE_COMMENTS)
1855 goto skip_child;
1856 xml_class = JSXML_CLASS_COMMENT;
1857 } else if (pn->pn_type == TOK_XMLPI) {
1858 if (IS_XML(str)) {
1859 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
1860 JSREPORT_ERROR,
1861 JSMSG_RESERVED_ID,
1862 js_ValueToPrintableString(cx,
1863 STRING_TO_JSVAL(str)));
1864 goto fail;
1867 if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1868 goto skip_child;
1870 qn = ParseNodeToQName(cx, pc, pn, inScopeNSes, JS_FALSE);
1871 if (!qn)
1872 goto fail;
1874 str = pn->pn_atom2
1875 ? ATOM_TO_STRING(pn->pn_atom2)
1876 : cx->runtime->emptyString;
1877 xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1878 } else {
1879 /* CDATA section content, or element text. */
1880 xml_class = JSXML_CLASS_TEXT;
1883 xml = js_NewXML(cx, xml_class);
1884 if (!xml)
1885 goto fail;
1886 xml->name = qn;
1887 if (pn->pn_type == TOK_XMLSPACE)
1888 xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1889 xml->xml_value = str;
1890 break;
1892 default:
1893 goto syntax;
1896 js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
1897 if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
1898 return NULL;
1899 return xml;
1901 skip_child:
1902 js_LeaveLocalRootScope(cx);
1903 return PN2X_SKIP_CHILD;
1905 #undef PN2X_SKIP_CHILD
1907 syntax:
1908 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1909 JSMSG_BAD_XML_MARKUP);
1910 fail:
1911 js_LeaveLocalRootScope(cx);
1912 return NULL;
1916 * XML helper, object-ops, and library functions. We start with the helpers,
1917 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1919 static JSBool
1920 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1922 jsval v;
1924 if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
1925 return JS_FALSE;
1926 if (!VALUE_IS_FUNCTION(cx, v)) {
1927 *vp = JSVAL_VOID;
1928 return JS_TRUE;
1930 return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1933 static JSBool
1934 FillSettingsCache(JSContext *cx)
1936 int i;
1937 const char *name;
1938 jsval v;
1940 /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1941 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
1942 name = xml_static_props[i].name;
1943 if (!GetXMLSetting(cx, name, &v))
1944 return JS_FALSE;
1945 if (js_ValueToBoolean(v))
1946 cx->xmlSettingFlags |= JS_BIT(i);
1947 else
1948 cx->xmlSettingFlags &= ~JS_BIT(i);
1951 cx->xmlSettingFlags |= XSF_CACHE_VALID;
1952 return JS_TRUE;
1955 static JSBool
1956 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1958 int i;
1960 if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
1961 return JS_FALSE;
1963 for (i = 0; xml_static_props[i].name; i++) {
1964 if (!strcmp(xml_static_props[i].name, name)) {
1965 *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
1966 return JS_TRUE;
1969 *bp = JS_FALSE;
1970 return JS_TRUE;
1973 static JSBool
1974 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1976 jsval v;
1978 return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1981 static JSBool
1982 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1984 JSBool flag;
1986 /* Just get the first flag to validate the setting flags cache. */
1987 if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
1988 return JS_FALSE;
1989 *flagsp = cx->xmlSettingFlags;
1990 return JS_TRUE;
1993 static JSXML *
1994 ParseXMLSource(JSContext *cx, JSString *src)
1996 jsval nsval;
1997 JSXMLNamespace *ns;
1998 size_t urilen, srclen, length, offset, dstlen;
1999 jschar *chars;
2000 const jschar *srcp, *endp;
2001 JSXML *xml;
2002 JSParseContext pc;
2003 const char *filename;
2004 uintN lineno;
2005 JSStackFrame *fp;
2006 JSOp op;
2007 JSParseNode *pn;
2008 JSXMLArray nsarray;
2009 uintN flags;
2011 static const char prefix[] = "<parent xmlns='";
2012 static const char middle[] = "'>";
2013 static const char suffix[] = "</parent>";
2015 #define constrlen(constr) (sizeof(constr) - 1)
2017 if (!js_GetDefaultXMLNamespace(cx, &nsval))
2018 return NULL;
2019 ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
2021 urilen = JSSTRING_LENGTH(ns->uri);
2022 srclen = JSSTRING_LENGTH(src);
2023 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
2024 constrlen(suffix);
2026 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
2027 if (!chars)
2028 return NULL;
2030 dstlen = length;
2031 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
2032 offset = dstlen;
2033 js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen);
2034 offset += urilen;
2035 dstlen = length - offset + 1;
2036 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
2037 &dstlen);
2038 offset += dstlen;
2039 srcp = JSSTRING_CHARS(src);
2040 js_strncpy(chars + offset, srcp, srclen);
2041 offset += srclen;
2042 dstlen = length - offset + 1;
2043 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
2044 &dstlen);
2045 chars [offset + dstlen] = 0;
2047 xml = NULL;
2048 for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
2049 JS_ASSERT(!fp->script);
2050 filename = NULL;
2051 lineno = 1;
2052 if (fp) {
2053 op = (JSOp) *fp->regs->pc;
2054 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
2055 filename = fp->script->filename;
2056 lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
2057 for (endp = srcp + srclen; srcp < endp; srcp++) {
2058 if (*srcp == '\n')
2059 --lineno;
2064 if (!js_InitParseContext(cx, &pc, NULL, chars, length, NULL,
2065 filename, lineno))
2066 goto out;
2067 pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
2068 if (pn && XMLArrayInit(cx, &nsarray, 1)) {
2069 if (GetXMLSettingFlags(cx, &flags))
2070 xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
2072 XMLArrayFinish(cx, &nsarray);
2074 js_FinishParseContext(cx, &pc);
2076 out:
2077 JS_free(cx, chars);
2078 return xml;
2080 #undef constrlen
2084 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
2086 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
2087 * the constraint:
2089 * for all x belonging to XML:
2090 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
2092 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
2093 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
2095 * Same goes for 10.4.1 Step 7(a).
2097 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
2098 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
2099 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
2100 * undeclared namespaces associated with x not belonging to ancestorNS.
2102 static JSXML *
2103 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
2105 JSXMLNamespace *ns;
2107 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace);
2108 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
2109 if (!ns || !xml)
2110 return xml;
2111 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
2112 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
2113 return NULL;
2114 ns->declared = JS_FALSE;
2116 xml->parent = NULL;
2117 return xml;
2120 static JSObject *
2121 ToXML(JSContext *cx, jsval v)
2123 JSObject *obj;
2124 JSXML *xml;
2125 JSClass *clasp;
2126 JSString *str;
2127 uint32 length;
2129 if (JSVAL_IS_PRIMITIVE(v)) {
2130 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2131 goto bad;
2132 } else {
2133 obj = JSVAL_TO_OBJECT(v);
2134 if (OBJECT_IS_XML(cx, obj)) {
2135 xml = (JSXML *) JS_GetPrivate(cx, obj);
2136 if (xml->xml_class == JSXML_CLASS_LIST) {
2137 if (xml->xml_kids.length != 1)
2138 goto bad;
2139 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
2140 if (xml) {
2141 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
2142 return js_GetXMLObject(cx, xml);
2145 return obj;
2148 clasp = OBJ_GET_CLASS(cx, obj);
2149 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2150 JS_ASSERT(0);
2153 if (clasp != &js_StringClass &&
2154 clasp != &js_NumberClass &&
2155 clasp != &js_BooleanClass) {
2156 goto bad;
2160 str = js_ValueToString(cx, v);
2161 if (!str)
2162 return NULL;
2163 if (IS_EMPTY(str)) {
2164 length = 0;
2165 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2166 xml = NULL;
2167 #endif
2168 } else {
2169 xml = ParseXMLSource(cx, str);
2170 if (!xml)
2171 return NULL;
2172 length = JSXML_LENGTH(xml);
2175 if (length == 0) {
2176 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2177 if (!obj)
2178 return NULL;
2179 } else if (length == 1) {
2180 xml = OrphanXMLChild(cx, xml, 0);
2181 if (!xml)
2182 return NULL;
2183 obj = js_GetXMLObject(cx, xml);
2184 if (!obj)
2185 return NULL;
2186 } else {
2187 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2188 return NULL;
2190 return obj;
2192 bad:
2193 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2194 JSDVG_IGNORE_STACK, v, NULL);
2195 return NULL;
2198 static JSBool
2199 Append(JSContext *cx, JSXML *list, JSXML *kid);
2201 static JSObject *
2202 ToXMLList(JSContext *cx, jsval v)
2204 JSObject *obj, *listobj;
2205 JSXML *xml, *list, *kid;
2206 JSClass *clasp;
2207 JSString *str;
2208 uint32 i, length;
2210 if (JSVAL_IS_PRIMITIVE(v)) {
2211 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2212 goto bad;
2213 } else {
2214 obj = JSVAL_TO_OBJECT(v);
2215 if (OBJECT_IS_XML(cx, obj)) {
2216 xml = (JSXML *) JS_GetPrivate(cx, obj);
2217 if (xml->xml_class != JSXML_CLASS_LIST) {
2218 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2219 if (!listobj)
2220 return NULL;
2221 list = (JSXML *) JS_GetPrivate(cx, listobj);
2222 if (!Append(cx, list, xml))
2223 return NULL;
2224 return listobj;
2226 return obj;
2229 clasp = OBJ_GET_CLASS(cx, obj);
2230 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2231 JS_ASSERT(0);
2234 if (clasp != &js_StringClass &&
2235 clasp != &js_NumberClass &&
2236 clasp != &js_BooleanClass) {
2237 goto bad;
2241 str = js_ValueToString(cx, v);
2242 if (!str)
2243 return NULL;
2244 if (IS_EMPTY(str)) {
2245 xml = NULL;
2246 length = 0;
2247 } else {
2248 if (!js_EnterLocalRootScope(cx))
2249 return NULL;
2250 xml = ParseXMLSource(cx, str);
2251 if (!xml) {
2252 js_LeaveLocalRootScope(cx);
2253 return NULL;
2255 length = JSXML_LENGTH(xml);
2258 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2259 if (listobj) {
2260 list = (JSXML *) JS_GetPrivate(cx, listobj);
2261 for (i = 0; i < length; i++) {
2262 kid = OrphanXMLChild(cx, xml, i);
2263 if (!kid || !Append(cx, list, kid)) {
2264 listobj = NULL;
2265 break;
2270 if (xml)
2271 js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2272 return listobj;
2274 bad:
2275 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2276 JSDVG_IGNORE_STACK, v, NULL);
2277 return NULL;
2281 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2282 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2283 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2284 * MakeXMLSpecialString subroutine.
2286 * These functions take ownership of sb->base, if sb is non-null, in all cases
2287 * of success or failure.
2289 static JSString *
2290 MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2291 JSString *str, JSString *str2,
2292 const jschar *prefix, size_t prefixlength,
2293 const jschar *suffix, size_t suffixlength)
2295 JSStringBuffer localSB;
2296 size_t length, length2, newlength;
2297 jschar *bp, *base;
2299 if (!sb) {
2300 sb = &localSB;
2301 js_InitStringBuffer(sb);
2304 length = JSSTRING_LENGTH(str);
2305 length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2306 newlength = STRING_BUFFER_OFFSET(sb) +
2307 prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2308 suffixlength;
2309 bp = base = (jschar *)
2310 JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2311 if (!bp) {
2312 js_FinishStringBuffer(sb);
2313 return NULL;
2316 bp += STRING_BUFFER_OFFSET(sb);
2317 js_strncpy(bp, prefix, prefixlength);
2318 bp += prefixlength;
2319 js_strncpy(bp, JSSTRING_CHARS(str), length);
2320 bp += length;
2321 if (length2 != 0) {
2322 *bp++ = (jschar) ' ';
2323 js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2324 bp += length2;
2326 js_strncpy(bp, suffix, suffixlength);
2327 bp[suffixlength] = 0;
2329 str = js_NewString(cx, base, newlength);
2330 if (!str)
2331 free(base);
2332 return str;
2335 static JSString *
2336 MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2338 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2339 'C', 'D', 'A', 'T', 'A',
2340 '['};
2341 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2343 return MakeXMLSpecialString(cx, sb, str, NULL,
2344 cdata_prefix_ucNstr, 9,
2345 cdata_suffix_ucNstr, 3);
2348 static JSString *
2349 MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2351 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2352 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2354 return MakeXMLSpecialString(cx, sb, str, NULL,
2355 comment_prefix_ucNstr, 4,
2356 comment_suffix_ucNstr, 3);
2359 static JSString *
2360 MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2361 JSString *value)
2363 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2364 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2366 return MakeXMLSpecialString(cx, sb, name, value,
2367 pi_prefix_ucNstr, 2,
2368 pi_suffix_ucNstr, 2);
2372 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2373 * equals, a double quote, an attribute value, and a closing double quote.
2375 static void
2376 AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2378 js_AppendChar(sb, '=');
2379 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2380 if (!valstr) {
2381 if (STRING_BUFFER_OK(sb)) {
2382 free(sb->base);
2383 sb->base = STRING_BUFFER_ERROR_BASE;
2385 return;
2387 js_AppendJSString(sb, valstr);
2391 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2393 * This function takes ownership of sb->base, if sb is non-null, in all cases
2394 * of success or failure.
2396 static JSString *
2397 EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2399 size_t length, newlength;
2400 const jschar *cp, *start, *end;
2401 jschar c;
2403 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2404 newlength = length;
2405 for (cp = start, end = cp + length; cp < end; cp++) {
2406 c = *cp;
2407 if (c == '<' || c == '>')
2408 newlength += 3;
2409 else if (c == '&')
2410 newlength += 4;
2412 if (newlength < length) {
2413 js_ReportAllocationOverflow(cx);
2414 return NULL;
2417 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2418 JSStringBuffer localSB;
2419 if (!sb) {
2420 sb = &localSB;
2421 js_InitStringBuffer(sb);
2423 if (!sb->grow(sb, newlength)) {
2424 JS_ReportOutOfMemory(cx);
2425 return NULL;
2427 for (cp = start; cp < end; cp++) {
2428 c = *cp;
2429 if (c == '<')
2430 js_AppendCString(sb, js_lt_entity_str);
2431 else if (c == '>')
2432 js_AppendCString(sb, js_gt_entity_str);
2433 else if (c == '&')
2434 js_AppendCString(sb, js_amp_entity_str);
2435 else
2436 js_AppendChar(sb, c);
2438 JS_ASSERT(STRING_BUFFER_OK(sb));
2439 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2440 if (!str)
2441 js_FinishStringBuffer(sb);
2443 return str;
2447 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2448 * This function takes ownership of sb->base, if sb is non-null, in all cases.
2450 static JSString *
2451 EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str,
2452 JSBool quote)
2454 size_t length, newlength;
2455 const jschar *cp, *start, *end;
2456 jschar c;
2458 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2459 newlength = length + (quote ? 2 : 0);
2460 for (cp = start, end = cp + length; cp < end; cp++) {
2461 c = *cp;
2462 if (c == '"')
2463 newlength += 5;
2464 else if (c == '<')
2465 newlength += 3;
2466 else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2467 newlength += 4;
2469 if (newlength < length) {
2470 js_ReportAllocationOverflow(cx);
2471 return NULL;
2474 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2475 JSStringBuffer localSB;
2476 if (!sb) {
2477 sb = &localSB;
2478 js_InitStringBuffer(sb);
2480 if (!sb->grow(sb, newlength)) {
2481 JS_ReportOutOfMemory(cx);
2482 return NULL;
2484 if (quote)
2485 js_AppendChar(sb, '"');
2486 for (cp = start; cp < end; cp++) {
2487 c = *cp;
2488 if (c == '"')
2489 js_AppendCString(sb, js_quot_entity_str);
2490 else if (c == '<')
2491 js_AppendCString(sb, js_lt_entity_str);
2492 else if (c == '&')
2493 js_AppendCString(sb, js_amp_entity_str);
2494 else if (c == '\n')
2495 js_AppendCString(sb, "&#xA;");
2496 else if (c == '\r')
2497 js_AppendCString(sb, "&#xD;");
2498 else if (c == '\t')
2499 js_AppendCString(sb, "&#x9;");
2500 else
2501 js_AppendChar(sb, c);
2503 if (quote)
2504 js_AppendChar(sb, '"');
2505 JS_ASSERT(STRING_BUFFER_OK(sb));
2506 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2507 if (!str)
2508 js_FinishStringBuffer(sb);
2510 return str;
2513 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2514 static JSXMLNamespace *
2515 GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes)
2517 JSXMLNamespace *match, *ns;
2518 uint32 i, n;
2519 jsval argv[2];
2520 JSObject *nsobj;
2522 JS_ASSERT(qn->uri);
2523 if (!qn->uri) {
2524 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2525 JSMSG_BAD_XML_NAMESPACE,
2526 qn->prefix
2527 ? js_ValueToPrintableString(cx,
2528 STRING_TO_JSVAL(qn->prefix))
2529 : js_undefined_str);
2530 return NULL;
2533 /* Look for a matching namespace in inScopeNSes, if provided. */
2534 match = NULL;
2535 if (inScopeNSes) {
2536 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2537 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace);
2538 if (!ns)
2539 continue;
2542 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2543 * If we preserve prefixes, we must match null qn->prefix against
2544 * an empty ns->prefix, in order to avoid generating redundant
2545 * prefixed and default namespaces for cases such as:
2547 * x = <t xmlns="http://foo.com"/>
2548 * print(x.toXMLString());
2550 * Per 10.3.2.1, the namespace attribute in t has an empty string
2551 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2553 * 1. If the [local name] property of a is "xmlns"
2554 * a. Map ns.prefix to the empty string
2556 * But t's name has a null prefix in this implementation, meaning
2557 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2558 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2559 * saying how "no value" maps to an ECMA-357 value -- but it must
2560 * map to the *undefined* prefix value).
2562 * Since "" != undefined (or null, in the current implementation)
2563 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2564 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2565 * This spec bug leads to ToXMLString results that duplicate the
2566 * declared namespace.
2568 if (js_EqualStrings(ns->uri, qn->uri) &&
2569 (ns->prefix == qn->prefix ||
2570 ((ns->prefix && qn->prefix)
2571 ? js_EqualStrings(ns->prefix, qn->prefix)
2572 : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) {
2573 match = ns;
2574 break;
2579 /* If we didn't match, make a new namespace from qn. */
2580 if (!match) {
2581 argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID;
2582 argv[1] = STRING_TO_JSVAL(qn->uri);
2583 nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2584 2, argv);
2585 if (!nsobj)
2586 return NULL;
2587 match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
2589 return match;
2592 static JSString *
2593 GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2595 const jschar *cp, *start, *end;
2596 size_t length, newlength, offset;
2597 uint32 i, n, m, serial;
2598 jschar *bp, *dp;
2599 JSBool done;
2600 JSXMLNamespace *ns;
2601 JSString *prefix;
2603 JS_ASSERT(!IS_EMPTY(uri));
2606 * If there are no *declared* namespaces, skip all collision detection and
2607 * return a short prefix quickly; an example of such a situation:
2609 * var x = <f/>;
2610 * var n = new Namespace("http://example.com/");
2611 * x.@n::att = "val";
2612 * x.toXMLString();
2614 * This is necessary for various log10 uses below to be valid.
2616 if (decls->length == 0)
2617 return JS_NewStringCopyZ(cx, "a");
2620 * Try peeling off the last filename suffix or pathname component till
2621 * we have a valid XML name. This heuristic will prefer "xul" given
2622 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2623 * likely URI of the form ".../xbl2/2005".
2625 JSSTRING_CHARS_AND_END(uri, start, end);
2626 cp = end;
2627 while (--cp > start) {
2628 if (*cp == '.' || *cp == '/' || *cp == ':') {
2629 ++cp;
2630 length = PTRDIFF(end, cp, jschar);
2631 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2632 break;
2633 end = --cp;
2636 length = PTRDIFF(end, cp, jschar);
2639 * If the namespace consisted only of non-XML names or names that begin
2640 * case-insensitively with "xml", arbitrarily create a prefix consisting
2641 * of 'a's of size length (allowing dp-calculating code to work with or
2642 * without this branch executing) plus the space for storing a hyphen and
2643 * the serial number (avoiding reallocation if a collision happens).
2645 bp = (jschar *) cp;
2646 newlength = length;
2647 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2648 newlength = length + 2 + (size_t) log10((double) decls->length);
2649 bp = (jschar *)
2650 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2651 if (!bp)
2652 return NULL;
2654 bp[newlength] = 0;
2655 for (i = 0; i < newlength; i++)
2656 bp[i] = 'a';
2660 * Now search through decls looking for a collision. If we collide with
2661 * an existing prefix, start tacking on a hyphen and a serial number.
2663 serial = 0;
2664 do {
2665 done = JS_TRUE;
2666 for (i = 0, n = decls->length; i < n; i++) {
2667 ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace);
2668 if (ns && ns->prefix &&
2669 JSSTRING_LENGTH(ns->prefix) == newlength &&
2670 !memcmp(JSSTRING_CHARS(ns->prefix), bp,
2671 newlength * sizeof(jschar))) {
2672 if (bp == cp) {
2673 newlength = length + 2 + (size_t) log10((double) n);
2674 bp = (jschar *)
2675 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2676 if (!bp)
2677 return NULL;
2678 js_strncpy(bp, cp, length);
2681 ++serial;
2682 JS_ASSERT(serial <= n);
2683 dp = bp + length + 2 + (size_t) log10((double) serial);
2684 *dp = 0;
2685 for (m = serial; m != 0; m /= 10)
2686 *--dp = (jschar)('0' + m % 10);
2687 *--dp = '-';
2688 JS_ASSERT(dp == bp + length);
2690 done = JS_FALSE;
2691 break;
2694 } while (!done);
2696 if (bp == cp) {
2697 offset = PTRDIFF(cp, start, jschar);
2698 prefix = js_NewDependentString(cx, uri, offset, length);
2699 } else {
2700 prefix = js_NewString(cx, bp, newlength);
2701 if (!prefix)
2702 JS_free(cx, bp);
2704 return prefix;
2707 static JSBool
2708 namespace_match(const void *a, const void *b)
2710 const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
2711 const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
2713 if (nsb->prefix)
2714 return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix);
2715 return js_EqualStrings(nsa->uri, nsb->uri);
2718 /* ECMA-357 10.2.1 and 10.2.2 */
2719 #define TO_SOURCE_FLAG 0x80000000
2721 static JSString *
2722 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2723 uint32 indentLevel)
2725 JSBool pretty, indentKids;
2726 JSStringBuffer sb;
2727 JSString *str, *prefix, *kidstr;
2728 JSXMLArrayCursor cursor;
2729 uint32 i, n, nextIndentLevel;
2730 JSXMLArray empty, decls, ancdecls;
2731 JSXMLNamespace *ns, *ns2;
2732 JSXML *attr, *kid;
2734 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2735 return NULL;
2737 js_InitStringBuffer(&sb);
2738 if (pretty) {
2739 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2741 if (!STRING_BUFFER_OK(&sb)) {
2742 JS_ReportOutOfMemory(cx);
2743 return NULL;
2746 str = NULL;
2748 switch (xml->xml_class) {
2749 case JSXML_CLASS_TEXT:
2750 /* Step 4. */
2751 if (pretty) {
2752 str = ChompXMLWhitespace(cx, xml->xml_value);
2753 if (!str)
2754 return NULL;
2755 } else {
2756 str = xml->xml_value;
2758 return EscapeElementValue(cx, &sb, str);
2760 case JSXML_CLASS_ATTRIBUTE:
2761 /* Step 5. */
2762 return EscapeAttributeValue(cx, &sb, xml->xml_value,
2763 (indentLevel & TO_SOURCE_FLAG) != 0);
2765 case JSXML_CLASS_COMMENT:
2766 /* Step 6. */
2767 return MakeXMLCommentString(cx, &sb, xml->xml_value);
2769 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2770 /* Step 7. */
2771 return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value);
2773 case JSXML_CLASS_LIST:
2774 /* ECMA-357 10.2.2. */
2775 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2776 i = 0;
2777 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2778 if (pretty && i != 0)
2779 js_AppendChar(&sb, '\n');
2781 kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2782 if (!kidstr)
2783 break;
2785 js_AppendJSString(&sb, kidstr);
2786 ++i;
2788 XMLArrayCursorFinish(&cursor);
2789 if (kid)
2790 goto list_out;
2792 if (!sb.base)
2793 return cx->runtime->emptyString;
2795 if (!STRING_BUFFER_OK(&sb)) {
2796 JS_ReportOutOfMemory(cx);
2797 return NULL;
2800 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2801 list_out:
2802 if (!str && STRING_BUFFER_OK(&sb))
2803 js_FinishStringBuffer(&sb);
2804 return str;
2806 default:;
2809 /* After this point, control must flow through label out: to exit. */
2810 if (!js_EnterLocalRootScope(cx))
2811 return NULL;
2813 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2814 if (!ancestorNSes) {
2815 XMLArrayInit(cx, &empty, 0);
2816 ancestorNSes = &empty;
2818 XMLArrayInit(cx, &decls, 0);
2819 ancdecls.capacity = 0;
2821 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2822 XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
2823 while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
2824 if (!ns->declared)
2825 continue;
2826 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2827 /* NOTE: may want to exclude unused namespaces here. */
2828 ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE);
2829 if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2))
2830 break;
2833 XMLArrayCursorFinish(&cursor);
2834 if (ns)
2835 goto out;
2838 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2839 * not own its member references. In the spec, ancdecls has no name, but
2840 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2842 if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
2843 goto out;
2844 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2845 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace);
2846 if (!ns2)
2847 continue;
2848 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
2849 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2850 goto out;
2852 for (i = 0, n = decls.length; i < n; i++) {
2853 ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace);
2854 if (!ns2)
2855 continue;
2856 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
2857 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2858 goto out;
2861 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2862 ns = GetNamespace(cx, xml->name, &ancdecls);
2863 if (!ns)
2864 goto out;
2866 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2867 if (!ns->prefix) {
2869 * Create a namespace prefix that isn't used by any member of decls.
2870 * Assign the new prefix to a copy of ns. Flag this namespace as if
2871 * it were declared, for assertion-testing's sake later below.
2873 * Erratum: if ns->prefix and xml->name are both null (*undefined* in
2874 * ECMA-357), we know that xml was named using the default namespace
2875 * (proof: see GetNamespace and the Namespace constructor called with
2876 * two arguments). So we ought not generate a new prefix here, when
2877 * we can declare ns as the default namespace for xml.
2879 * This helps descendants inherit the namespace instead of redundantly
2880 * redeclaring it with generated prefixes in each descendant.
2882 if (!xml->name->prefix) {
2883 prefix = cx->runtime->emptyString;
2884 } else {
2885 prefix = GeneratePrefix(cx, ns->uri, &ancdecls);
2886 if (!prefix)
2887 goto out;
2889 ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE);
2890 if (!ns)
2891 goto out;
2894 * If the xml->name was unprefixed, we must remove any declared default
2895 * namespace from decls before appending ns. How can you get a default
2896 * namespace in decls that doesn't match the one from name? Apparently
2897 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2898 * to fix this is to update x's in-scope namespaces when setNamespace
2899 * is called, but that's not specified by ECMA-357.
2901 * Likely Erratum here, depending on whether the lack of update to x's
2902 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2903 * erratum or not. Note that changing setNamespace to update the list
2904 * of in-scope namespaces will change x.namespaceDeclarations().
2906 if (IS_EMPTY(prefix)) {
2907 i = XMLArrayFindMember(&decls, ns, namespace_match);
2908 if (i != XML_NOT_FOUND)
2909 XMLArrayDelete(cx, &decls, i, JS_TRUE);
2913 * In the spec, ancdecls has no name, but is always written out as
2914 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2915 * that union in ancdecls, any time we append a namespace strong
2916 * ref to decls, we must also append a weak ref to ancdecls. Order
2917 * matters here: code at label out: releases strong refs in decls.
2919 if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
2920 !XMLARRAY_APPEND(cx, &decls, ns)) {
2921 goto out;
2925 /* Format the element or point-tag into sb. */
2926 js_AppendChar(&sb, '<');
2928 if (ns->prefix && !IS_EMPTY(ns->prefix)) {
2929 js_AppendJSString(&sb, ns->prefix);
2930 js_AppendChar(&sb, ':');
2932 js_AppendJSString(&sb, xml->name->localName);
2935 * Step 16 makes a union to avoid writing two loops in step 17, to share
2936 * common attribute value appending spec-code. We prefer two loops for
2937 * faster code and less data overhead.
2940 /* Step 17(b): append attributes. */
2941 XMLArrayCursorInit(&cursor, &xml->xml_attrs);
2942 while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2943 js_AppendChar(&sb, ' ');
2944 ns2 = GetNamespace(cx, attr->name, &ancdecls);
2945 if (!ns2)
2946 break;
2948 /* 17(b)(ii): NULL means *undefined* here. */
2949 if (!ns2->prefix) {
2950 prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
2951 if (!prefix)
2952 break;
2954 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2955 ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE);
2956 if (!ns2)
2957 break;
2960 * In the spec, ancdecls has no name, but is always written out as
2961 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2962 * that union in ancdecls, any time we append a namespace strong
2963 * ref to decls, we must also append a weak ref to ancdecls. Order
2964 * matters here: code at label out: releases strong refs in decls.
2966 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
2967 !XMLARRAY_APPEND(cx, &decls, ns2)) {
2968 break;
2972 /* 17(b)(iii). */
2973 if (!IS_EMPTY(ns2->prefix)) {
2974 js_AppendJSString(&sb, ns2->prefix);
2975 js_AppendChar(&sb, ':');
2978 /* 17(b)(iv). */
2979 js_AppendJSString(&sb, attr->name->localName);
2981 /* 17(d-g). */
2982 AppendAttributeValue(cx, &sb, attr->xml_value);
2984 XMLArrayCursorFinish(&cursor);
2985 if (attr)
2986 goto out;
2988 /* Step 17(c): append XML namespace declarations. */
2989 XMLArrayCursorInit(&cursor, &decls);
2990 while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
2991 JS_ASSERT(ns2->declared);
2993 js_AppendCString(&sb, " xmlns");
2995 /* 17(c)(ii): NULL means *undefined* here. */
2996 if (!ns2->prefix) {
2997 prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
2998 if (!prefix)
2999 break;
3000 ns2->prefix = prefix;
3003 /* 17(c)(iii). */
3004 if (!IS_EMPTY(ns2->prefix)) {
3005 js_AppendChar(&sb, ':');
3006 js_AppendJSString(&sb, ns2->prefix);
3009 /* 17(d-g). */
3010 AppendAttributeValue(cx, &sb, ns2->uri);
3012 XMLArrayCursorFinish(&cursor);
3013 if (ns2)
3014 goto out;
3016 /* Step 18: handle point tags. */
3017 n = xml->xml_kids.length;
3018 if (n == 0) {
3019 js_AppendCString(&sb, "/>");
3020 } else {
3021 /* Steps 19 through 25: handle element content, and open the end-tag. */
3022 js_AppendChar(&sb, '>');
3023 indentKids = n > 1 ||
3024 (n == 1 &&
3025 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
3026 kid->xml_class != JSXML_CLASS_TEXT);
3028 if (pretty && indentKids) {
3029 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
3030 goto out;
3031 nextIndentLevel = indentLevel + i;
3032 } else {
3033 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
3036 XMLArrayCursorInit(&cursor, &xml->xml_kids);
3037 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3038 if (pretty && indentKids)
3039 js_AppendChar(&sb, '\n');
3041 kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
3042 if (!kidstr)
3043 break;
3045 js_AppendJSString(&sb, kidstr);
3047 XMLArrayCursorFinish(&cursor);
3048 if (kid)
3049 goto out;
3051 if (pretty && indentKids) {
3052 js_AppendChar(&sb, '\n');
3053 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
3055 js_AppendCString(&sb, "</");
3057 /* Step 26. */
3058 if (ns->prefix && !IS_EMPTY(ns->prefix)) {
3059 js_AppendJSString(&sb, ns->prefix);
3060 js_AppendChar(&sb, ':');
3063 /* Step 27. */
3064 js_AppendJSString(&sb, xml->name->localName);
3065 js_AppendChar(&sb, '>');
3068 if (!STRING_BUFFER_OK(&sb)) {
3069 JS_ReportOutOfMemory(cx);
3070 goto out;
3073 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
3074 out:
3075 js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
3076 if (!str && STRING_BUFFER_OK(&sb))
3077 js_FinishStringBuffer(&sb);
3078 XMLArrayFinish(cx, &decls);
3079 if (ancdecls.capacity != 0)
3080 XMLArrayFinish(cx, &ancdecls);
3081 return str;
3084 /* ECMA-357 10.2 */
3085 static JSString *
3086 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
3088 JSObject *obj;
3089 JSString *str;
3090 JSXML *xml;
3092 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
3093 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3094 JSMSG_BAD_XML_CONVERSION,
3095 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
3096 return NULL;
3099 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
3100 return js_ValueToString(cx, v);
3102 if (JSVAL_IS_STRING(v))
3103 return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
3105 obj = JSVAL_TO_OBJECT(v);
3106 if (!OBJECT_IS_XML(cx, obj)) {
3107 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
3108 return NULL;
3109 str = js_ValueToString(cx, v);
3110 if (!str)
3111 return NULL;
3112 return EscapeElementValue(cx, NULL, str);
3115 /* Handle non-element cases in this switch, returning from each case. */
3116 xml = (JSXML *) JS_GetPrivate(cx, obj);
3117 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
3120 static JSXMLQName *
3121 ToAttributeName(JSContext *cx, jsval v)
3123 JSString *name, *uri, *prefix;
3124 JSObject *obj;
3125 JSClass *clasp;
3126 JSXMLQName *qn;
3127 JSTempValueRooter tvr;
3129 if (JSVAL_IS_STRING(v)) {
3130 name = JSVAL_TO_STRING(v);
3131 uri = prefix = cx->runtime->emptyString;
3132 } else {
3133 if (JSVAL_IS_PRIMITIVE(v)) {
3134 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
3135 JSDVG_IGNORE_STACK, v, NULL);
3136 return NULL;
3139 obj = JSVAL_TO_OBJECT(v);
3140 clasp = OBJ_GET_CLASS(cx, obj);
3141 if (clasp == &js_AttributeNameClass)
3142 return (JSXMLQName *) JS_GetPrivate(cx, obj);
3144 if (clasp == &js_QNameClass.base) {
3145 qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
3146 uri = qn->uri;
3147 prefix = qn->prefix;
3148 name = qn->localName;
3149 } else {
3150 if (clasp == &js_AnyNameClass) {
3151 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3152 } else {
3153 name = js_ValueToString(cx, v);
3154 if (!name)
3155 return NULL;
3157 uri = prefix = cx->runtime->emptyString;
3161 qn = js_NewXMLQName(cx, uri, prefix, name);
3162 if (!qn)
3163 return NULL;
3165 JS_PUSH_TEMP_ROOT_QNAME(cx, qn, &tvr);
3166 obj = js_GetAttributeNameObject(cx, qn);
3167 JS_POP_TEMP_ROOT(cx, &tvr);
3168 if (!obj)
3169 return NULL;
3170 return qn;
3173 static void
3174 ReportBadXMLName(JSContext *cx, jsval id)
3176 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, id, NULL);
3179 static JSBool
3180 IsFunctionQName(JSContext *cx, JSXMLQName *qn, jsid *funidp)
3182 JSAtom *atom;
3184 atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom;
3185 if (qn->uri && atom &&
3186 (qn->uri == ATOM_TO_STRING(atom) ||
3187 js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) {
3188 return JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp);
3190 *funidp = 0;
3191 return JS_TRUE;
3194 JSBool
3195 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
3197 JSXMLQName *qn;
3199 if (OBJ_GET_CLASS(cx, obj) == &js_QNameClass.base) {
3200 qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
3201 return IsFunctionQName(cx, qn, funidp);
3203 *funidp = 0;
3204 return JS_TRUE;
3207 static JSXMLQName *
3208 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
3210 JSString *name;
3211 JSObject *obj;
3212 JSClass *clasp;
3213 uint32 index;
3214 JSXMLQName *qn;
3216 if (JSVAL_IS_STRING(v)) {
3217 name = JSVAL_TO_STRING(v);
3218 } else {
3219 if (JSVAL_IS_PRIMITIVE(v)) {
3220 ReportBadXMLName(cx, v);
3221 return NULL;
3224 obj = JSVAL_TO_OBJECT(v);
3225 clasp = OBJ_GET_CLASS(cx, obj);
3226 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
3227 goto out;
3228 if (clasp == &js_AnyNameClass) {
3229 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3230 goto construct;
3232 name = js_ValueToString(cx, v);
3233 if (!name)
3234 return NULL;
3238 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3240 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3242 * First, _P_ should be _s_, to refer to the given string.
3244 * Second, why does ToXMLName applied to the string type throw TypeError
3245 * only for numeric literals without any leading or trailing whitespace?
3247 * If the idea is to reject uint32 property names, then the check needs to
3248 * be stricter, to exclude hexadecimal and floating point literals.
3250 if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
3251 goto bad;
3253 if (*JSSTRING_CHARS(name) == '@') {
3254 name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1);
3255 if (!name)
3256 return NULL;
3257 *funidp = 0;
3258 return ToAttributeName(cx, STRING_TO_JSVAL(name));
3261 construct:
3262 v = STRING_TO_JSVAL(name);
3263 obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
3264 if (!obj)
3265 return NULL;
3267 out:
3268 qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
3269 if (!IsFunctionQName(cx, qn, funidp))
3270 return NULL;
3271 return qn;
3273 bad:
3274 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3275 JSMSG_BAD_XML_NAME,
3276 js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
3277 return NULL;
3280 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3281 static JSBool
3282 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
3284 JSXMLNamespace *match, *ns2;
3285 uint32 i, n, m;
3287 if (xml->xml_class != JSXML_CLASS_ELEMENT)
3288 return JS_TRUE;
3290 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
3291 if (!ns->prefix) {
3292 match = NULL;
3293 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3294 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
3295 if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) {
3296 match = ns2;
3297 break;
3300 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
3301 return JS_FALSE;
3302 } else {
3303 if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri))
3304 return JS_TRUE;
3305 match = NULL;
3306 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3307 m = XML_NOT_FOUND;
3308 #endif
3309 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3310 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
3311 if (ns2 && ns2->prefix &&
3312 js_EqualStrings(ns2->prefix, ns->prefix)) {
3313 match = ns2;
3314 m = i;
3315 break;
3318 if (match && !js_EqualStrings(match->uri, ns->uri)) {
3319 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
3320 JSXMLNamespace);
3321 JS_ASSERT(ns2 == match);
3322 match->prefix = NULL;
3323 if (!AddInScopeNamespace(cx, xml, match))
3324 return JS_FALSE;
3326 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
3327 return JS_FALSE;
3330 /* OPTION: enforce that descendants have superset namespaces. */
3331 return JS_TRUE;
3334 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3335 static JSBool
3336 Append(JSContext *cx, JSXML *list, JSXML *xml)
3338 uint32 i, j, k, n;
3339 JSXML *kid;
3341 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
3342 i = list->xml_kids.length;
3343 n = 1;
3344 if (xml->xml_class == JSXML_CLASS_LIST) {
3345 list->xml_target = xml->xml_target;
3346 list->xml_targetprop = xml->xml_targetprop;
3347 n = JSXML_LENGTH(xml);
3348 k = i + n;
3349 if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
3350 return JS_FALSE;
3351 for (j = 0; j < n; j++) {
3352 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
3353 if (kid)
3354 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3356 return JS_TRUE;
3359 list->xml_target = xml->parent;
3360 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3361 list->xml_targetprop = NULL;
3362 else
3363 list->xml_targetprop = xml->name;
3364 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3365 return JS_FALSE;
3366 return JS_TRUE;
3369 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3370 static JSXML *
3371 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3373 static JSXML *
3374 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3376 JSXML *copy;
3377 JSBool ok;
3379 /* Our caller may not be protecting newborns with a local root scope. */
3380 if (!js_EnterLocalRootScope(cx))
3381 return NULL;
3382 copy = DeepCopyInLRS(cx, xml, flags);
3383 if (copy) {
3384 if (obj) {
3385 /* Caller provided the object for this copy, hook 'em up. */
3386 ok = JS_SetPrivate(cx, obj, copy);
3387 if (ok)
3388 copy->object = obj;
3389 } else {
3390 ok = js_GetXMLObject(cx, copy) != NULL;
3392 if (!ok)
3393 copy = NULL;
3395 js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
3396 return copy;
3400 * (i) We must be in a local root scope (InLRS).
3401 * (ii) parent must have a rooted object.
3402 * (iii) from's owning object must be locked if not thread-local.
3404 static JSBool
3405 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3406 uintN flags)
3408 uint32 j, n;
3409 JSXMLArrayCursor cursor;
3410 JSBool ok;
3411 JSXML *kid, *kid2;
3412 JSString *str;
3414 JS_ASSERT(cx->localRootStack);
3416 n = from->length;
3417 if (!XMLArraySetCapacity(cx, to, n))
3418 return JS_FALSE;
3420 XMLArrayCursorInit(&cursor, from);
3421 j = 0;
3422 ok = JS_TRUE;
3423 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3424 if ((flags & XSF_IGNORE_COMMENTS) &&
3425 kid->xml_class == JSXML_CLASS_COMMENT) {
3426 continue;
3428 if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3429 kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3430 continue;
3432 if ((flags & XSF_IGNORE_WHITESPACE) &&
3433 (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3434 continue;
3436 kid2 = DeepCopyInLRS(cx, kid, flags);
3437 if (!kid2) {
3438 to->length = j;
3439 ok = JS_FALSE;
3440 break;
3443 if ((flags & XSF_IGNORE_WHITESPACE) &&
3444 n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3445 str = ChompXMLWhitespace(cx, kid2->xml_value);
3446 if (!str) {
3447 to->length = j;
3448 ok = JS_FALSE;
3449 break;
3451 kid2->xml_value = str;
3454 XMLARRAY_SET_MEMBER(to, j, kid2);
3455 ++j;
3456 if (parent->xml_class != JSXML_CLASS_LIST)
3457 kid2->parent = parent;
3459 XMLArrayCursorFinish(&cursor);
3460 if (!ok)
3461 return JS_FALSE;
3463 if (j < n)
3464 XMLArrayTrim(to);
3465 return JS_TRUE;
3468 static JSXML *
3469 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
3471 JSXML *copy;
3472 JSXMLQName *qn;
3473 JSBool ok;
3474 uint32 i, n;
3475 JSXMLNamespace *ns, *ns2;
3477 /* Our caller must be protecting newborn objects. */
3478 JS_ASSERT(cx->localRootStack);
3480 JS_CHECK_RECURSION(cx, return NULL);
3482 copy = js_NewXML(cx, (JSXMLClass) xml->xml_class);
3483 if (!copy)
3484 return NULL;
3485 qn = xml->name;
3486 if (qn) {
3487 qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
3488 if (!qn) {
3489 ok = JS_FALSE;
3490 goto out;
3493 copy->name = qn;
3494 copy->xml_flags = xml->xml_flags;
3496 if (JSXML_HAS_VALUE(xml)) {
3497 copy->xml_value = xml->xml_value;
3498 ok = JS_TRUE;
3499 } else {
3500 ok = DeepCopySetInLRS(cx, &xml->xml_kids, &copy->xml_kids, copy, flags);
3501 if (!ok)
3502 goto out;
3504 if (xml->xml_class == JSXML_CLASS_LIST) {
3505 copy->xml_target = xml->xml_target;
3506 copy->xml_targetprop = xml->xml_targetprop;
3507 } else {
3508 n = xml->xml_namespaces.length;
3509 ok = XMLArraySetCapacity(cx, &copy->xml_namespaces, n);
3510 if (!ok)
3511 goto out;
3512 for (i = 0; i < n; i++) {
3513 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
3514 if (!ns)
3515 continue;
3516 ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared);
3517 if (!ns2) {
3518 copy->xml_namespaces.length = i;
3519 ok = JS_FALSE;
3520 goto out;
3522 XMLARRAY_SET_MEMBER(&copy->xml_namespaces, i, ns2);
3525 ok = DeepCopySetInLRS(cx, &xml->xml_attrs, &copy->xml_attrs, copy,
3527 if (!ok)
3528 goto out;
3532 out:
3533 if (!ok)
3534 return NULL;
3535 return copy;
3538 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3539 static void
3540 DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index)
3542 JSXML *kid;
3544 if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3545 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3546 if (kid)
3547 kid->parent = NULL;
3548 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3552 typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml);
3554 static JSBool
3555 MatchAttrName(JSXMLQName *nameqn, JSXML *attr)
3557 JSXMLQName *attrqn = attr->name;
3559 return (IS_STAR(nameqn->localName) ||
3560 js_EqualStrings(attrqn->localName, nameqn->localName)) &&
3561 (!nameqn->uri ||
3562 js_EqualStrings(attrqn->uri, nameqn->uri));
3565 static JSBool
3566 MatchElemName(JSXMLQName *nameqn, JSXML *elem)
3568 return (IS_STAR(nameqn->localName) ||
3569 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3570 js_EqualStrings(elem->name->localName, nameqn->localName))) &&
3571 (!nameqn->uri ||
3572 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3573 js_EqualStrings(elem->name->uri, nameqn->uri)));
3576 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3577 static JSBool
3578 DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list)
3580 uint32 i, n;
3581 JSXML *attr, *kid;
3583 JS_CHECK_RECURSION(cx, return JS_FALSE);
3585 if (xml->xml_class == JSXML_CLASS_ELEMENT &&
3586 OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) {
3587 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
3588 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3589 if (attr && MatchAttrName(nameqn, attr)) {
3590 if (!Append(cx, list, attr))
3591 return JS_FALSE;
3596 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3597 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3598 if (!kid)
3599 continue;
3600 if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass &&
3601 MatchElemName(nameqn, kid)) {
3602 if (!Append(cx, list, kid))
3603 return JS_FALSE;
3605 if (!DescendantsHelper(cx, kid, nameqn, list))
3606 return JS_FALSE;
3608 return JS_TRUE;
3611 static JSXML *
3612 Descendants(JSContext *cx, JSXML *xml, jsval id)
3614 jsid funid;
3615 JSXMLQName *nameqn;
3616 JSObject *listobj;
3617 JSXML *list, *kid;
3618 uint32 i, n;
3619 JSBool ok;
3621 nameqn = ToXMLName(cx, id, &funid);
3622 if (!nameqn)
3623 return NULL;
3625 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3626 if (!listobj)
3627 return NULL;
3628 list = (JSXML *) JS_GetPrivate(cx, listobj);
3629 if (funid)
3630 return list;
3633 * Protect nameqn's object and strings from GC by linking list to it
3634 * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj,
3635 * which protects list. Any other object allocations occuring beneath
3636 * DescendantsHelper use local roots.
3638 list->name = nameqn;
3639 if (!js_EnterLocalRootScope(cx))
3640 return NULL;
3641 if (xml->xml_class == JSXML_CLASS_LIST) {
3642 ok = JS_TRUE;
3643 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
3644 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3645 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
3646 ok = DescendantsHelper(cx, kid, nameqn, list);
3647 if (!ok)
3648 break;
3651 } else {
3652 ok = DescendantsHelper(cx, xml, nameqn, list);
3654 js_LeaveLocalRootScopeWithResult(cx, (jsval) list);
3655 if (!ok)
3656 return NULL;
3657 list->name = NULL;
3658 return list;
3661 static JSBool
3662 xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
3664 /* Recursive (JSXML *) parameterized version of Equals. */
3665 static JSBool
3666 XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3668 JSXMLQName *qn, *vqn;
3669 uint32 i, j, n;
3670 JSXMLArrayCursor cursor, vcursor;
3671 JSXML *kid, *vkid, *attr, *vattr;
3672 JSBool ok;
3673 JSObject *xobj, *vobj;
3675 retry:
3676 if (xml->xml_class != vxml->xml_class) {
3677 if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
3678 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3679 if (xml)
3680 goto retry;
3682 if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3683 vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3684 if (vxml)
3685 goto retry;
3687 *bp = JS_FALSE;
3688 return JS_TRUE;
3691 qn = xml->name;
3692 vqn = vxml->name;
3693 if (qn) {
3694 *bp = vqn &&
3695 js_EqualStrings(qn->localName, vqn->localName) &&
3696 js_EqualStrings(qn->uri, vqn->uri);
3697 } else {
3698 *bp = vqn == NULL;
3700 if (!*bp)
3701 return JS_TRUE;
3703 if (JSXML_HAS_VALUE(xml)) {
3704 *bp = js_EqualStrings(xml->xml_value, vxml->xml_value);
3705 } else if (xml->xml_kids.length != vxml->xml_kids.length) {
3706 *bp = JS_FALSE;
3707 } else {
3708 XMLArrayCursorInit(&cursor, &xml->xml_kids);
3709 XMLArrayCursorInit(&vcursor, &vxml->xml_kids);
3710 for (;;) {
3711 kid = (JSXML *) XMLArrayCursorNext(&cursor);
3712 vkid = (JSXML *) XMLArrayCursorNext(&vcursor);
3713 if (!kid || !vkid) {
3714 *bp = !kid && !vkid;
3715 ok = JS_TRUE;
3716 break;
3718 xobj = js_GetXMLObject(cx, kid);
3719 vobj = js_GetXMLObject(cx, vkid);
3720 ok = xobj && vobj &&
3721 xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp);
3722 if (!ok || !*bp)
3723 break;
3725 XMLArrayCursorFinish(&vcursor);
3726 XMLArrayCursorFinish(&cursor);
3727 if (!ok)
3728 return JS_FALSE;
3730 if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3731 n = xml->xml_attrs.length;
3732 if (n != vxml->xml_attrs.length)
3733 *bp = JS_FALSE;
3734 for (i = 0; *bp && i < n; i++) {
3735 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3736 if (!attr)
3737 continue;
3738 j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3739 if (j == XML_NOT_FOUND) {
3740 *bp = JS_FALSE;
3741 break;
3743 vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3744 if (!vattr)
3745 continue;
3746 *bp = js_EqualStrings(attr->xml_value, vattr->xml_value);
3751 return JS_TRUE;
3754 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3755 static JSBool
3756 Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3758 JSObject *vobj;
3759 JSXML *vxml;
3761 if (JSVAL_IS_PRIMITIVE(v)) {
3762 *bp = JS_FALSE;
3763 if (xml->xml_class == JSXML_CLASS_LIST) {
3764 if (xml->xml_kids.length == 1) {
3765 vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3766 if (!vxml)
3767 return JS_TRUE;
3768 vobj = js_GetXMLObject(cx, vxml);
3769 if (!vobj)
3770 return JS_FALSE;
3771 return js_XMLObjectOps.equality(cx, vobj, v, bp);
3773 if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3774 *bp = JS_TRUE;
3776 } else {
3777 vobj = JSVAL_TO_OBJECT(v);
3778 if (!OBJECT_IS_XML(cx, vobj)) {
3779 *bp = JS_FALSE;
3780 } else {
3781 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3782 if (!XMLEquals(cx, xml, vxml, bp))
3783 return JS_FALSE;
3786 return JS_TRUE;
3789 static JSBool
3790 CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3792 JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3794 do {
3795 if (xml == kid) {
3796 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3797 JSMSG_CYCLIC_VALUE, js_XML_str);
3798 return JS_FALSE;
3800 } while ((xml = xml->parent) != NULL);
3802 return JS_TRUE;
3805 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3806 static JSBool
3807 Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3809 uint32 j, n;
3810 JSXML *vxml, *kid;
3811 JSObject *vobj;
3812 JSString *str;
3814 if (!JSXML_HAS_KIDS(xml))
3815 return JS_TRUE;
3817 n = 1;
3818 vxml = NULL;
3819 if (!JSVAL_IS_PRIMITIVE(v)) {
3820 vobj = JSVAL_TO_OBJECT(v);
3821 if (OBJECT_IS_XML(cx, vobj)) {
3822 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3823 if (vxml->xml_class == JSXML_CLASS_LIST) {
3824 n = vxml->xml_kids.length;
3825 if (n == 0)
3826 return JS_TRUE;
3827 for (j = 0; j < n; j++) {
3828 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3829 if (!kid)
3830 continue;
3831 if (!CheckCycle(cx, xml, kid))
3832 return JS_FALSE;
3834 } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3835 /* OPTION: enforce that descendants have superset namespaces. */
3836 if (!CheckCycle(cx, xml, vxml))
3837 return JS_FALSE;
3841 if (!vxml) {
3842 str = js_ValueToString(cx, v);
3843 if (!str)
3844 return JS_FALSE;
3846 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3847 if (!vxml)
3848 return JS_FALSE;
3849 vxml->xml_value = str;
3852 if (i > xml->xml_kids.length)
3853 i = xml->xml_kids.length;
3855 if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3856 return JS_FALSE;
3858 if (vxml->xml_class == JSXML_CLASS_LIST) {
3859 for (j = 0; j < n; j++) {
3860 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3861 if (!kid)
3862 continue;
3863 kid->parent = xml;
3864 XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3866 /* OPTION: enforce that descendants have superset namespaces. */
3868 } else {
3869 vxml->parent = xml;
3870 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3872 return JS_TRUE;
3875 static JSBool
3876 IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp)
3878 JSString *str;
3880 if (index <= JSVAL_INT_MAX) {
3881 *idvp = INT_TO_JSVAL(index);
3882 } else {
3883 str = js_NumberToString(cx, (jsdouble) index);
3884 if (!str)
3885 return JS_FALSE;
3886 *idvp = STRING_TO_JSVAL(str);
3888 return JS_TRUE;
3891 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3892 static JSBool
3893 Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3895 uint32 n;
3896 JSXML *vxml, *kid;
3897 JSObject *vobj;
3898 JSString *str;
3900 if (!JSXML_HAS_KIDS(xml))
3901 return JS_TRUE;
3904 * 9.1.1.12
3905 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3906 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3908 n = xml->xml_kids.length;
3909 if (i > n)
3910 i = n;
3912 vxml = NULL;
3913 if (!JSVAL_IS_PRIMITIVE(v)) {
3914 vobj = JSVAL_TO_OBJECT(v);
3915 if (OBJECT_IS_XML(cx, vobj))
3916 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3919 switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) {
3920 case JSXML_CLASS_ELEMENT:
3921 /* OPTION: enforce that descendants have superset namespaces. */
3922 if (!CheckCycle(cx, xml, vxml))
3923 return JS_FALSE;
3924 case JSXML_CLASS_COMMENT:
3925 case JSXML_CLASS_PROCESSING_INSTRUCTION:
3926 case JSXML_CLASS_TEXT:
3927 goto do_replace;
3929 case JSXML_CLASS_LIST:
3930 if (i < n)
3931 DeleteByIndex(cx, xml, i);
3932 if (!Insert(cx, xml, i, v))
3933 return JS_FALSE;
3934 break;
3936 default:
3937 str = js_ValueToString(cx, v);
3938 if (!str)
3939 return JS_FALSE;
3941 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3942 if (!vxml)
3943 return JS_FALSE;
3944 vxml->xml_value = str;
3946 do_replace:
3947 vxml->parent = xml;
3948 if (i < n) {
3949 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3950 if (kid)
3951 kid->parent = NULL;
3953 if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3954 return JS_FALSE;
3955 break;
3958 return JS_TRUE;
3961 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3962 static void
3963 DeleteNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName *nameqn,
3964 JSBool attributes)
3966 JSXMLArray *array;
3967 uint32 index, deleteCount;
3968 JSXML *kid;
3969 JSXMLNameMatcher matcher;
3971 if (xml->xml_class == JSXML_CLASS_LIST) {
3972 array = &xml->xml_kids;
3973 for (index = 0; index < array->length; index++) {
3974 kid = XMLARRAY_MEMBER(array, index, JSXML);
3975 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT)
3976 DeleteNamedProperty(cx, kid, nameqn, attributes);
3978 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3979 if (attributes) {
3980 array = &xml->xml_attrs;
3981 matcher = MatchAttrName;
3982 } else {
3983 array = &xml->xml_kids;
3984 matcher = MatchElemName;
3986 deleteCount = 0;
3987 for (index = 0; index < array->length; index++) {
3988 kid = XMLARRAY_MEMBER(array, index, JSXML);
3989 if (kid && matcher(nameqn, kid)) {
3990 kid->parent = NULL;
3991 XMLArrayDelete(cx, array, index, JS_FALSE);
3992 ++deleteCount;
3993 } else if (deleteCount != 0) {
3994 XMLARRAY_SET_MEMBER(array,
3995 index - deleteCount,
3996 array->vector[index]);
3999 array->length -= deleteCount;
4003 /* ECMA-357 9.2.1.3 index case. */
4004 static void
4005 DeleteListElement(JSContext *cx, JSXML *xml, uint32 index)
4007 JSXML *kid, *parent;
4008 uint32 kidIndex;
4010 JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST);
4012 if (index < xml->xml_kids.length) {
4013 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
4014 if (kid) {
4015 parent = kid->parent;
4016 if (parent) {
4017 JS_ASSERT(parent != xml);
4018 JS_ASSERT(JSXML_HAS_KIDS(parent));
4020 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4021 DeleteNamedProperty(cx, parent, kid->name, JS_TRUE);
4022 } else {
4023 kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
4024 NULL);
4025 JS_ASSERT(kidIndex != XML_NOT_FOUND);
4026 DeleteByIndex(cx, parent, kidIndex);
4029 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
4034 static JSBool
4035 SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
4037 JSXMLArray *nsarray;
4038 uint32 i, n;
4039 JSXMLNamespace *ns;
4041 nsarray = &xml->xml_namespaces;
4042 while ((xml = xml->parent) != NULL) {
4043 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
4044 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
4045 if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
4046 if (!XMLARRAY_APPEND(cx, nsarray, ns))
4047 return JS_FALSE;
4051 return JS_TRUE;
4054 static JSBool
4055 GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, JSXML *list)
4057 JSXMLArray *array;
4058 JSXMLNameMatcher matcher;
4059 JSXMLArrayCursor cursor;
4060 JSXML *kid;
4061 JSBool attrs;
4063 if (xml->xml_class == JSXML_CLASS_LIST) {
4064 XMLArrayCursorInit(&cursor, &xml->xml_kids);
4065 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
4066 if (kid->xml_class == JSXML_CLASS_ELEMENT &&
4067 !GetNamedProperty(cx, kid, nameqn, list)) {
4068 break;
4071 XMLArrayCursorFinish(&cursor);
4072 if (kid)
4073 return JS_FALSE;
4074 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4075 attrs = (OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass);
4076 if (attrs) {
4077 array = &xml->xml_attrs;
4078 matcher = MatchAttrName;
4079 } else {
4080 array = &xml->xml_kids;
4081 matcher = MatchElemName;
4084 XMLArrayCursorInit(&cursor, array);
4085 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
4086 if (matcher(nameqn, kid)) {
4087 if (!attrs &&
4088 kid->xml_class == JSXML_CLASS_ELEMENT &&
4089 !SyncInScopeNamespaces(cx, kid)) {
4090 break;
4092 if (!Append(cx, list, kid))
4093 break;
4096 XMLArrayCursorFinish(&cursor);
4097 if (kid)
4098 return JS_FALSE;
4101 return JS_TRUE;
4104 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
4105 static JSBool
4106 GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4108 JSXML *xml, *list, *kid;
4109 uint32 index;
4110 JSObject *kidobj, *listobj;
4111 JSXMLQName *nameqn;
4112 jsid funid;
4113 jsval roots[2];
4114 JSTempValueRooter tvr;
4116 xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
4117 if (!xml)
4118 return JS_TRUE;
4120 if (js_IdIsIndex(id, &index)) {
4121 if (xml->xml_class != JSXML_CLASS_LIST) {
4122 *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
4123 } else {
4125 * ECMA-357 9.2.1.1 starts here.
4127 * Erratum: 9.2 is not completely clear that indexed properties
4128 * correspond to kids, but that's what it seems to say, and it's
4129 * what any sane user would want.
4131 if (index < xml->xml_kids.length) {
4132 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
4133 if (!kid) {
4134 *vp = JSVAL_VOID;
4135 return JS_TRUE;
4137 kidobj = js_GetXMLObject(cx, kid);
4138 if (!kidobj)
4139 return JS_FALSE;
4141 *vp = OBJECT_TO_JSVAL(kidobj);
4142 } else {
4143 *vp = JSVAL_VOID;
4146 return JS_TRUE;
4150 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
4152 nameqn = ToXMLName(cx, id, &funid);
4153 if (!nameqn)
4154 return JS_FALSE;
4155 if (funid)
4156 return js_GetXMLFunction(cx, obj, funid, vp);
4158 roots[0] = OBJECT_TO_JSVAL(nameqn->object);
4159 JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr);
4161 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
4162 if (listobj) {
4163 roots[1] = OBJECT_TO_JSVAL(listobj);
4164 tvr.count++;
4166 list = (JSXML *) JS_GetPrivate(cx, listobj);
4167 if (!GetNamedProperty(cx, xml, nameqn, list)) {
4168 listobj = NULL;
4169 } else {
4171 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
4172 * given list's [[TargetProperty]] to the property that is being
4173 * appended. This means that any use of the internal [[Get]]
4174 * property returns a list which, when used by e.g. [[Insert]]
4175 * duplicates the last element matched by id. See bug 336921.
4177 list->xml_target = xml;
4178 list->xml_targetprop = nameqn;
4179 *vp = OBJECT_TO_JSVAL(listobj);
4183 JS_POP_TEMP_ROOT(cx, &tvr);
4184 return listobj != NULL;
4187 static JSXML *
4188 CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
4190 JS_ASSERT(xml->object != obj);
4192 xml = DeepCopy(cx, xml, obj, 0);
4193 if (!xml)
4194 return NULL;
4196 JS_ASSERT(xml->object == obj);
4197 return xml;
4200 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
4201 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
4203 static JSString *
4204 KidToString(JSContext *cx, JSXML *xml, uint32 index)
4206 JSXML *kid;
4207 JSObject *kidobj;
4209 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
4210 if (!kid)
4211 return cx->runtime->emptyString;
4212 kidobj = js_GetXMLObject(cx, kid);
4213 if (!kidobj)
4214 return NULL;
4215 return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj));
4218 /* Forward declared -- its implementation uses other statics that call it. */
4219 static JSBool
4220 ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
4222 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
4223 static JSBool
4224 PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4226 JSBool ok, primitiveAssign;
4227 enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
4228 jsval roots[3];
4229 JSTempValueRooter tvr;
4230 JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
4231 JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
4232 JSXMLQName *targetprop, *nameqn, *attrqn;
4233 uint32 index, i, j, k, n, q, matchIndex;
4234 jsval attrval, nsval;
4235 jsid funid;
4236 JSString *left, *right, *space;
4237 JSXMLNamespace *ns;
4239 xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
4240 if (!xml)
4241 return JS_TRUE;
4243 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
4244 if (!xml)
4245 return JS_FALSE;
4247 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
4248 vxml = NULL;
4249 if (!JSVAL_IS_PRIMITIVE(*vp)) {
4250 vobj = JSVAL_TO_OBJECT(*vp);
4251 if (OBJECT_IS_XML(cx, vobj))
4252 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4255 /* Control flow after here must exit via label out. */
4256 ok = js_EnterLocalRootScope(cx);
4257 if (!ok)
4258 return JS_FALSE;
4259 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4260 roots[ID_ROOT] = id;
4261 roots[VAL_ROOT] = *vp;
4262 JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr);
4264 if (js_IdIsIndex(id, &index)) {
4265 if (xml->xml_class != JSXML_CLASS_LIST) {
4266 /* See NOTE in spec: this variation is reserved for future use. */
4267 ReportBadXMLName(cx, id);
4268 goto bad;
4272 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
4274 i = index;
4276 /* 2(a-b). */
4277 if (xml->xml_target) {
4278 ok = ResolveValue(cx, xml->xml_target, &rxml);
4279 if (!ok)
4280 goto out;
4281 if (!rxml)
4282 goto out;
4283 JS_ASSERT(rxml->object);
4284 } else {
4285 rxml = NULL;
4288 /* 2(c). */
4289 if (index >= xml->xml_kids.length) {
4290 /* 2(c)(i). */
4291 if (rxml) {
4292 if (rxml->xml_class == JSXML_CLASS_LIST) {
4293 if (rxml->xml_kids.length != 1)
4294 goto out;
4295 rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
4296 if (!rxml)
4297 goto out;
4298 ok = js_GetXMLObject(cx, rxml) != NULL;
4299 if (!ok)
4300 goto out;
4304 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
4305 * _y.[[Parent]] = r_ where _r_ is the result of
4306 * [[ResolveValue]] called on _x.[[TargetObject]] in
4307 * 2(a)(i). This can result in text parenting text:
4309 * var MYXML = new XML();
4310 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
4312 * (testcase from Werner Sharp <wsharp@macromedia.com>).
4314 * To match insertChildAfter, insertChildBefore,
4315 * prependChild, and setChildren, we should silently
4316 * do nothing in this case.
4318 if (!JSXML_HAS_KIDS(rxml))
4319 goto out;
4322 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
4323 targetprop = xml->xml_targetprop;
4324 if (!targetprop || IS_STAR(targetprop->localName)) {
4325 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
4326 kid = js_NewXML(cx, JSXML_CLASS_TEXT);
4327 if (!kid)
4328 goto bad;
4329 } else {
4330 nameobj = js_GetXMLQNameObject(cx, targetprop);
4331 if (!nameobj)
4332 goto bad;
4333 if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
4335 * 2(c)(iii)(1-3).
4336 * Note that rxml can't be null here, because target
4337 * and targetprop are non-null.
4339 ok = GetProperty(cx, rxml->object, id, &attrval);
4340 if (!ok)
4341 goto out;
4342 if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
4343 goto out;
4344 attrobj = JSVAL_TO_OBJECT(attrval);
4345 attr = (JSXML *) JS_GetPrivate(cx, attrobj);
4346 if (JSXML_LENGTH(attr) != 0)
4347 goto out;
4349 kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4350 } else {
4351 /* 2(c)(v). */
4352 kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
4354 if (!kid)
4355 goto bad;
4357 /* An important bit of 2(c)(ii). */
4358 kid->name = targetprop;
4361 /* Final important bit of 2(c)(ii). */
4362 kid->parent = rxml;
4364 /* 2(c)(vi-vii). */
4365 i = xml->xml_kids.length;
4366 if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
4368 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
4369 * y.[[Parent]] is here called kid->parent, which we know
4370 * from 2(c)(ii) is _r_, here called rxml. So let's just
4371 * test that! Erratum, the spec should be simpler here.
4373 if (rxml) {
4374 JS_ASSERT(JSXML_HAS_KIDS(rxml));
4375 n = rxml->xml_kids.length;
4376 j = n - 1;
4377 if (n != 0 && i != 0) {
4378 for (n = j, j = 0; j < n; j++) {
4379 if (rxml->xml_kids.vector[j] ==
4380 xml->xml_kids.vector[i-1]) {
4381 break;
4386 kidobj = js_GetXMLObject(cx, kid);
4387 if (!kidobj)
4388 goto bad;
4389 ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4390 if (!ok)
4391 goto out;
4395 * 2(c)(vii)(2-3).
4396 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4397 * typo for [[TargetProperty]].
4399 if (vxml) {
4400 kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4401 ? vxml->xml_targetprop
4402 : vxml->name;
4406 /* 2(c)(viii). */
4407 ok = Append(cx, xml, kid);
4408 if (!ok)
4409 goto out;
4412 /* 2(d). */
4413 if (!vxml ||
4414 vxml->xml_class == JSXML_CLASS_TEXT ||
4415 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4416 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4417 if (!ok)
4418 goto out;
4419 roots[VAL_ROOT] = *vp;
4422 /* 2(e). */
4423 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4424 if (!kid)
4425 goto out;
4426 parent = kid->parent;
4427 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4428 nameobj = js_GetAttributeNameObject(cx, kid->name);
4429 if (!nameobj)
4430 goto bad;
4431 id = OBJECT_TO_JSVAL(nameobj);
4433 if (parent) {
4434 /* 2(e)(i). */
4435 parentobj = js_GetXMLObject(cx, parent);
4436 if (!parentobj)
4437 goto bad;
4438 ok = PutProperty(cx, parentobj, id, vp);
4439 if (!ok)
4440 goto out;
4442 /* 2(e)(ii). */
4443 ok = GetProperty(cx, parentobj, id, vp);
4444 if (!ok)
4445 goto out;
4446 attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
4448 /* 2(e)(iii). */
4449 xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
4453 /* 2(f). */
4454 else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4456 * 2(f)(i)
4458 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4459 * if we do that we never change the parent of each child in the
4460 * list. Since [[Put]] when called on an XML object deeply copies
4461 * the provided list _V_, we also do so here. Perhaps the shallow
4462 * copy was a misguided optimization?
4464 copy = DeepCopyInLRS(cx, vxml, 0);
4465 if (!copy)
4466 goto bad;
4467 copyobj = js_GetXMLObject(cx, copy);
4468 if (!copyobj)
4469 goto bad;
4471 JS_ASSERT(parent != xml);
4472 if (parent) {
4473 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4474 JS_ASSERT(q != XML_NOT_FOUND);
4475 ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj));
4476 if (!ok)
4477 goto out;
4479 #ifdef DEBUG
4480 /* Erratum: this loop in the spec is useless. */
4481 for (j = 0, n = copy->xml_kids.length; j < n; j++) {
4482 kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
4483 JS_ASSERT(XMLARRAY_MEMBER(&copy->xml_kids, j, JSXML)
4484 == kid2);
4486 #endif
4490 * 2(f)(iv-vi).
4491 * Erratum: notice the unhandled zero-length V basis case and
4492 * the off-by-one errors for the n != 0 cases in the spec.
4494 n = copy->xml_kids.length;
4495 if (n == 0) {
4496 XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4497 } else {
4498 ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4499 if (!ok)
4500 goto out;
4502 for (j = 0; j < n; j++)
4503 xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4507 /* 2(g). */
4508 else if (vxml || JSXML_HAS_VALUE(kid)) {
4509 if (parent) {
4510 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4511 JS_ASSERT(q != XML_NOT_FOUND);
4512 ok = Replace(cx, parent, q, *vp);
4513 if (!ok)
4514 goto out;
4516 vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4517 if (!vxml)
4518 goto out;
4519 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
4523 * 2(g)(iii).
4524 * Erratum: _V_ may not be of type XML, but all index-named
4525 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4526 * according to 9.2.1.1 Overview and other places in the spec.
4528 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4529 * or an XML/XMLList object. If *vp is a string, call ToXML
4530 * on it to satisfy the constraint.
4532 if (!vxml) {
4533 JS_ASSERT(JSVAL_IS_STRING(*vp));
4534 vobj = ToXML(cx, *vp);
4535 if (!vobj)
4536 goto bad;
4537 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4538 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4540 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4543 /* 2(h). */
4544 else {
4545 kidobj = js_GetXMLObject(cx, kid);
4546 if (!kidobj)
4547 goto bad;
4548 id = ATOM_KEY(cx->runtime->atomState.starAtom);
4549 ok = PutProperty(cx, kidobj, id, vp);
4550 if (!ok)
4551 goto out;
4553 } else {
4555 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4557 nameqn = ToXMLName(cx, id, &funid);
4558 if (!nameqn)
4559 goto bad;
4560 if (funid) {
4561 ok = js_SetProperty(cx, obj, funid, vp);
4562 goto out;
4564 nameobj = nameqn->object;
4565 roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
4567 if (xml->xml_class == JSXML_CLASS_LIST) {
4569 * Step 3 of 9.2.1.2.
4570 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4571 * or an r with r.[[Length]] != 1, throw TypeError.
4573 n = JSXML_LENGTH(xml);
4574 if (n > 1)
4575 goto type_error;
4576 if (n == 0) {
4577 ok = ResolveValue(cx, xml, &rxml);
4578 if (!ok)
4579 goto out;
4580 if (!rxml || JSXML_LENGTH(rxml) != 1)
4581 goto type_error;
4582 ok = Append(cx, xml, rxml);
4583 if (!ok)
4584 goto out;
4586 JS_ASSERT(JSXML_LENGTH(xml) == 1);
4587 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4588 if (!xml)
4589 goto out;
4590 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
4591 obj = js_GetXMLObject(cx, xml);
4592 if (!obj)
4593 goto bad;
4594 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4596 /* FALL THROUGH to non-list case */
4600 * ECMA-357 9.1.1.2.
4601 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4602 * effort in ToString or [[DeepCopy]].
4605 if (JSXML_HAS_VALUE(xml))
4606 goto out;
4608 if (!vxml ||
4609 vxml->xml_class == JSXML_CLASS_TEXT ||
4610 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4611 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4612 if (!ok)
4613 goto out;
4614 } else {
4615 rxml = DeepCopyInLRS(cx, vxml, 0);
4616 if (!rxml || !js_GetXMLObject(cx, rxml))
4617 goto bad;
4618 vxml = rxml;
4619 *vp = OBJECT_TO_JSVAL(vxml->object);
4621 roots[VAL_ROOT] = *vp;
4624 * 6.
4625 * Erratum: why is this done here, so early? use is way later....
4627 ok = js_GetDefaultXMLNamespace(cx, &nsval);
4628 if (!ok)
4629 goto out;
4631 if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
4632 /* 7(a). */
4633 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4634 goto out;
4636 /* 7(b-c). */
4637 if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4638 n = vxml->xml_kids.length;
4639 if (n == 0) {
4640 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4641 } else {
4642 left = KidToString(cx, vxml, 0);
4643 if (!left)
4644 goto bad;
4646 space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom);
4647 for (i = 1; i < n; i++) {
4648 left = js_ConcatStrings(cx, left, space);
4649 if (!left)
4650 goto bad;
4651 right = KidToString(cx, vxml, i);
4652 if (!right)
4653 goto bad;
4654 left = js_ConcatStrings(cx, left, right);
4655 if (!left)
4656 goto bad;
4659 roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4661 } else {
4662 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4663 if (!ok)
4664 goto out;
4665 roots[VAL_ROOT] = *vp;
4668 /* 7(d-e). */
4669 match = NULL;
4670 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4671 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4672 if (!attr)
4673 continue;
4674 attrqn = attr->name;
4675 if (js_EqualStrings(attrqn->localName, nameqn->localName) &&
4676 (!nameqn->uri ||
4677 js_EqualStrings(attrqn->uri, nameqn->uri))) {
4678 if (!match) {
4679 match = attr;
4680 } else {
4681 DeleteNamedProperty(cx, xml, attrqn, JS_TRUE);
4682 --i;
4687 /* 7(f). */
4688 attr = match;
4689 if (!attr) {
4690 /* 7(f)(i-ii). */
4691 if (!nameqn->uri) {
4692 left = right = cx->runtime->emptyString;
4693 } else {
4694 left = nameqn->uri;
4695 right = nameqn->prefix;
4697 nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
4698 if (!nameqn)
4699 goto bad;
4701 /* 7(f)(iii). */
4702 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4703 if (!attr)
4704 goto bad;
4705 attr->parent = xml;
4706 attr->name = nameqn;
4708 /* 7(f)(iv). */
4709 ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4710 if (!ok)
4711 goto out;
4713 /* 7(f)(v-vi). */
4714 ns = GetNamespace(cx, nameqn, NULL);
4715 if (!ns)
4716 goto bad;
4717 ok = AddInScopeNamespace(cx, xml, ns);
4718 if (!ok)
4719 goto out;
4722 /* 7(g). */
4723 attr->xml_value = JSVAL_TO_STRING(*vp);
4724 goto out;
4727 /* 8-9. */
4728 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4729 !IS_STAR(nameqn->localName)) {
4730 goto out;
4733 /* 10-11. */
4734 id = JSVAL_VOID;
4735 primitiveAssign = !vxml && !IS_STAR(nameqn->localName);
4737 /* 12. */
4738 k = n = xml->xml_kids.length;
4739 matchIndex = XML_NOT_FOUND;
4740 kid2 = NULL;
4741 while (k != 0) {
4742 --k;
4743 kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
4744 if (kid && MatchElemName(nameqn, kid)) {
4745 if (matchIndex != XML_NOT_FOUND)
4746 DeleteByIndex(cx, xml, matchIndex);
4747 matchIndex = k;
4748 kid2 = kid;
4753 * Erratum: ECMA-357 specified child insertion inconsistently:
4754 * insertChildBefore and insertChildAfter insert an arbitrary XML
4755 * instance, and therefore can create cycles, but appendChild as
4756 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4757 * its argument. But the "Semantics" in 13.4.4.3 do not include
4758 * any [[DeepCopy]] call.
4760 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4761 * required adding cycle detection, and allowing duplicate kids to
4762 * be created (see comment 6 in the bug). Allowing duplicate kid
4763 * references means the loop above will delete all but the lowest
4764 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4765 * parent. Thus the need to restore parent here. This is covered
4766 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4768 if (kid2) {
4769 JS_ASSERT(kid2->parent == xml || !kid2->parent);
4770 if (!kid2->parent)
4771 kid2->parent = xml;
4774 /* 13. */
4775 if (matchIndex == XML_NOT_FOUND) {
4776 /* 13(a). */
4777 matchIndex = n;
4779 /* 13(b). */
4780 if (primitiveAssign) {
4781 if (!nameqn->uri) {
4782 ns = (JSXMLNamespace *)
4783 JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
4784 left = ns->uri;
4785 right = ns->prefix;
4786 } else {
4787 left = nameqn->uri;
4788 right = nameqn->prefix;
4790 nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
4791 if (!nameqn)
4792 goto bad;
4794 /* 13(b)(iii). */
4795 vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4796 if (!vobj)
4797 goto bad;
4798 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4799 vxml->parent = xml;
4800 vxml->name = nameqn;
4802 /* 13(b)(iv-vi). */
4803 ns = GetNamespace(cx, nameqn, NULL);
4804 if (!ns)
4805 goto bad;
4806 ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj));
4807 if (!ok)
4808 goto out;
4809 ok = AddInScopeNamespace(cx, vxml, ns);
4810 if (!ok)
4811 goto out;
4815 /* 14. */
4816 if (primitiveAssign) {
4817 JSXMLArrayCursor cursor;
4819 XMLArrayCursorInit(&cursor, &xml->xml_kids);
4820 cursor.index = matchIndex;
4821 kid = (JSXML *) XMLArrayCursorItem(&cursor);
4822 if (JSXML_HAS_KIDS(kid)) {
4823 XMLArrayFinish(cx, &kid->xml_kids);
4824 ok = XMLArrayInit(cx, &kid->xml_kids, 1);
4827 /* 14(b-c). */
4828 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4829 if (ok) {
4830 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4831 if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) {
4832 roots[VAL_ROOT] = *vp;
4833 if ((JSXML *) XMLArrayCursorItem(&cursor) == kid)
4834 ok = Replace(cx, kid, 0, *vp);
4837 XMLArrayCursorFinish(&cursor);
4838 } else {
4839 /* 15(a). */
4840 ok = Replace(cx, xml, matchIndex, *vp);
4844 out:
4845 JS_POP_TEMP_ROOT(cx, &tvr);
4846 js_LeaveLocalRootScope(cx);
4847 return ok;
4849 type_error:
4850 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4851 JSMSG_BAD_XMLLIST_PUT,
4852 js_ValueToPrintableString(cx, id));
4853 bad:
4854 ok = JS_FALSE;
4855 goto out;
4858 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4859 static JSBool
4860 ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4862 JSXML *target, *base;
4863 JSXMLQName *targetprop;
4864 JSObject *targetpropobj;
4865 jsval id, tv;
4867 /* Our caller must be protecting newborn objects. */
4868 JS_ASSERT(cx->localRootStack);
4870 if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4871 if (!js_GetXMLObject(cx, list))
4872 return JS_FALSE;
4873 *result = list;
4874 return JS_TRUE;
4877 target = list->xml_target;
4878 targetprop = list->xml_targetprop;
4879 if (!target || !targetprop || IS_STAR(targetprop->localName)) {
4880 *result = NULL;
4881 return JS_TRUE;
4884 targetpropobj = js_GetXMLQNameObject(cx, targetprop);
4885 if (!targetpropobj)
4886 return JS_FALSE;
4887 if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) {
4888 *result = NULL;
4889 return JS_TRUE;
4892 if (!ResolveValue(cx, target, &base))
4893 return JS_FALSE;
4894 if (!base) {
4895 *result = NULL;
4896 return JS_TRUE;
4898 if (!js_GetXMLObject(cx, base))
4899 return JS_FALSE;
4901 id = OBJECT_TO_JSVAL(targetpropobj);
4902 if (!GetProperty(cx, base->object, id, &tv))
4903 return JS_FALSE;
4904 target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
4906 if (JSXML_LENGTH(target) == 0) {
4907 if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4908 *result = NULL;
4909 return JS_TRUE;
4911 tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4912 if (!PutProperty(cx, base->object, id, &tv))
4913 return JS_FALSE;
4914 if (!GetProperty(cx, base->object, id, &tv))
4915 return JS_FALSE;
4916 target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
4919 *result = target;
4920 return JS_TRUE;
4923 static JSBool
4924 HasNamedProperty(JSXML *xml, JSXMLQName *nameqn)
4926 JSBool found;
4927 JSXMLArrayCursor cursor;
4928 JSXML *kid;
4929 JSXMLArray *array;
4930 JSXMLNameMatcher matcher;
4931 uint32 i, n;
4933 if (xml->xml_class == JSXML_CLASS_LIST) {
4934 found = JS_FALSE;
4935 XMLArrayCursorInit(&cursor, &xml->xml_kids);
4936 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
4937 found = HasNamedProperty(kid, nameqn);
4938 if (found)
4939 break;
4941 XMLArrayCursorFinish(&cursor);
4942 return found;
4945 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4946 if (OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) {
4947 array = &xml->xml_attrs;
4948 matcher = MatchAttrName;
4949 } else {
4950 array = &xml->xml_kids;
4951 matcher = MatchElemName;
4953 for (i = 0, n = array->length; i < n; i++) {
4954 kid = XMLARRAY_MEMBER(array, i, JSXML);
4955 if (kid && matcher(nameqn, kid))
4956 return JS_TRUE;
4960 return JS_FALSE;
4963 static JSBool
4964 HasIndexedProperty(JSXML *xml, uint32 i)
4966 if (xml->xml_class == JSXML_CLASS_LIST)
4967 return i < JSXML_LENGTH(xml);
4969 if (xml->xml_class == JSXML_CLASS_ELEMENT)
4970 return i == 0;
4972 return JS_FALSE;
4975 static JSBool
4976 HasSimpleContent(JSXML *xml);
4978 static JSBool
4979 HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
4981 JSObject *pobj;
4982 JSProperty *prop;
4983 JSXML *xml;
4984 JSTempValueRooter tvr;
4985 JSBool ok;
4987 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_XMLClass);
4989 if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
4990 return JS_FALSE;
4991 if (prop) {
4992 OBJ_DROP_PROPERTY(cx, pobj, prop);
4993 } else {
4994 xml = (JSXML *) JS_GetPrivate(cx, obj);
4995 if (HasSimpleContent(xml)) {
4997 * Search in String.prototype to set found whenever
4998 * js_GetXMLFunction returns existing function.
5000 JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
5001 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
5002 &tvr.u.object);
5003 JS_ASSERT(tvr.u.object);
5004 if (ok) {
5005 ok = js_LookupProperty(cx, tvr.u.object, funid, &pobj, &prop);
5006 if (ok && prop)
5007 OBJ_DROP_PROPERTY(cx, pobj, prop);
5009 JS_POP_TEMP_ROOT(cx, &tvr);
5010 if (!ok)
5011 return JS_FALSE;
5014 *found = (prop != NULL);
5015 return JS_TRUE;
5018 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
5019 static JSBool
5020 HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found)
5022 JSXML *xml;
5023 uint32 i;
5024 JSXMLQName *qn;
5025 jsid funid;
5027 xml = (JSXML *) JS_GetPrivate(cx, obj);
5028 if (js_IdIsIndex(id, &i)) {
5029 *found = HasIndexedProperty(xml, i);
5030 } else {
5031 qn = ToXMLName(cx, id, &funid);
5032 if (!qn)
5033 return JS_FALSE;
5034 if (funid) {
5035 if (!HasFunctionProperty(cx, obj, funid, found))
5036 return JS_FALSE;
5037 } else {
5038 *found = HasNamedProperty(xml, qn);
5041 return JS_TRUE;
5044 static void
5045 xml_finalize(JSContext *cx, JSObject *obj)
5047 JSXML *xml;
5049 xml = (JSXML *) JS_GetPrivate(cx, obj);
5050 if (!xml)
5051 return;
5052 if (xml->object == obj)
5053 xml->object = NULL;
5054 UNMETER(xml_stats.livexmlobj);
5057 static void
5058 xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
5060 uint32 i;
5061 JSXML *xml;
5063 for (i = 0; i < len; i++) {
5064 xml = vec[i];
5065 if (xml) {
5066 JS_SET_TRACING_INDEX(trc, "xml_vector", i);
5067 JS_CallTracer(trc, xml, JSTRACE_XML);
5073 * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
5074 * be native. Therefore, xml_lookupProperty must return a valid JSProperty
5075 * pointer parameter via *propp to signify "property found". Since the only
5076 * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
5077 * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case
5078 * in the interpreter, the only time we add a JSScopeProperty here is when an
5079 * unqualified name is being accessed or when "name in xml" is called.
5081 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
5082 * giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
5084 * NB: xml_deleteProperty must take care to remove any property added here.
5086 * FIXME This clashes with the function namespace implementation which also
5087 * uses native properties. Effectively after xml_lookupProperty any property
5088 * stored previously using assignments to xml.function::name will be removed.
5089 * We partially workaround the problem in js_GetXMLFunction. There we take
5090 * advantage of the fact that typically function:: is used to access the
5091 * functions from XML.prototype. So when js_GetProperty returns a non-function
5092 * property, we assume that it represents the result of GetProperty setter
5093 * hiding the function and use an extra prototype chain lookup to recover it.
5094 * For a proper solution see bug 355257.
5096 static JSBool
5097 xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
5098 JSProperty **propp)
5100 jsval v;
5101 JSBool found;
5102 JSXML *xml;
5103 uint32 i;
5104 JSXMLQName *qn;
5105 jsid funid;
5106 JSScopeProperty *sprop;
5108 v = ID_TO_VALUE(id);
5109 xml = (JSXML *) JS_GetPrivate(cx, obj);
5110 if (js_IdIsIndex(v, &i)) {
5111 found = HasIndexedProperty(xml, i);
5112 } else {
5113 qn = ToXMLName(cx, v, &funid);
5114 if (!qn)
5115 return JS_FALSE;
5116 if (funid)
5117 return js_LookupProperty(cx, obj, funid, objp, propp);
5118 found = HasNamedProperty(xml, qn);
5120 if (!found) {
5121 *objp = NULL;
5122 *propp = NULL;
5123 } else {
5124 sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
5125 SPROP_INVALID_SLOT, JSPROP_ENUMERATE,
5126 0, 0);
5127 if (!sprop)
5128 return JS_FALSE;
5130 JS_LOCK_OBJ(cx, obj);
5131 *objp = obj;
5132 *propp = (JSProperty *) sprop;
5134 return JS_TRUE;
5137 static JSBool
5138 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
5139 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
5140 JSProperty **propp)
5142 if (VALUE_IS_FUNCTION(cx, value) || getter || setter ||
5143 (attrs & JSPROP_ENUMERATE) == 0 ||
5144 (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
5145 return js_DefineProperty(cx, obj, id, value, getter, setter, attrs,
5146 propp);
5149 if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value))
5150 return JS_FALSE;
5151 if (propp)
5152 *propp = NULL;
5153 return JS_TRUE;
5156 static JSBool
5157 xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5159 if (id == JS_DEFAULT_XML_NAMESPACE_ID) {
5160 *vp = JSVAL_VOID;
5161 return JS_TRUE;
5164 return GetProperty(cx, obj, ID_TO_VALUE(id), vp);
5167 static JSBool
5168 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5170 return PutProperty(cx, obj, ID_TO_VALUE(id), vp);
5173 static JSBool
5174 FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5175 JSBool *foundp)
5177 if (!prop)
5178 return HasProperty(cx, obj, ID_TO_VALUE(id), foundp);
5180 *foundp = JS_TRUE;
5181 return JS_TRUE;
5184 static JSBool
5185 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5186 uintN *attrsp)
5188 JSBool found;
5190 if (!FoundProperty(cx, obj, id, prop, &found))
5191 return JS_FALSE;
5192 *attrsp = found ? JSPROP_ENUMERATE : 0;
5193 return JS_TRUE;
5196 static JSBool
5197 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5198 uintN *attrsp)
5200 JSBool found;
5202 if (!FoundProperty(cx, obj, id, prop, &found))
5203 return JS_FALSE;
5204 if (found) {
5205 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5206 JSMSG_CANT_SET_XML_ATTRS);
5208 return !found;
5211 static JSBool
5212 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
5214 JSXML *xml;
5215 jsval idval;
5216 uint32 index;
5217 JSXMLQName *nameqn;
5218 jsid funid;
5220 idval = ID_TO_VALUE(id);
5221 xml = (JSXML *) JS_GetPrivate(cx, obj);
5222 if (js_IdIsIndex(idval, &index)) {
5223 if (xml->xml_class != JSXML_CLASS_LIST) {
5224 /* See NOTE in spec: this variation is reserved for future use. */
5225 ReportBadXMLName(cx, id);
5226 return JS_FALSE;
5229 /* ECMA-357 9.2.1.3. */
5230 DeleteListElement(cx, xml, index);
5231 } else {
5232 nameqn = ToXMLName(cx, idval, &funid);
5233 if (!nameqn)
5234 return JS_FALSE;
5235 if (funid)
5236 return js_DeleteProperty(cx, obj, funid, rval);
5238 DeleteNamedProperty(cx, xml, nameqn,
5239 OBJ_GET_CLASS(cx, nameqn->object) ==
5240 &js_AttributeNameClass);
5244 * If this object has its own (mutable) scope, then we may have added a
5245 * property to the scope in xml_lookupProperty for it to return to mean
5246 * "found" and to provide a handle for access operations to call the
5247 * property's getter or setter. But now it's time to remove any such
5248 * property, to purge the property cache and remove the scope entry.
5250 if (OBJ_SCOPE(obj)->object == obj && !js_DeleteProperty(cx, obj, id, rval))
5251 return JS_FALSE;
5253 *rval = JSVAL_TRUE;
5254 return JS_TRUE;
5257 static JSBool
5258 xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
5260 JSXML *xml;
5262 if (hint == JSTYPE_OBJECT) {
5263 /* Called from for..in code in js_Interpret: return an XMLList. */
5264 xml = (JSXML *) JS_GetPrivate(cx, obj);
5265 if (xml->xml_class != JSXML_CLASS_LIST) {
5266 obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj));
5267 if (!obj)
5268 return JS_FALSE;
5270 *vp = OBJECT_TO_JSVAL(obj);
5271 return JS_TRUE;
5274 return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp);
5277 static JSBool
5278 xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
5279 jsval *statep, jsid *idp)
5281 JSXML *xml;
5282 uint32 length, index;
5283 JSXMLArrayCursor *cursor;
5285 xml = (JSXML *) JS_GetPrivate(cx, obj);
5286 length = JSXML_LENGTH(xml);
5288 switch (enum_op) {
5289 case JSENUMERATE_INIT:
5290 if (length == 0) {
5291 cursor = NULL;
5292 } else {
5293 cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
5294 if (!cursor)
5295 return JS_FALSE;
5296 XMLArrayCursorInit(cursor, &xml->xml_kids);
5298 *statep = PRIVATE_TO_JSVAL(cursor);
5299 if (idp)
5300 *idp = INT_TO_JSID(length);
5301 break;
5303 case JSENUMERATE_NEXT:
5304 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5305 if (cursor && cursor->array && (index = cursor->index) < length) {
5306 *idp = INT_TO_JSID(index);
5307 cursor->index = index + 1;
5308 break;
5310 /* FALL THROUGH */
5312 case JSENUMERATE_DESTROY:
5313 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5314 if (cursor) {
5315 XMLArrayCursorFinish(cursor);
5316 JS_free(cx, cursor);
5318 *statep = JSVAL_NULL;
5319 break;
5321 return JS_TRUE;
5324 static JSBool
5325 xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
5327 return JS_TRUE;
5330 static void
5331 xml_trace(JSTracer *trc, JSObject *obj)
5333 JSXML *xml;
5335 xml = (JSXML *) JS_GetPrivate(trc->context, obj);
5336 if (xml)
5337 JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private");
5340 static void
5341 xml_clear(JSContext *cx, JSObject *obj)
5345 static JSBool
5346 HasSimpleContent(JSXML *xml)
5348 JSXML *kid;
5349 JSBool simple;
5350 uint32 i, n;
5352 again:
5353 switch (xml->xml_class) {
5354 case JSXML_CLASS_COMMENT:
5355 case JSXML_CLASS_PROCESSING_INSTRUCTION:
5356 return JS_FALSE;
5357 case JSXML_CLASS_LIST:
5358 if (xml->xml_kids.length == 0)
5359 return JS_TRUE;
5360 if (xml->xml_kids.length == 1) {
5361 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5362 if (kid) {
5363 xml = kid;
5364 goto again;
5367 /* FALL THROUGH */
5368 default:
5369 simple = JS_TRUE;
5370 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5371 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5372 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5373 simple = JS_FALSE;
5374 break;
5377 return simple;
5382 * 11.2.2.1 Step 3(d) onward.
5384 static JSObject *
5385 xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5387 JSTempValueRooter tvr;
5389 JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL));
5392 * As our callers have a bad habit of passing a pointer to an unrooted
5393 * local value as vp, we use a proper root here.
5395 JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
5396 if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value))
5397 obj = NULL;
5398 *vp = tvr.u.value;
5399 JS_POP_TEMP_ROOT(cx, &tvr);
5400 return obj;
5403 static JSBool
5404 xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5406 return js_SetProperty(cx, obj, id, vp);
5409 static JSBool
5410 xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
5411 jsval *statep, jsid *idp, jsval *vp)
5413 JSXML *xml, *kid;
5414 uint32 length, index;
5415 JSXMLArrayCursor *cursor;
5416 JSObject *kidobj;
5418 xml = (JSXML *) JS_GetPrivate(cx, obj);
5419 length = JSXML_LENGTH(xml);
5420 JS_ASSERT(INT_FITS_IN_JSVAL(length));
5422 switch (enum_op) {
5423 case JSENUMERATE_INIT:
5424 if (length == 0) {
5425 cursor = NULL;
5426 } else {
5427 cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
5428 if (!cursor)
5429 return JS_FALSE;
5430 XMLArrayCursorInit(cursor, &xml->xml_kids);
5432 *statep = PRIVATE_TO_JSVAL(cursor);
5433 if (idp)
5434 *idp = INT_TO_JSID(length);
5435 if (vp)
5436 *vp = JSVAL_VOID;
5437 break;
5439 case JSENUMERATE_NEXT:
5440 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5441 if (cursor && cursor->array && (index = cursor->index) < length) {
5442 while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) {
5443 if (++index == length)
5444 goto destroy;
5446 kidobj = js_GetXMLObject(cx, kid);
5447 if (!kidobj)
5448 return JS_FALSE;
5449 JS_ASSERT(INT_FITS_IN_JSVAL(index));
5450 *idp = INT_TO_JSID(index);
5451 *vp = OBJECT_TO_JSVAL(kidobj);
5452 cursor->index = index + 1;
5453 break;
5455 /* FALL THROUGH */
5457 case JSENUMERATE_DESTROY:
5458 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5459 if (cursor) {
5460 destroy:
5461 XMLArrayCursorFinish(cursor);
5462 JS_free(cx, cursor);
5464 *statep = JSVAL_NULL;
5465 break;
5467 return JS_TRUE;
5470 static JSBool
5471 xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
5473 JSXML *xml, *vxml;
5474 JSObject *vobj;
5475 JSBool ok;
5476 JSString *str, *vstr;
5477 jsdouble d, d2;
5479 xml = (JSXML *) JS_GetPrivate(cx, obj);
5480 vxml = NULL;
5481 if (!JSVAL_IS_PRIMITIVE(v)) {
5482 vobj = JSVAL_TO_OBJECT(v);
5483 if (OBJECT_IS_XML(cx, vobj))
5484 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
5487 if (xml->xml_class == JSXML_CLASS_LIST) {
5488 ok = Equals(cx, xml, v, bp);
5489 } else if (vxml) {
5490 if (vxml->xml_class == JSXML_CLASS_LIST) {
5491 ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
5492 } else {
5493 if (((xml->xml_class == JSXML_CLASS_TEXT ||
5494 xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5495 HasSimpleContent(vxml)) ||
5496 ((vxml->xml_class == JSXML_CLASS_TEXT ||
5497 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5498 HasSimpleContent(xml))) {
5499 ok = js_EnterLocalRootScope(cx);
5500 if (ok) {
5501 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5502 vstr = js_ValueToString(cx, v);
5503 ok = str && vstr;
5504 if (ok)
5505 *bp = js_EqualStrings(str, vstr);
5506 js_LeaveLocalRootScope(cx);
5508 } else {
5509 ok = XMLEquals(cx, xml, vxml, bp);
5512 } else {
5513 ok = js_EnterLocalRootScope(cx);
5514 if (ok) {
5515 if (HasSimpleContent(xml)) {
5516 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5517 vstr = js_ValueToString(cx, v);
5518 ok = str && vstr;
5519 if (ok)
5520 *bp = js_EqualStrings(str, vstr);
5521 } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
5522 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5523 if (!str) {
5524 ok = JS_FALSE;
5525 } else if (JSVAL_IS_STRING(v)) {
5526 *bp = js_EqualStrings(str, JSVAL_TO_STRING(v));
5527 } else {
5528 ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5529 if (ok) {
5530 d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5531 : *JSVAL_TO_DOUBLE(v);
5532 *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
5535 } else {
5536 *bp = JS_FALSE;
5538 js_LeaveLocalRootScope(cx);
5541 return ok;
5544 static JSBool
5545 xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp)
5547 JSBool ok;
5548 JSObject *listobj, *robj;
5549 JSXML *list, *lxml, *rxml;
5551 ok = js_EnterLocalRootScope(cx);
5552 if (!ok)
5553 return JS_FALSE;
5555 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5556 if (!listobj) {
5557 ok = JS_FALSE;
5558 goto out;
5561 list = (JSXML *) JS_GetPrivate(cx, listobj);
5562 lxml = (JSXML *) JS_GetPrivate(cx, obj);
5563 ok = Append(cx, list, lxml);
5564 if (!ok)
5565 goto out;
5567 if (VALUE_IS_XML(cx, v)) {
5568 rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5569 } else {
5570 robj = ToXML(cx, v);
5571 if (!robj) {
5572 ok = JS_FALSE;
5573 goto out;
5575 rxml = (JSXML *) JS_GetPrivate(cx, robj);
5577 ok = Append(cx, list, rxml);
5578 if (!ok)
5579 goto out;
5581 *vp = OBJECT_TO_JSVAL(listobj);
5582 out:
5583 js_LeaveLocalRootScopeWithResult(cx, *vp);
5584 return ok;
5587 /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */
5588 JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = {
5589 { js_NewObjectMap, js_DestroyObjectMap,
5590 xml_lookupProperty, xml_defineProperty,
5591 xml_getProperty, xml_setProperty,
5592 xml_getAttributes, xml_setAttributes,
5593 xml_deleteProperty, xml_defaultValue,
5594 xml_enumerate, js_CheckAccess,
5595 NULL, NULL,
5596 NULL, NULL,
5597 NULL, xml_hasInstance,
5598 js_SetProtoOrParent, js_SetProtoOrParent,
5599 js_TraceObject, xml_clear,
5600 NULL, NULL },
5601 xml_getMethod, xml_setMethod,
5602 xml_enumerateValues, xml_equality,
5603 xml_concatenate
5606 static JSObjectOps *
5607 xml_getObjectOps(JSContext *cx, JSClass *clasp)
5609 return &js_XMLObjectOps.base;
5612 JS_FRIEND_DATA(JSClass) js_XMLClass = {
5613 js_XML_str,
5614 JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
5615 JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
5616 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
5617 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize,
5618 xml_getObjectOps, NULL, NULL, NULL,
5619 NULL, NULL, JS_CLASS_TRACE(xml_trace), NULL
5622 static JSXML *
5623 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
5625 JSXML *xml;
5626 JSFunction *fun;
5627 char numBuf[12];
5629 JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
5631 *objp = JS_THIS_OBJECT(cx, vp);
5632 xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, vp + 2);
5633 if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5634 return xml;
5636 if (xml->xml_kids.length == 1) {
5637 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5638 if (xml) {
5639 *objp = js_GetXMLObject(cx, xml);
5640 if (!*objp)
5641 return NULL;
5642 vp[1] = OBJECT_TO_JSVAL(*objp);
5643 return xml;
5647 fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp));
5648 JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
5649 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5650 JSMSG_NON_LIST_XML_METHOD,
5651 JS_GetFunctionName(fun), numBuf);
5652 return NULL;
5655 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5656 #define XML_METHOD_PROLOG \
5657 JSObject *obj = JS_THIS_OBJECT(cx, vp); \
5658 JSXML *xml = (JSXML *)JS_GetInstancePrivate(cx, obj, &js_XMLClass, vp+2); \
5659 if (!xml) \
5660 return JS_FALSE
5662 #define NON_LIST_XML_METHOD_PROLOG \
5663 JSObject *obj; \
5664 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5665 if (!xml) \
5666 return JS_FALSE; \
5667 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5669 static JSBool
5670 xml_addNamespace(JSContext *cx, uintN argc, jsval *vp)
5672 JSXMLNamespace *ns;
5674 NON_LIST_XML_METHOD_PROLOG;
5675 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5676 goto done;
5677 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5678 if (!xml)
5679 return JS_FALSE;
5681 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
5682 return JS_FALSE;
5683 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5685 ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
5686 if (!AddInScopeNamespace(cx, xml, ns))
5687 return JS_FALSE;
5688 ns->declared = JS_TRUE;
5690 done:
5691 *vp = OBJECT_TO_JSVAL(obj);
5692 return JS_TRUE;
5695 static JSBool
5696 xml_appendChild(JSContext *cx, uintN argc, jsval *vp)
5698 jsval name, v;
5699 JSObject *vobj;
5700 JSXML *vxml;
5702 NON_LIST_XML_METHOD_PROLOG;
5703 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5704 if (!xml)
5705 return JS_FALSE;
5707 if (!js_GetAnyName(cx, &name))
5708 return JS_FALSE;
5710 if (!GetProperty(cx, obj, name, &v))
5711 return JS_FALSE;
5713 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5714 vobj = JSVAL_TO_OBJECT(v);
5715 JS_ASSERT(OBJECT_IS_XML(cx, vobj));
5716 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
5717 JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
5719 if (!IndexToIdVal(cx, vxml->xml_kids.length, &name))
5720 return JS_FALSE;
5721 *vp = (argc != 0) ? vp[2] : JSVAL_VOID;
5722 if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, vp))
5723 return JS_FALSE;
5725 *vp = OBJECT_TO_JSVAL(obj);
5726 return JS_TRUE;
5729 /* XML and XMLList */
5730 static JSBool
5731 xml_attribute(JSContext *cx, uintN argc, jsval *vp)
5733 JSXMLQName *qn;
5735 if (argc == 0) {
5736 js_ReportMissingArg(cx, vp, 0);
5737 return JS_FALSE;
5740 qn = ToAttributeName(cx, vp[2]);
5741 if (!qn)
5742 return JS_FALSE;
5743 vp[2] = OBJECT_TO_JSVAL(qn->object); /* local root */
5745 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), vp[2], vp);
5748 /* XML and XMLList */
5749 static JSBool
5750 xml_attributes(JSContext *cx, uintN argc, jsval *vp)
5752 jsval name;
5753 JSXMLQName *qn;
5754 JSTempValueRooter tvr;
5755 JSBool ok;
5757 name = ATOM_KEY(cx->runtime->atomState.starAtom);
5758 qn = ToAttributeName(cx, name);
5759 if (!qn)
5760 return JS_FALSE;
5761 name = OBJECT_TO_JSVAL(qn->object);
5762 JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr);
5763 ok = GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
5764 JS_POP_TEMP_ROOT(cx, &tvr);
5765 return ok;
5768 static JSXML *
5769 xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5771 JSObject *listobj;
5772 JSXML *list;
5774 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5775 if (!listobj)
5776 return NULL;
5778 *rval = OBJECT_TO_JSVAL(listobj);
5779 list = (JSXML *) JS_GetPrivate(cx, listobj);
5780 list->xml_target = xml;
5781 return list;
5784 static JSBool
5785 xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5786 jsval *rval)
5788 uint32 index;
5789 JSXML *kid;
5790 JSObject *kidobj;
5792 /* ECMA-357 13.4.4.6 */
5793 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5795 if (js_IdIsIndex(name, &index)) {
5796 if (index >= JSXML_LENGTH(xml)) {
5797 *rval = JSVAL_VOID;
5798 } else {
5799 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5800 if (!kid) {
5801 *rval = JSVAL_VOID;
5802 } else {
5803 kidobj = js_GetXMLObject(cx, kid);
5804 if (!kidobj)
5805 return JS_FALSE;
5806 *rval = OBJECT_TO_JSVAL(kidobj);
5809 return JS_TRUE;
5812 return GetProperty(cx, obj, name, rval);
5815 /* XML and XMLList */
5816 static JSBool
5817 xml_child(JSContext *cx, uintN argc, jsval *vp)
5819 jsval name, v;
5820 JSXML *list, *kid, *vxml;
5821 JSXMLArrayCursor cursor;
5822 JSObject *kidobj;
5824 XML_METHOD_PROLOG;
5825 name = argc != 0 ? vp[2] : JSVAL_VOID;
5826 if (xml->xml_class == JSXML_CLASS_LIST) {
5827 /* ECMA-357 13.5.4.4 */
5828 list = xml_list_helper(cx, xml, vp);
5829 if (!list)
5830 return JS_FALSE;
5832 XMLArrayCursorInit(&cursor, &xml->xml_kids);
5833 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
5834 kidobj = js_GetXMLObject(cx, kid);
5835 if (!kidobj)
5836 break;
5837 if (!xml_child_helper(cx, kidobj, kid, name, &v))
5838 break;
5839 if (JSVAL_IS_VOID(v)) {
5840 /* The property didn't exist in this kid. */
5841 continue;
5844 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5845 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5846 if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
5847 !Append(cx, list, vxml)) {
5848 break;
5851 XMLArrayCursorFinish(&cursor);
5852 return !kid;
5855 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5856 if (!xml_child_helper(cx, obj, xml, name, vp))
5857 return JS_FALSE;
5858 if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp))
5859 return JS_FALSE;
5860 return JS_TRUE;
5863 static JSBool
5864 xml_childIndex(JSContext *cx, uintN argc, jsval *vp)
5866 JSXML *parent;
5867 uint32 i, n;
5869 NON_LIST_XML_METHOD_PROLOG;
5870 parent = xml->parent;
5871 if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
5872 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
5873 return JS_TRUE;
5875 for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5876 if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5877 break;
5879 JS_ASSERT(i < n);
5880 return js_NewNumberInRootedValue(cx, i, vp);
5883 /* XML and XMLList */
5884 static JSBool
5885 xml_children(JSContext *cx, uintN argc, jsval *vp)
5887 jsval name;
5889 name = ATOM_KEY(cx->runtime->atomState.starAtom);
5890 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
5893 /* XML and XMLList */
5894 static JSBool
5895 xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
5897 JSXML *list, *kid, *vxml;
5898 JSBool ok;
5899 uint32 i, n;
5900 JSObject *kidobj;
5901 jsval v;
5903 list = xml_list_helper(cx, xml, vp);
5904 if (!list)
5905 return JS_FALSE;
5907 ok = JS_TRUE;
5909 if (xml->xml_class == JSXML_CLASS_LIST) {
5910 /* 13.5.4.6 Step 2. */
5911 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5912 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5913 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5914 ok = js_EnterLocalRootScope(cx);
5915 if (!ok)
5916 break;
5917 kidobj = js_GetXMLObject(cx, kid);
5918 if (kidobj) {
5919 ok = xml_comments_helper(cx, kidobj, kid, &v);
5920 } else {
5921 ok = JS_FALSE;
5922 v = JSVAL_NULL;
5924 js_LeaveLocalRootScopeWithResult(cx, v);
5925 if (!ok)
5926 break;
5927 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5928 if (JSXML_LENGTH(vxml) != 0) {
5929 ok = Append(cx, list, vxml);
5930 if (!ok)
5931 break;
5935 } else {
5936 /* 13.4.4.9 Step 2. */
5937 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5938 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5939 if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
5940 ok = Append(cx, list, kid);
5941 if (!ok)
5942 break;
5947 return ok;
5950 static JSBool
5951 xml_comments(JSContext *cx, uintN argc, jsval *vp)
5953 XML_METHOD_PROLOG;
5954 return xml_comments_helper(cx, obj, xml, vp);
5957 /* XML and XMLList */
5958 static JSBool
5959 xml_contains(JSContext *cx, uintN argc, jsval *vp)
5961 jsval value;
5962 JSBool eq;
5963 JSXMLArrayCursor cursor;
5964 JSXML *kid;
5965 JSObject *kidobj;
5967 XML_METHOD_PROLOG;
5968 value = argc != 0 ? vp[2] : JSVAL_VOID;
5969 if (xml->xml_class == JSXML_CLASS_LIST) {
5970 eq = JS_FALSE;
5971 XMLArrayCursorInit(&cursor, &xml->xml_kids);
5972 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
5973 kidobj = js_GetXMLObject(cx, kid);
5974 if (!kidobj || !xml_equality(cx, kidobj, value, &eq))
5975 break;
5976 if (eq)
5977 break;
5979 XMLArrayCursorFinish(&cursor);
5980 if (kid && !eq)
5981 return JS_FALSE;
5982 } else {
5983 if (!xml_equality(cx, obj, value, &eq))
5984 return JS_FALSE;
5986 *vp = BOOLEAN_TO_JSVAL(eq);
5987 return JS_TRUE;
5990 /* XML and XMLList */
5991 static JSBool
5992 xml_copy(JSContext *cx, uintN argc, jsval *vp)
5994 JSXML *copy;
5996 XML_METHOD_PROLOG;
5997 copy = DeepCopy(cx, xml, NULL, 0);
5998 if (!copy)
5999 return JS_FALSE;
6000 *vp = OBJECT_TO_JSVAL(copy->object);
6001 return JS_TRUE;
6004 /* XML and XMLList */
6005 static JSBool
6006 xml_descendants(JSContext *cx, uintN argc, jsval *vp)
6008 jsval name;
6009 JSXML *list;
6011 XML_METHOD_PROLOG;
6012 name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2];
6013 list = Descendants(cx, xml, name);
6014 if (!list)
6015 return JS_FALSE;
6016 *vp = OBJECT_TO_JSVAL(list->object);
6017 return JS_TRUE;
6020 /* XML and XMLList */
6021 static JSBool
6022 xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6023 JSXMLQName *nameqn, jsval *vp)
6025 JSXML *list, *kid, *vxml;
6026 jsval v;
6027 JSBool ok;
6028 JSXMLArrayCursor cursor;
6029 JSObject *kidobj;
6030 uint32 i, n;
6032 list = xml_list_helper(cx, xml, vp);
6033 if (!list)
6034 return JS_FALSE;
6036 list->xml_targetprop = nameqn;
6037 ok = JS_TRUE;
6039 if (xml->xml_class == JSXML_CLASS_LIST) {
6040 /* 13.5.4.6 */
6041 XMLArrayCursorInit(&cursor, &xml->xml_kids);
6042 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
6043 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6044 ok = js_EnterLocalRootScope(cx);
6045 if (!ok)
6046 break;
6047 kidobj = js_GetXMLObject(cx, kid);
6048 if (kidobj) {
6049 ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v);
6050 } else {
6051 ok = JS_FALSE;
6052 v = JSVAL_NULL;
6054 js_LeaveLocalRootScopeWithResult(cx, v);
6055 if (!ok)
6056 break;
6057 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
6058 if (JSXML_LENGTH(vxml) != 0) {
6059 ok = Append(cx, list, vxml);
6060 if (!ok)
6061 break;
6065 XMLArrayCursorFinish(&cursor);
6066 } else {
6067 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6068 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6069 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
6070 MatchElemName(nameqn, kid)) {
6071 ok = Append(cx, list, kid);
6072 if (!ok)
6073 break;
6078 return ok;
6081 static JSBool
6082 xml_elements(JSContext *cx, uintN argc, jsval *vp)
6084 jsval name;
6085 JSXMLQName *nameqn;
6086 jsid funid;
6088 XML_METHOD_PROLOG;
6090 name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2];
6091 nameqn = ToXMLName(cx, name, &funid);
6092 if (!nameqn)
6093 return JS_FALSE;
6094 vp[2] = OBJECT_TO_JSVAL(nameqn->object);
6096 if (funid)
6097 return xml_list_helper(cx, xml, vp) != NULL;
6099 return xml_elements_helper(cx, obj, xml, nameqn, vp);
6102 /* XML and XMLList */
6103 static JSBool
6104 xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
6106 JSObject *obj;
6107 jsval name;
6108 JSBool found;
6110 obj = JS_THIS_OBJECT(cx, vp);
6111 if (!JS_InstanceOf(cx, obj, &js_XMLClass, vp + 2))
6112 return JS_FALSE;
6114 name = argc != 0 ? vp[2] : JSVAL_VOID;
6115 if (!HasProperty(cx, obj, name, &found))
6116 return JS_FALSE;
6117 if (found) {
6118 *vp = JSVAL_TRUE;
6119 return JS_TRUE;
6121 return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, vp);
6124 /* XML and XMLList */
6125 static JSBool
6126 xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp)
6128 JSXML *kid;
6129 JSObject *kidobj;
6130 uint32 i, n;
6132 XML_METHOD_PROLOG;
6133 again:
6134 switch (xml->xml_class) {
6135 case JSXML_CLASS_ATTRIBUTE:
6136 case JSXML_CLASS_COMMENT:
6137 case JSXML_CLASS_PROCESSING_INSTRUCTION:
6138 case JSXML_CLASS_TEXT:
6139 *vp = JSVAL_FALSE;
6140 break;
6141 case JSXML_CLASS_LIST:
6142 if (xml->xml_kids.length == 0) {
6143 *vp = JSVAL_TRUE;
6144 } else if (xml->xml_kids.length == 1) {
6145 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6146 if (kid) {
6147 kidobj = js_GetXMLObject(cx, kid);
6148 if (!kidobj)
6149 return JS_FALSE;
6150 obj = kidobj;
6151 xml = (JSXML *) JS_GetPrivate(cx, obj);
6152 goto again;
6155 /* FALL THROUGH */
6156 default:
6157 *vp = JSVAL_FALSE;
6158 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6159 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6160 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6161 *vp = JSVAL_TRUE;
6162 break;
6165 break;
6167 return JS_TRUE;
6170 /* XML and XMLList */
6171 static JSBool
6172 xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp)
6174 XML_METHOD_PROLOG;
6175 *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
6176 return JS_TRUE;
6179 typedef struct JSTempRootedNSArray {
6180 JSTempValueRooter tvr;
6181 JSXMLArray array;
6182 jsval value; /* extra root for temporaries */
6183 } JSTempRootedNSArray;
6185 JS_STATIC_DLL_CALLBACK(void)
6186 trace_temp_ns_array(JSTracer *trc, JSTempValueRooter *tvr)
6188 JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr;
6190 namespace_trace_vector(trc,
6191 (JSXMLNamespace **)tmp->array.vector,
6192 tmp->array.length);
6193 XMLArrayCursorTrace(trc, tmp->array.cursors);
6194 JS_CALL_VALUE_TRACER(trc, tmp->value, "temp_ns_array_value");
6197 static void
6198 InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
6200 XMLArrayInit(cx, &tmp->array, 0);
6201 tmp->value = JSVAL_NULL;
6202 JS_PUSH_TEMP_ROOT_TRACE(cx, trace_temp_ns_array, &tmp->tvr);
6205 static void
6206 FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
6208 JS_ASSERT(tmp->tvr.u.trace == trace_temp_ns_array);
6209 JS_POP_TEMP_ROOT(cx, &tmp->tvr);
6210 XMLArrayFinish(cx, &tmp->array);
6214 * Populate a new JS array with elements of JSTempRootedNSArray.array and
6215 * place the result into rval. rval must point to a rooted location.
6217 static JSBool
6218 TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval)
6220 JSObject *arrayobj;
6221 uint32 i, n;
6222 JSXMLNamespace *ns;
6223 JSObject *nsobj;
6225 arrayobj = js_NewArrayObject(cx, 0, NULL);
6226 if (!arrayobj)
6227 return JS_FALSE;
6228 *rval = OBJECT_TO_JSVAL(arrayobj);
6229 for (i = 0, n = tmp->array.length; i < n; i++) {
6230 ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace);
6231 if (!ns)
6232 continue;
6233 nsobj = js_GetXMLNamespaceObject(cx, ns);
6234 if (!nsobj)
6235 return JS_FALSE;
6236 tmp->value = OBJECT_TO_JSVAL(nsobj);
6237 if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value))
6238 return JS_FALSE;
6240 return JS_TRUE;
6243 static JSBool
6244 FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
6246 uint32 length, i, j, n;
6247 JSXMLNamespace *ns, *ns2;
6249 length = nsarray->length;
6250 do {
6251 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6252 continue;
6253 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6254 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
6255 if (!ns)
6256 continue;
6258 for (j = 0; j < length; j++) {
6259 ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace);
6260 if (ns2 &&
6261 ((ns2->prefix && ns->prefix)
6262 ? js_EqualStrings(ns2->prefix, ns->prefix)
6263 : js_EqualStrings(ns2->uri, ns->uri))) {
6264 break;
6268 if (j == length) {
6269 if (!XMLARRAY_APPEND(cx, nsarray, ns))
6270 return JS_FALSE;
6271 ++length;
6274 } while ((xml = xml->parent) != NULL);
6275 JS_ASSERT(length == nsarray->length);
6277 return JS_TRUE;
6280 static JSBool
6281 xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp)
6283 JSTempRootedNSArray namespaces;
6284 JSBool ok;
6286 NON_LIST_XML_METHOD_PROLOG;
6288 InitTempNSArray(cx, &namespaces);
6289 ok = FindInScopeNamespaces(cx, xml, &namespaces.array) &&
6290 TempNSArrayToJSArray(cx, &namespaces, vp);
6291 FinishTempNSArray(cx, &namespaces);
6292 return ok;
6295 static JSBool
6296 xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp)
6298 jsval arg;
6299 JSXML *kid;
6300 uint32 i;
6302 NON_LIST_XML_METHOD_PROLOG;
6303 *vp = OBJECT_TO_JSVAL(obj);
6304 if (!JSXML_HAS_KIDS(xml) || argc == 0)
6305 return JS_TRUE;
6307 arg = vp[2];
6308 if (JSVAL_IS_NULL(arg)) {
6309 kid = NULL;
6310 i = 0;
6311 } else {
6312 if (!VALUE_IS_XML(cx, arg))
6313 return JS_TRUE;
6314 kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
6315 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
6316 if (i == XML_NOT_FOUND)
6317 return JS_TRUE;
6318 ++i;
6321 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6322 if (!xml)
6323 return JS_FALSE;
6324 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
6327 static JSBool
6328 xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp)
6330 jsval arg;
6331 JSXML *kid;
6332 uint32 i;
6334 NON_LIST_XML_METHOD_PROLOG;
6335 *vp = OBJECT_TO_JSVAL(obj);
6336 if (!JSXML_HAS_KIDS(xml) || argc == 0)
6337 return JS_TRUE;
6339 arg = vp[2];
6340 if (JSVAL_IS_NULL(arg)) {
6341 kid = NULL;
6342 i = xml->xml_kids.length;
6343 } else {
6344 if (!VALUE_IS_XML(cx, arg))
6345 return JS_TRUE;
6346 kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
6347 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
6348 if (i == XML_NOT_FOUND)
6349 return JS_TRUE;
6352 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6353 if (!xml)
6354 return JS_FALSE;
6355 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
6358 /* XML and XMLList */
6359 static JSBool
6360 xml_length(JSContext *cx, uintN argc, jsval *vp)
6362 XML_METHOD_PROLOG;
6363 if (xml->xml_class != JSXML_CLASS_LIST) {
6364 *vp = JSVAL_ONE;
6365 } else {
6366 if (!js_NewNumberInRootedValue(cx, xml->xml_kids.length, vp))
6367 return JS_FALSE;
6369 return JS_TRUE;
6372 static JSBool
6373 xml_localName(JSContext *cx, uintN argc, jsval *vp)
6375 NON_LIST_XML_METHOD_PROLOG;
6376 *vp = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL;
6377 return JS_TRUE;
6380 static JSBool
6381 xml_name(JSContext *cx, uintN argc, jsval *vp)
6383 JSObject *nameobj;
6385 NON_LIST_XML_METHOD_PROLOG;
6386 if (!xml->name) {
6387 *vp = JSVAL_NULL;
6388 } else {
6389 nameobj = js_GetXMLQNameObject(cx, xml->name);
6390 if (!nameobj)
6391 return JS_FALSE;
6392 *vp = OBJECT_TO_JSVAL(nameobj);
6394 return JS_TRUE;
6397 static JSBool
6398 xml_namespace(JSContext *cx, uintN argc, jsval *vp)
6400 JSString *prefix;
6401 JSTempRootedNSArray inScopeNSes;
6402 JSBool ok;
6403 jsuint i, length;
6404 JSXMLNamespace *ns;
6405 JSObject *nsobj;
6407 NON_LIST_XML_METHOD_PROLOG;
6408 if (argc == 0 && !JSXML_HAS_NAME(xml)) {
6409 *vp = JSVAL_NULL;
6410 return JS_TRUE;
6413 if (argc == 0) {
6414 prefix = NULL;
6415 } else {
6416 prefix = js_ValueToString(cx, vp[2]);
6417 if (!prefix)
6418 return JS_FALSE;
6419 vp[2] = STRING_TO_JSVAL(prefix); /* local root */
6422 /* After this point the control must flow through label out. */
6423 InitTempNSArray(cx, &inScopeNSes);
6424 ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array);
6425 if (!ok)
6426 goto out;
6428 if (!prefix) {
6429 ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
6430 if (!ns) {
6431 ok = JS_FALSE;
6432 goto out;
6434 } else {
6435 ns = NULL;
6436 for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
6437 ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace);
6438 if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix))
6439 break;
6440 ns = NULL;
6444 if (!ns) {
6445 *vp = JSVAL_VOID;
6446 } else {
6447 nsobj = js_GetXMLNamespaceObject(cx, ns);
6448 if (!nsobj) {
6449 ok = JS_FALSE;
6450 goto out;
6452 *vp = OBJECT_TO_JSVAL(nsobj);
6455 out:
6456 FinishTempNSArray(cx, &inScopeNSes);
6457 return JS_TRUE;
6460 static JSBool
6461 xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp)
6463 JSBool ok;
6464 JSTempRootedNSArray ancestors, declared;
6465 JSXML *yml;
6466 uint32 i, n;
6467 JSXMLNamespace *ns;
6469 NON_LIST_XML_METHOD_PROLOG;
6470 if (JSXML_HAS_VALUE(xml))
6471 return JS_TRUE;
6473 /* From here, control flow must goto out to finish these arrays. */
6474 ok = JS_TRUE;
6475 InitTempNSArray(cx, &ancestors);
6476 InitTempNSArray(cx, &declared);
6477 yml = xml;
6479 while ((yml = yml->parent) != NULL) {
6480 JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
6481 for (i = 0, n = yml->xml_namespaces.length; i < n; i++) {
6482 ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace);
6483 if (ns &&
6484 !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6485 ok = XMLARRAY_APPEND(cx, &ancestors.array, ns);
6486 if (!ok)
6487 goto out;
6492 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6493 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
6494 if (!ns)
6495 continue;
6496 if (!ns->declared)
6497 continue;
6498 if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6499 ok = XMLARRAY_APPEND(cx, &declared.array, ns);
6500 if (!ok)
6501 goto out;
6505 ok = TempNSArrayToJSArray(cx, &declared, vp);
6507 out:
6508 /* Finishing must be in reverse order of initialization to follow LIFO. */
6509 FinishTempNSArray(cx, &declared);
6510 FinishTempNSArray(cx, &ancestors);
6511 return ok;
6514 static const char js_attribute_str[] = "attribute";
6515 static const char js_text_str[] = "text";
6517 /* Exported to jsgc.c #ifdef DEBUG. */
6518 const char *js_xml_class_str[] = {
6519 "list",
6520 "element",
6521 js_attribute_str,
6522 "processing-instruction",
6523 js_text_str,
6524 "comment"
6527 static JSBool
6528 xml_nodeKind(JSContext *cx, uintN argc, jsval *vp)
6530 JSString *str;
6532 NON_LIST_XML_METHOD_PROLOG;
6533 str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6534 if (!str)
6535 return JS_FALSE;
6536 *vp = STRING_TO_JSVAL(str);
6537 return JS_TRUE;
6540 static void
6541 NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index)
6543 if (xml->xml_class == JSXML_CLASS_LIST)
6544 DeleteListElement(cx, xml, index);
6545 else
6546 DeleteByIndex(cx, xml, index);
6549 /* XML and XMLList */
6550 static JSBool
6551 xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml)
6553 JSXML *kid, *kid2;
6554 uint32 i, n;
6555 JSObject *kidobj;
6556 JSString *str;
6558 if (!JSXML_HAS_KIDS(xml))
6559 return JS_TRUE;
6561 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6562 if (!xml)
6563 return JS_FALSE;
6565 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6566 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6567 if (!kid)
6568 continue;
6569 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6570 kidobj = js_GetXMLObject(cx, kid);
6571 if (!kidobj || !xml_normalize_helper(cx, kidobj, kid))
6572 return JS_FALSE;
6573 } else if (kid->xml_class == JSXML_CLASS_TEXT) {
6574 while (i + 1 < n &&
6575 (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
6576 kid2->xml_class == JSXML_CLASS_TEXT) {
6577 str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
6578 if (!str)
6579 return JS_FALSE;
6580 NormalizingDelete(cx, xml, i + 1);
6581 n = xml->xml_kids.length;
6582 kid->xml_value = str;
6584 if (IS_EMPTY(kid->xml_value)) {
6585 NormalizingDelete(cx, xml, i);
6586 n = xml->xml_kids.length;
6587 --i;
6592 return JS_TRUE;
6595 static JSBool
6596 xml_normalize(JSContext *cx, uintN argc, jsval *vp)
6598 XML_METHOD_PROLOG;
6599 *vp = OBJECT_TO_JSVAL(obj);
6600 return xml_normalize_helper(cx, obj, xml);
6603 /* XML and XMLList */
6604 static JSBool
6605 xml_parent(JSContext *cx, uintN argc, jsval *vp)
6607 JSXML *parent, *kid;
6608 uint32 i, n;
6609 JSObject *parentobj;
6611 XML_METHOD_PROLOG;
6612 parent = xml->parent;
6613 if (xml->xml_class == JSXML_CLASS_LIST) {
6614 *vp = JSVAL_VOID;
6615 n = xml->xml_kids.length;
6616 if (n == 0)
6617 return JS_TRUE;
6619 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6620 if (!kid)
6621 return JS_TRUE;
6622 parent = kid->parent;
6623 for (i = 1; i < n; i++) {
6624 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6625 if (kid && kid->parent != parent)
6626 return JS_TRUE;
6630 if (!parent) {
6631 *vp = JSVAL_NULL;
6632 return JS_TRUE;
6635 parentobj = js_GetXMLObject(cx, parent);
6636 if (!parentobj)
6637 return JS_FALSE;
6638 *vp = OBJECT_TO_JSVAL(parentobj);
6639 return JS_TRUE;
6642 /* XML and XMLList */
6643 static JSBool
6644 xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6645 JSXMLQName *nameqn, jsval *vp)
6647 JSXML *list, *kid, *vxml;
6648 JSBool ok;
6649 JSXMLArrayCursor cursor;
6650 JSObject *kidobj;
6651 jsval v;
6652 uint32 i, n;
6654 list = xml_list_helper(cx, xml, vp);
6655 if (!list)
6656 return JS_FALSE;
6658 list->xml_targetprop = nameqn;
6659 ok = JS_TRUE;
6661 if (xml->xml_class == JSXML_CLASS_LIST) {
6662 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6663 XMLArrayCursorInit(&cursor, &xml->xml_kids);
6664 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
6665 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6666 ok = js_EnterLocalRootScope(cx);
6667 if (!ok)
6668 break;
6669 kidobj = js_GetXMLObject(cx, kid);
6670 if (kidobj) {
6671 ok = xml_processingInstructions_helper(cx, kidobj, kid,
6672 nameqn, &v);
6673 } else {
6674 ok = JS_FALSE;
6675 v = JSVAL_NULL;
6677 js_LeaveLocalRootScopeWithResult(cx, v);
6678 if (!ok)
6679 break;
6680 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
6681 if (JSXML_LENGTH(vxml) != 0) {
6682 ok = Append(cx, list, vxml);
6683 if (!ok)
6684 break;
6688 XMLArrayCursorFinish(&cursor);
6689 } else {
6690 /* 13.4.4.28 Step 4. */
6691 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6692 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6693 if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
6694 (IS_STAR(nameqn->localName) ||
6695 js_EqualStrings(nameqn->localName, kid->name->localName))) {
6696 ok = Append(cx, list, kid);
6697 if (!ok)
6698 break;
6703 return ok;
6706 static JSBool
6707 xml_processingInstructions(JSContext *cx, uintN argc, jsval *vp)
6709 jsval name;
6710 JSXMLQName *nameqn;
6711 jsid funid;
6713 XML_METHOD_PROLOG;
6715 name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2];
6716 nameqn = ToXMLName(cx, name, &funid);
6717 if (!nameqn)
6718 return JS_FALSE;
6719 vp[2] = OBJECT_TO_JSVAL(nameqn->object);
6721 if (funid)
6722 return xml_list_helper(cx, xml, vp) != NULL;
6724 return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp);
6727 static JSBool
6728 xml_prependChild(JSContext *cx, uintN argc, jsval *vp)
6730 NON_LIST_XML_METHOD_PROLOG;
6731 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6732 if (!xml)
6733 return JS_FALSE;
6734 *vp = OBJECT_TO_JSVAL(obj);
6735 return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID);
6738 /* XML and XMLList */
6739 static JSBool
6740 xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
6742 uint32 index;
6744 XML_METHOD_PROLOG;
6745 *vp = JSVAL_FALSE;
6746 if (argc != 0 && js_IdIsIndex(vp[2], &index)) {
6747 if (xml->xml_class == JSXML_CLASS_LIST) {
6748 /* 13.5.4.18. */
6749 *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6750 } else {
6751 /* 13.4.4.30. */
6752 *vp = BOOLEAN_TO_JSVAL(index == 0);
6755 return JS_TRUE;
6758 static JSBool
6759 namespace_full_match(const void *a, const void *b)
6761 const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
6762 const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
6764 if (nsa->prefix && nsb->prefix &&
6765 !js_EqualStrings(nsa->prefix, nsb->prefix)) {
6766 return JS_FALSE;
6768 return js_EqualStrings(nsa->uri, nsb->uri);
6771 static JSBool
6772 xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
6774 JSXMLNamespace *thisns, *attrns;
6775 uint32 i, n;
6776 JSXML *attr, *kid;
6778 thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6779 JS_ASSERT(thisns);
6780 if (thisns == ns)
6781 return JS_TRUE;
6783 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6784 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6785 if (!attr)
6786 continue;
6787 attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
6788 JS_ASSERT(attrns);
6789 if (attrns == ns)
6790 return JS_TRUE;
6793 i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
6794 if (i != XML_NOT_FOUND)
6795 XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
6797 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6798 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6799 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6800 if (!xml_removeNamespace_helper(cx, kid, ns))
6801 return JS_FALSE;
6804 return JS_TRUE;
6807 static JSBool
6808 xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp)
6810 JSXMLNamespace *ns;
6812 NON_LIST_XML_METHOD_PROLOG;
6813 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6814 goto done;
6815 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6816 if (!xml)
6817 return JS_FALSE;
6819 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6820 return JS_FALSE;
6821 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6822 ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
6824 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6825 if (!xml_removeNamespace_helper(cx, xml, ns))
6826 return JS_FALSE;
6827 done:
6828 *vp = OBJECT_TO_JSVAL(obj);
6829 return JS_TRUE;
6832 static JSBool
6833 xml_replace(JSContext *cx, uintN argc, jsval *vp)
6835 jsval value;
6836 JSXML *vxml, *kid;
6837 uint32 index, i;
6838 JSXMLQName *nameqn;
6840 NON_LIST_XML_METHOD_PROLOG;
6841 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6842 goto done;
6844 if (argc <= 1) {
6845 value = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.
6846 typeAtoms[JSTYPE_VOID]));
6847 } else {
6848 value = vp[3];
6849 vxml = VALUE_IS_XML(cx, value)
6850 ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value))
6851 : NULL;
6852 if (!vxml) {
6853 if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
6854 return JS_FALSE;
6855 value = vp[3];
6856 } else {
6857 vxml = DeepCopy(cx, vxml, NULL, 0);
6858 if (!vxml)
6859 return JS_FALSE;
6860 value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
6864 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6865 if (!xml)
6866 return JS_FALSE;
6868 if (argc == 0 || !js_IdIsIndex(vp[2], &index)) {
6870 * Call function QName per spec, not ToXMLName, to avoid attribute
6871 * names.
6873 if (!QNameHelper(cx, NULL, &js_QNameClass.base, argc == 0 ? -1 : 1,
6874 vp + 2, vp)) {
6875 return JS_FALSE;
6877 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6878 nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
6880 i = xml->xml_kids.length;
6881 index = XML_NOT_FOUND;
6882 while (i != 0) {
6883 --i;
6884 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6885 if (kid && MatchElemName(nameqn, kid)) {
6886 if (i != XML_NOT_FOUND)
6887 DeleteByIndex(cx, xml, i);
6888 index = i;
6892 if (index == XML_NOT_FOUND)
6893 goto done;
6896 if (!Replace(cx, xml, index, value))
6897 return JS_FALSE;
6899 done:
6900 *vp = OBJECT_TO_JSVAL(obj);
6901 return JS_TRUE;
6904 static JSBool
6905 xml_setChildren(JSContext *cx, uintN argc, jsval *vp)
6907 JSObject *obj;
6909 if (!StartNonListXMLMethod(cx, vp, &obj))
6910 return JS_FALSE;
6912 *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */
6913 if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), vp))
6914 return JS_FALSE;
6916 *vp = OBJECT_TO_JSVAL(obj);
6917 return JS_TRUE;
6920 static JSBool
6921 xml_setLocalName(JSContext *cx, uintN argc, jsval *vp)
6923 jsval name;
6924 JSXMLQName *nameqn;
6925 JSString *namestr;
6927 NON_LIST_XML_METHOD_PROLOG;
6928 if (!JSXML_HAS_NAME(xml))
6929 return JS_TRUE;
6931 if (argc == 0) {
6932 namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6933 } else {
6934 name = vp[2];
6935 if (!JSVAL_IS_PRIMITIVE(name) &&
6936 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) {
6937 nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name));
6938 namestr = nameqn->localName;
6939 } else {
6940 if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
6941 return JS_FALSE;
6942 name = vp[2];
6943 namestr = JSVAL_TO_STRING(name);
6947 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6948 if (!xml)
6949 return JS_FALSE;
6950 xml->name->localName = namestr;
6951 return JS_TRUE;
6954 static JSBool
6955 xml_setName(JSContext *cx, uintN argc, jsval *vp)
6957 jsval name;
6958 JSXMLQName *nameqn;
6959 JSObject *nameobj;
6960 JSXML *nsowner;
6961 JSXMLArray *nsarray;
6962 uint32 i, n;
6963 JSXMLNamespace *ns;
6965 NON_LIST_XML_METHOD_PROLOG;
6966 if (!JSXML_HAS_NAME(xml))
6967 return JS_TRUE;
6969 if (argc == 0) {
6970 name = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.
6971 typeAtoms[JSTYPE_VOID]));
6972 } else {
6973 name = vp[2];
6974 if (!JSVAL_IS_PRIMITIVE(name) &&
6975 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base &&
6976 !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)))
6977 ->uri) {
6978 name = vp[2] = STRING_TO_JSVAL(nameqn->localName);
6982 nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name);
6983 if (!nameobj)
6984 return JS_FALSE;
6985 nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
6987 /* ECMA-357 13.4.4.35 Step 4. */
6988 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6989 nameqn->uri = cx->runtime->emptyString;
6991 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6992 if (!xml)
6993 return JS_FALSE;
6994 xml->name = nameqn;
6997 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6998 * in-scope namespaces, either by finding an in-scope namespace with a
6999 * matching uri and setting the new name's prefix to that namespace's
7000 * prefix, or by extending the in-scope namespaces for xml (which are in
7001 * xml->parent if xml is an attribute or a PI).
7003 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
7004 nsowner = xml;
7005 } else {
7006 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
7007 return JS_TRUE;
7008 nsowner = xml->parent;
7011 if (nameqn->prefix) {
7013 * The name being set has a prefix, which originally came from some
7014 * namespace object (which may be the null namespace, where both the
7015 * prefix and uri are the empty string). We must go through a full
7016 * GetNamespace in case that namespace is in-scope in nsowner.
7018 * If we find such an in-scope namespace, we return true right away,
7019 * in this block. Otherwise, we fall through to the final return of
7020 * AddInScopeNamespace(cx, nsowner, ns).
7022 ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
7023 if (!ns)
7024 return JS_FALSE;
7026 /* XXXbe have to test membership to see whether GetNamespace added */
7027 if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
7028 return JS_TRUE;
7029 } else {
7031 * At this point, we know nameqn->prefix is null, so nameqn->uri can't
7032 * be the empty string (the null namespace always uses the empty string
7033 * for both prefix and uri).
7035 * This means we must inline GetNamespace and specialize it to match
7036 * uri only, never prefix. If we find a namespace with nameqn's uri
7037 * already in nsowner->xml_namespaces, then all that we need do is set
7038 * nameqn->prefix to that namespace's prefix.
7040 * If no such namespace exists, we can create one without going through
7041 * the constructor, because we know nameqn->uri is non-empty (so prefix
7042 * does not need to be converted from null to empty by QName).
7044 JS_ASSERT(!IS_EMPTY(nameqn->uri));
7046 nsarray = &nsowner->xml_namespaces;
7047 for (i = 0, n = nsarray->length; i < n; i++) {
7048 ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace);
7049 if (ns && js_EqualStrings(ns->uri, nameqn->uri)) {
7050 nameqn->prefix = ns->prefix;
7051 return JS_TRUE;
7055 ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE);
7056 if (!ns)
7057 return JS_FALSE;
7060 if (!AddInScopeNamespace(cx, nsowner, ns))
7061 return JS_FALSE;
7062 vp[0] = JSVAL_VOID;
7063 return JS_TRUE;
7066 static JSBool
7067 xml_setNamespace(JSContext *cx, uintN argc, jsval *vp)
7069 JSObject *nsobj, *qnobj;
7070 JSXMLNamespace *ns;
7071 jsval qnargv[2];
7072 JSXML *nsowner;
7074 NON_LIST_XML_METHOD_PROLOG;
7075 if (!JSXML_HAS_NAME(xml))
7076 return JS_TRUE;
7078 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
7079 if (!xml || !js_GetXMLQNameObject(cx, xml->name))
7080 return JS_FALSE;
7082 nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj,
7083 argc == 0 ? 0 : 1, vp + 2);
7084 if (!nsobj)
7085 return JS_FALSE;
7086 vp[0] = OBJECT_TO_JSVAL(nsobj);
7087 ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
7088 ns->declared = JS_TRUE;
7090 qnargv[0] = vp[2] = OBJECT_TO_JSVAL(nsobj);
7091 qnargv[1] = OBJECT_TO_JSVAL(xml->name->object);
7092 qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv);
7093 if (!qnobj)
7094 return JS_FALSE;
7096 xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj);
7099 * Erratum: the spec fails to update the governing in-scope namespaces.
7100 * See the erratum noted in xml_setName, above.
7102 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
7103 nsowner = xml;
7104 } else {
7105 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
7106 return JS_TRUE;
7107 nsowner = xml->parent;
7109 if (!AddInScopeNamespace(cx, nsowner, ns))
7110 return JS_FALSE;
7111 vp[0] = JSVAL_VOID;
7112 return JS_TRUE;
7115 /* XML and XMLList */
7116 static JSBool
7117 xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
7119 JSXML *list, *kid, *vxml;
7120 uint32 i, n;
7121 JSBool ok;
7122 JSObject *kidobj;
7123 jsval v;
7125 list = xml_list_helper(cx, xml, vp);
7126 if (!list)
7127 return JS_FALSE;
7129 if (xml->xml_class == JSXML_CLASS_LIST) {
7130 ok = JS_TRUE;
7131 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
7132 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
7133 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
7134 ok = js_EnterLocalRootScope(cx);
7135 if (!ok)
7136 break;
7137 kidobj = js_GetXMLObject(cx, kid);
7138 if (kidobj) {
7139 ok = xml_text_helper(cx, kidobj, kid, &v);
7140 } else {
7141 ok = JS_FALSE;
7142 v = JSVAL_NULL;
7144 js_LeaveLocalRootScopeWithResult(cx, v);
7145 if (!ok)
7146 return JS_FALSE;
7147 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
7148 if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
7149 return JS_FALSE;
7152 } else {
7153 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
7154 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
7155 if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
7156 if (!Append(cx, list, kid))
7157 return JS_FALSE;
7161 return JS_TRUE;
7164 static JSBool
7165 xml_text(JSContext *cx, uintN argc, jsval *vp)
7167 XML_METHOD_PROLOG;
7168 return xml_text_helper(cx, obj, xml, vp);
7171 /* XML and XMLList */
7172 static JSString *
7173 xml_toString_helper(JSContext *cx, JSXML *xml)
7175 JSString *str, *kidstr;
7176 JSXML *kid;
7177 JSXMLArrayCursor cursor;
7179 if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
7180 xml->xml_class == JSXML_CLASS_TEXT) {
7181 return xml->xml_value;
7184 if (!HasSimpleContent(xml))
7185 return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0);
7187 str = cx->runtime->emptyString;
7188 if (!js_EnterLocalRootScope(cx))
7189 return NULL;
7190 XMLArrayCursorInit(&cursor, &xml->xml_kids);
7191 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
7192 if (kid->xml_class != JSXML_CLASS_COMMENT &&
7193 kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
7194 kidstr = xml_toString_helper(cx, kid);
7195 if (!kidstr) {
7196 str = NULL;
7197 break;
7199 str = js_ConcatStrings(cx, str, kidstr);
7200 if (!str)
7201 break;
7204 XMLArrayCursorFinish(&cursor);
7205 js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
7206 return str;
7209 static JSBool
7210 xml_toSource(JSContext *cx, uintN argc, jsval *vp)
7212 jsval thisv;
7213 JSString *str;
7215 thisv = JS_THIS(cx, vp);
7216 if (JSVAL_IS_NULL(thisv))
7217 return JS_FALSE;
7218 str = ToXMLString(cx, thisv, TO_SOURCE_FLAG);
7219 if (!str)
7220 return JS_FALSE;
7221 *vp = STRING_TO_JSVAL(str);
7222 return JS_TRUE;
7225 static JSBool
7226 xml_toString(JSContext *cx, uintN argc, jsval *vp)
7228 JSString *str;
7230 XML_METHOD_PROLOG;
7231 str = xml_toString_helper(cx, xml);
7232 if (!str)
7233 return JS_FALSE;
7234 *vp = STRING_TO_JSVAL(str);
7235 return JS_TRUE;
7238 /* XML and XMLList */
7239 static JSBool
7240 xml_toXMLString(JSContext *cx, uintN argc, jsval *vp)
7242 jsval thisv;
7243 JSString *str;
7245 thisv = JS_THIS(cx, vp);
7246 if (JSVAL_IS_NULL(thisv))
7247 return JS_FALSE;
7248 str = ToXMLString(cx, thisv, 0);
7249 if (!str)
7250 return JS_FALSE;
7251 *vp = STRING_TO_JSVAL(str);
7252 return JS_TRUE;
7255 /* XML and XMLList */
7256 static JSBool
7257 xml_valueOf(JSContext *cx, uintN argc, jsval *vp)
7259 *vp = JS_THIS(cx, vp);
7260 return !JSVAL_IS_NULL(*vp);
7263 static JSFunctionSpec xml_methods[] = {
7264 JS_FN("addNamespace", xml_addNamespace, 1,0),
7265 JS_FN("appendChild", xml_appendChild, 1,0),
7266 JS_FN(js_attribute_str, xml_attribute, 1,0),
7267 JS_FN("attributes", xml_attributes, 0,0),
7268 JS_FN("child", xml_child, 1,0),
7269 JS_FN("childIndex", xml_childIndex, 0,0),
7270 JS_FN("children", xml_children, 0,0),
7271 JS_FN("comments", xml_comments, 0,0),
7272 JS_FN("contains", xml_contains, 1,0),
7273 JS_FN("copy", xml_copy, 0,0),
7274 JS_FN("descendants", xml_descendants, 1,0),
7275 JS_FN("elements", xml_elements, 1,0),
7276 JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0),
7277 JS_FN("hasComplexContent", xml_hasComplexContent, 1,0),
7278 JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0),
7279 JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0),
7280 JS_FN("insertChildAfter", xml_insertChildAfter, 2,0),
7281 JS_FN("insertChildBefore", xml_insertChildBefore, 2,0),
7282 JS_FN(js_length_str, xml_length, 0,0),
7283 JS_FN(js_localName_str, xml_localName, 0,0),
7284 JS_FN(js_name_str, xml_name, 0,0),
7285 JS_FN(js_namespace_str, xml_namespace, 1,0),
7286 JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0),
7287 JS_FN("nodeKind", xml_nodeKind, 0,0),
7288 JS_FN("normalize", xml_normalize, 0,0),
7289 JS_FN(js_xml_parent_str, xml_parent, 0,0),
7290 JS_FN("processingInstructions",xml_processingInstructions,1,0),
7291 JS_FN("prependChild", xml_prependChild, 1,0),
7292 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0),
7293 JS_FN("removeNamespace", xml_removeNamespace, 1,0),
7294 JS_FN("replace", xml_replace, 2,0),
7295 JS_FN("setChildren", xml_setChildren, 1,0),
7296 JS_FN("setLocalName", xml_setLocalName, 1,0),
7297 JS_FN("setName", xml_setName, 1,0),
7298 JS_FN("setNamespace", xml_setNamespace, 1,0),
7299 JS_FN(js_text_str, xml_text, 0,0),
7300 JS_FN(js_toSource_str, xml_toSource, 0,0),
7301 JS_FN(js_toString_str, xml_toString, 0,0),
7302 JS_FN(js_toXMLString_str, xml_toXMLString, 0,0),
7303 JS_FN(js_valueOf_str, xml_valueOf, 0,0),
7304 JS_FS_END
7307 static JSBool
7308 CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
7310 int i;
7311 const char *name;
7312 jsval v;
7314 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
7315 name = xml_static_props[i].name;
7316 if (!JS_GetProperty(cx, from, name, &v))
7317 return JS_FALSE;
7318 if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v))
7319 return JS_FALSE;
7322 name = xml_static_props[i].name;
7323 if (!JS_GetProperty(cx, from, name, &v))
7324 return JS_FALSE;
7325 if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v))
7326 return JS_FALSE;
7327 return JS_TRUE;
7330 static JSBool
7331 SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
7333 int i;
7334 jsval v;
7336 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
7337 v = JSVAL_TRUE;
7338 if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
7339 return JS_FALSE;
7341 v = INT_TO_JSVAL(2);
7342 return JS_SetProperty(cx, obj, xml_static_props[i].name, &v);
7345 static JSBool
7346 xml_settings(JSContext *cx, uintN argc, jsval *vp)
7348 JSObject *settings;
7349 JSObject *obj;
7351 settings = JS_NewObject(cx, NULL, NULL, NULL);
7352 if (!settings)
7353 return JS_FALSE;
7354 *vp = OBJECT_TO_JSVAL(settings);
7355 obj = JS_THIS_OBJECT(cx, vp);
7356 return obj && CopyXMLSettings(cx, obj, settings);
7359 static JSBool
7360 xml_setSettings(JSContext *cx, uintN argc, jsval *vp)
7362 JSObject *obj, *settings;
7363 jsval v;
7364 JSBool ok;
7366 obj = JS_THIS_OBJECT(cx, vp);
7367 if (!obj)
7368 return JS_FALSE;
7369 v = (argc == 0) ? JSVAL_VOID : vp[2];
7370 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
7371 cx->xmlSettingFlags = 0;
7372 ok = SetDefaultXMLSettings(cx, obj);
7373 } else {
7374 if (JSVAL_IS_PRIMITIVE(v))
7375 return JS_TRUE;
7376 settings = JSVAL_TO_OBJECT(v);
7377 cx->xmlSettingFlags = 0;
7378 ok = CopyXMLSettings(cx, settings, obj);
7380 if (ok)
7381 cx->xmlSettingFlags |= XSF_CACHE_VALID;
7382 return ok;
7385 static JSBool
7386 xml_defaultSettings(JSContext *cx, uintN argc, jsval *vp)
7388 JSObject *settings;
7390 settings = JS_NewObject(cx, NULL, NULL, NULL);
7391 if (!settings)
7392 return JS_FALSE;
7393 *vp = OBJECT_TO_JSVAL(settings);
7394 return SetDefaultXMLSettings(cx, settings);
7397 static JSFunctionSpec xml_static_methods[] = {
7398 JS_FN("settings", xml_settings, 0,0),
7399 JS_FN("setSettings", xml_setSettings, 1,0),
7400 JS_FN("defaultSettings", xml_defaultSettings, 0,0),
7401 JS_FS_END
7404 static JSBool
7405 XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7407 jsval v;
7408 JSXML *xml, *copy;
7409 JSObject *xobj, *vobj;
7410 JSClass *clasp;
7412 v = argv[0];
7413 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7414 v = STRING_TO_JSVAL(cx->runtime->emptyString);
7416 xobj = ToXML(cx, v);
7417 if (!xobj)
7418 return JS_FALSE;
7419 *rval = OBJECT_TO_JSVAL(xobj);
7420 xml = (JSXML *) JS_GetPrivate(cx, xobj);
7422 if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) {
7423 vobj = JSVAL_TO_OBJECT(v);
7424 clasp = OBJ_GET_CLASS(cx, vobj);
7425 if (clasp == &js_XMLClass ||
7426 (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
7427 /* No need to lock obj, it's newly constructed and thread local. */
7428 copy = DeepCopy(cx, xml, obj, 0);
7429 if (!copy)
7430 return JS_FALSE;
7431 JS_ASSERT(copy->object == obj);
7432 *rval = OBJECT_TO_JSVAL(obj);
7433 return JS_TRUE;
7436 return JS_TRUE;
7439 static JSBool
7440 XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7442 jsval v;
7443 JSObject *vobj, *listobj;
7444 JSXML *xml, *list;
7446 v = argv[0];
7447 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7448 v = STRING_TO_JSVAL(cx->runtime->emptyString);
7450 if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) {
7451 vobj = JSVAL_TO_OBJECT(v);
7452 if (OBJECT_IS_XML(cx, vobj)) {
7453 xml = (JSXML *) JS_GetPrivate(cx, vobj);
7454 if (xml->xml_class == JSXML_CLASS_LIST) {
7455 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7456 if (!listobj)
7457 return JS_FALSE;
7458 *rval = OBJECT_TO_JSVAL(listobj);
7460 list = (JSXML *) JS_GetPrivate(cx, listobj);
7461 if (!Append(cx, list, xml))
7462 return JS_FALSE;
7463 return JS_TRUE;
7468 /* Toggle on XML support since the script has explicitly requested it. */
7469 listobj = ToXMLList(cx, v);
7470 if (!listobj)
7471 return JS_FALSE;
7473 *rval = OBJECT_TO_JSVAL(listobj);
7474 return JS_TRUE;
7477 #define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar))
7478 #define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLElemVar))
7479 #define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *))
7481 static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = {
7482 JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */
7483 JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */
7484 JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */
7485 JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */
7486 JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */
7487 JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */
7490 #ifdef DEBUG_notme
7491 JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
7492 uint32 xml_serial;
7493 #endif
7495 JSXML *
7496 js_NewXML(JSContext *cx, JSXMLClass xml_class)
7498 JSXML *xml;
7500 xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]);
7501 if (!xml)
7502 return NULL;
7504 xml->object = NULL;
7505 xml->domnode = NULL;
7506 xml->parent = NULL;
7507 xml->name = NULL;
7508 xml->xml_class = xml_class;
7509 xml->xml_flags = 0;
7510 if (JSXML_CLASS_HAS_VALUE(xml_class)) {
7511 xml->xml_value = cx->runtime->emptyString;
7512 } else {
7513 XMLArrayInit(cx, &xml->xml_kids, 0);
7514 if (xml_class == JSXML_CLASS_LIST) {
7515 xml->xml_target = NULL;
7516 xml->xml_targetprop = NULL;
7517 } else {
7518 XMLArrayInit(cx, &xml->xml_namespaces, 0);
7519 XMLArrayInit(cx, &xml->xml_attrs, 0);
7523 #ifdef DEBUG_notme
7524 JS_APPEND_LINK(&xml->links, &xml_leaks);
7525 xml->serial = xml_serial++;
7526 #endif
7527 METER(xml_stats.xml);
7528 METER(xml_stats.livexml);
7529 return xml;
7532 void
7533 js_TraceXML(JSTracer *trc, JSXML *xml)
7535 if (xml->object)
7536 JS_CALL_OBJECT_TRACER(trc, xml->object, "object");
7537 if (xml->name)
7538 JS_CALL_TRACER(trc, xml->name, JSTRACE_QNAME, "name");
7539 if (xml->parent)
7540 JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent");
7542 if (JSXML_HAS_VALUE(xml)) {
7543 if (xml->xml_value)
7544 JS_CALL_STRING_TRACER(trc, xml->xml_value, "value");
7545 return;
7548 xml_trace_vector(trc,
7549 (JSXML **) xml->xml_kids.vector,
7550 xml->xml_kids.length);
7551 XMLArrayCursorTrace(trc, xml->xml_kids.cursors);
7552 if (IS_GC_MARKING_TRACER(trc))
7553 XMLArrayTrim(&xml->xml_kids);
7555 if (xml->xml_class == JSXML_CLASS_LIST) {
7556 if (xml->xml_target)
7557 JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target");
7558 if (xml->xml_targetprop) {
7559 JS_CALL_TRACER(trc, xml->xml_targetprop, JSTRACE_QNAME,
7560 "targetprop");
7562 } else {
7563 namespace_trace_vector(trc,
7564 (JSXMLNamespace **)xml->xml_namespaces.vector,
7565 xml->xml_namespaces.length);
7566 XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
7567 if (IS_GC_MARKING_TRACER(trc))
7568 XMLArrayTrim(&xml->xml_namespaces);
7570 xml_trace_vector(trc,
7571 (JSXML **) xml->xml_attrs.vector,
7572 xml->xml_attrs.length);
7573 XMLArrayCursorTrace(trc, xml->xml_attrs.cursors);
7574 if (IS_GC_MARKING_TRACER(trc))
7575 XMLArrayTrim(&xml->xml_attrs);
7579 void
7580 js_FinalizeXML(JSContext *cx, JSXML *xml)
7582 if (JSXML_HAS_KIDS(xml)) {
7583 XMLArrayFinish(cx, &xml->xml_kids);
7584 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
7585 XMLArrayFinish(cx, &xml->xml_namespaces);
7586 XMLArrayFinish(cx, &xml->xml_attrs);
7590 #ifdef DEBUG_notme
7591 JS_REMOVE_LINK(&xml->links);
7592 #endif
7594 UNMETER(xml_stats.livexml);
7597 JSObject *
7598 js_ParseNodeToXMLObject(JSContext *cx, JSParseContext *pc, JSParseNode *pn)
7600 jsval nsval;
7601 JSXMLNamespace *ns;
7602 JSXMLArray nsarray;
7603 JSXML *xml;
7605 if (!js_GetDefaultXMLNamespace(cx, &nsval))
7606 return NULL;
7607 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
7608 ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
7610 if (!XMLArrayInit(cx, &nsarray, 1))
7611 return NULL;
7613 XMLARRAY_APPEND(cx, &nsarray, ns);
7614 xml = ParseNodeToXML(cx, pc, pn, &nsarray, XSF_PRECOMPILED_ROOT);
7615 XMLArrayFinish(cx, &nsarray);
7616 if (!xml)
7617 return NULL;
7619 return xml->object;
7622 JSObject *
7623 js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7625 JSXML *xml;
7626 JSObject *obj;
7627 JSTempValueRooter tvr;
7629 xml = js_NewXML(cx, xml_class);
7630 if (!xml)
7631 return NULL;
7632 JS_PUSH_TEMP_ROOT_XML(cx, xml, &tvr);
7633 obj = js_GetXMLObject(cx, xml);
7634 JS_POP_TEMP_ROOT(cx, &tvr);
7635 return obj;
7638 static JSObject *
7639 NewXMLObject(JSContext *cx, JSXML *xml)
7641 JSObject *obj;
7643 obj = js_NewObject(cx, &js_XMLClass, NULL, NULL, 0);
7644 if (!obj || !JS_SetPrivate(cx, obj, xml)) {
7645 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
7646 return NULL;
7648 METER(xml_stats.xmlobj);
7649 METER(xml_stats.livexmlobj);
7650 return obj;
7653 JSObject *
7654 js_GetXMLObject(JSContext *cx, JSXML *xml)
7656 JSObject *obj;
7658 obj = xml->object;
7659 if (obj) {
7660 JS_ASSERT(JS_GetPrivate(cx, obj) == xml);
7661 return obj;
7665 * A JSXML cannot be shared among threads unless it has an object.
7666 * A JSXML cannot be given an object unless:
7667 * (a) it has no parent; or
7668 * (b) its parent has no object (therefore is thread-private); or
7669 * (c) its parent's object is locked.
7671 * Once given an object, a JSXML is immutable.
7673 JS_ASSERT(!xml->parent ||
7674 !xml->parent->object ||
7675 JS_IS_OBJ_LOCKED(cx, xml->parent->object));
7677 obj = NewXMLObject(cx, xml);
7678 if (!obj)
7679 return NULL;
7680 xml->object = obj;
7681 return obj;
7684 JSObject *
7685 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7687 return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2,
7688 namespace_props, namespace_methods, NULL, NULL);
7691 JSObject *
7692 js_InitQNameClass(JSContext *cx, JSObject *obj)
7694 return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2,
7695 qname_props, qname_methods, NULL, NULL);
7698 JSObject *
7699 js_InitAttributeNameClass(JSContext *cx, JSObject *obj)
7701 return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2,
7702 qname_props, qname_methods, NULL, NULL);
7705 JSObject *
7706 js_InitAnyNameClass(JSContext *cx, JSObject *obj)
7708 jsval v;
7710 if (!js_GetAnyName(cx, &v))
7711 return NULL;
7712 return JSVAL_TO_OBJECT(v);
7715 JSObject *
7716 js_InitXMLClass(JSContext *cx, JSObject *obj)
7718 JSObject *proto, *pobj;
7719 JSFunction *fun;
7720 JSXML *xml;
7721 JSProperty *prop;
7722 JSScopeProperty *sprop;
7723 jsval cval, vp[3];
7725 /* Define the isXMLName function. */
7726 if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7727 return NULL;
7729 /* Define the XML class constructor and prototype. */
7730 proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
7731 NULL, xml_methods,
7732 xml_static_props, xml_static_methods);
7733 if (!proto)
7734 return NULL;
7736 xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7737 if (!xml || !JS_SetPrivate(cx, proto, xml))
7738 return NULL;
7739 xml->object = proto;
7740 METER(xml_stats.xmlobj);
7741 METER(xml_stats.livexmlobj);
7744 * Prepare to set default settings on the XML constructor we just made.
7745 * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY,
7746 * which is xml_getProperty, which creates a new XMLList every time! We
7747 * must instead call js_LookupProperty directly.
7749 if (!js_LookupProperty(cx, proto,
7750 ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
7751 &pobj, &prop)) {
7752 return NULL;
7754 JS_ASSERT(prop);
7755 sprop = (JSScopeProperty *) prop;
7756 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
7757 cval = OBJ_GET_SLOT(cx, pobj, sprop->slot);
7758 OBJ_DROP_PROPERTY(cx, pobj, prop);
7759 JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
7761 /* Set default settings. */
7762 vp[0] = JSVAL_NULL;
7763 vp[1] = cval;
7764 vp[2] = JSVAL_VOID;
7765 if (!xml_setSettings(cx, 1, vp))
7766 return NULL;
7768 /* Define the XMLList function and give it the same prototype as XML. */
7769 fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0);
7770 if (!fun)
7771 return NULL;
7772 if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
7773 JSPROP_READONLY | JSPROP_PERMANENT)) {
7774 return NULL;
7776 return proto;
7779 JSObject *
7780 js_InitXMLClasses(JSContext *cx, JSObject *obj)
7782 if (!js_InitNamespaceClass(cx, obj))
7783 return NULL;
7784 if (!js_InitQNameClass(cx, obj))
7785 return NULL;
7786 if (!js_InitAttributeNameClass(cx, obj))
7787 return NULL;
7788 if (!js_InitAnyNameClass(cx, obj))
7789 return NULL;
7790 if (!js_InitXMLFilterClass(cx, obj))
7791 return NULL;
7792 return js_InitXMLClass(cx, obj);
7795 JSBool
7796 js_GetFunctionNamespace(JSContext *cx, jsval *vp)
7798 JSRuntime *rt;
7799 JSObject *obj;
7800 JSAtom *atom;
7801 JSString *prefix, *uri;
7803 /* An invalid URI, for internal use only, guaranteed not to collide. */
7804 static const char anti_uri[] = "@mozilla.org/js/function";
7806 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7807 rt = cx->runtime;
7808 obj = rt->functionNamespaceObject;
7809 if (!obj) {
7810 JS_LOCK_GC(rt);
7811 obj = rt->functionNamespaceObject;
7812 if (!obj) {
7813 JS_UNLOCK_GC(rt);
7816 * Note that any race to atomize anti_uri here is resolved by
7817 * the atom table code, such that at most one atom for anti_uri
7818 * is created. We store in rt->atomState.lazy unconditionally,
7819 * since we are guaranteed to overwrite either null or the same
7820 * atom pointer.
7822 atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED);
7823 if (!atom)
7824 return JS_FALSE;
7825 rt->atomState.lazy.functionNamespaceURIAtom = atom;
7827 prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]);
7828 uri = ATOM_TO_STRING(atom);
7829 obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE);
7830 if (!obj)
7831 return JS_FALSE;
7834 * Avoid entraining any in-scope Object.prototype. The loss of
7835 * Namespace.prototype is not detectable, as there is no way to
7836 * refer to this instance in scripts. When used to qualify method
7837 * names, its prefix and uri references are copied to the QName.
7839 OBJ_CLEAR_PROTO(cx, obj);
7840 OBJ_CLEAR_PARENT(cx, obj);
7842 JS_LOCK_GC(rt);
7843 if (!rt->functionNamespaceObject)
7844 rt->functionNamespaceObject = obj;
7845 else
7846 obj = rt->functionNamespaceObject;
7848 JS_UNLOCK_GC(rt);
7850 *vp = OBJECT_TO_JSVAL(obj);
7851 return JS_TRUE;
7855 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7856 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7857 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a
7858 * lightweight function activation). There's no requirement that fp->varobj
7859 * lie directly on fp->scopeChain, although it should be reachable using the
7860 * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h).
7862 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7863 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7864 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7865 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7866 * the default XML namespace will be set to ("", n.uri). So the uri string
7867 * is really the only usefully stored value of the default namespace.
7869 JSBool
7870 js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7872 JSStackFrame *fp;
7873 JSObject *nsobj, *obj, *tmp;
7874 jsval v;
7876 fp = cx->fp;
7877 nsobj = fp->xmlNamespace;
7878 if (nsobj) {
7879 *vp = OBJECT_TO_JSVAL(nsobj);
7880 return JS_TRUE;
7883 obj = NULL;
7884 for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) {
7885 obj = tmp;
7886 if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v))
7887 return JS_FALSE;
7888 if (!JSVAL_IS_PRIMITIVE(v)) {
7889 fp->xmlNamespace = JSVAL_TO_OBJECT(v);
7890 *vp = v;
7891 return JS_TRUE;
7895 nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL);
7896 if (!nsobj)
7897 return JS_FALSE;
7898 v = OBJECT_TO_JSVAL(nsobj);
7899 if (obj &&
7900 !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v,
7901 JS_PropertyStub, JS_PropertyStub,
7902 JSPROP_PERMANENT, NULL)) {
7903 return JS_FALSE;
7905 fp->xmlNamespace = nsobj;
7906 *vp = v;
7907 return JS_TRUE;
7910 JSBool
7911 js_SetDefaultXMLNamespace(JSContext *cx, jsval v)
7913 jsval argv[2];
7914 JSObject *nsobj, *varobj;
7915 JSStackFrame *fp;
7917 argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString);
7918 argv[1] = v;
7919 nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
7920 2, argv);
7921 if (!nsobj)
7922 return JS_FALSE;
7923 v = OBJECT_TO_JSVAL(nsobj);
7925 fp = cx->fp;
7926 varobj = fp->varobj;
7927 if (varobj) {
7928 if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v,
7929 JS_PropertyStub, JS_PropertyStub,
7930 JSPROP_PERMANENT, NULL)) {
7931 return JS_FALSE;
7933 } else {
7934 JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags));
7936 fp->xmlNamespace = JSVAL_TO_OBJECT(v);
7937 return JS_TRUE;
7940 JSBool
7941 js_ToAttributeName(JSContext *cx, jsval *vp)
7943 JSXMLQName *qn;
7945 qn = ToAttributeName(cx, *vp);
7946 if (!qn)
7947 return JS_FALSE;
7948 *vp = OBJECT_TO_JSVAL(qn->object);
7949 return JS_TRUE;
7952 JSString *
7953 js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote)
7955 return EscapeAttributeValue(cx, NULL, str, quote);
7958 JSString *
7959 js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7961 size_t len, len2, newlen;
7962 jschar *chars, *chars2;
7964 JSSTRING_CHARS_AND_LENGTH(str, chars, len);
7965 if (!JSSTRING_IS_MUTABLE(str)) {
7966 str = js_NewStringCopyN(cx, chars, len);
7967 if (!str)
7968 return NULL;
7969 chars = JSFLATSTR_CHARS(str);
7970 } else {
7972 * Reallocating str (because we know it has no other references)
7973 * requires purging any deflated string cached for it.
7975 js_PurgeDeflatedStringCache(cx->runtime, str);
7978 JSSTRING_CHARS_AND_LENGTH(str2, chars2, len2);
7979 newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
7980 chars = (jschar *) JS_realloc(cx, chars, (newlen+1) * sizeof(jschar));
7981 if (!chars)
7982 return NULL;
7984 JSFLATSTR_INIT(str, chars, newlen);
7985 chars += len;
7986 if (isName) {
7987 *chars++ = ' ';
7988 js_strncpy(chars, chars2, len2);
7989 chars += len2;
7990 } else {
7991 *chars++ = '=';
7992 *chars++ = '"';
7993 js_strncpy(chars, chars2, len2);
7994 chars += len2;
7995 *chars++ = '"';
7997 *chars = 0;
7998 return str;
8001 JSString *
8002 js_EscapeElementValue(JSContext *cx, JSString *str)
8004 return EscapeElementValue(cx, NULL, str);
8007 JSString *
8008 js_ValueToXMLString(JSContext *cx, jsval v)
8010 return ToXMLString(cx, v, 0);
8013 static JSBool
8014 anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
8015 jsval *rval)
8017 *rval = ATOM_KEY(cx->runtime->atomState.starAtom);
8018 return JS_TRUE;
8021 JSBool
8022 js_GetAnyName(JSContext *cx, jsval *vp)
8024 JSRuntime *rt;
8025 JSObject *obj;
8026 JSXMLQName *qn;
8027 JSBool ok;
8029 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
8030 rt = cx->runtime;
8031 obj = rt->anynameObject;
8032 if (!obj) {
8033 JS_LOCK_GC(rt);
8034 obj = rt->anynameObject;
8035 if (!obj) {
8036 JS_UNLOCK_GC(rt);
8039 * Protect multiple newborns created below, in the do-while(0)
8040 * loop used to ensure that we leave this local root scope.
8042 ok = js_EnterLocalRootScope(cx);
8043 if (!ok)
8044 return JS_FALSE;
8046 do {
8047 qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString,
8048 ATOM_TO_STRING(rt->atomState.starAtom));
8049 if (!qn) {
8050 ok = JS_FALSE;
8051 break;
8054 obj = js_NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL,
8055 NULL, 0);
8056 if (!obj || !JS_SetPrivate(cx, obj, qn)) {
8057 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
8058 ok = JS_FALSE;
8059 break;
8061 qn->object = obj;
8062 METER(xml_stats.qnameobj);
8063 METER(xml_stats.liveqnameobj);
8066 * Avoid entraining any Object.prototype found via cx's scope
8067 * chain or global object. This loses the default toString,
8068 * but no big deal: we want to customize toString anyway for
8069 * clearer diagnostics.
8071 if (!JS_DefineFunction(cx, obj, js_toString_str,
8072 anyname_toString, 0, 0)) {
8073 ok = JS_FALSE;
8074 break;
8076 JS_ASSERT(!OBJ_GET_PROTO(cx, obj));
8077 JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
8078 } while (0);
8080 js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj));
8081 if (!ok)
8082 return JS_FALSE;
8084 JS_LOCK_GC(rt);
8085 if (!rt->anynameObject)
8086 rt->anynameObject = obj;
8087 else
8088 obj = rt->anynameObject;
8090 JS_UNLOCK_GC(rt);
8092 *vp = OBJECT_TO_JSVAL(obj);
8093 return JS_TRUE;
8096 JSBool
8097 js_FindXMLProperty(JSContext *cx, jsval nameval, JSObject **objp, jsid *idp)
8099 JSObject *nameobj;
8100 jsval v;
8101 JSXMLQName *qn;
8102 jsid funid;
8103 JSObject *obj, *target, *proto, *pobj;
8104 JSXML *xml;
8105 JSBool found;
8106 JSProperty *prop;
8107 const char *printable;
8109 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nameval));
8110 nameobj = JSVAL_TO_OBJECT(nameval);
8111 if (OBJ_GET_CLASS(cx, nameobj) == &js_AnyNameClass) {
8112 v = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.starAtom));
8113 nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1,
8114 &v);
8115 if (!nameobj)
8116 return JS_FALSE;
8117 } else {
8118 JS_ASSERT(OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass ||
8119 OBJ_GET_CLASS(cx, nameobj) == &js_QNameClass.base);
8122 qn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
8123 if (!IsFunctionQName(cx, qn, &funid))
8124 return JS_FALSE;
8126 obj = cx->fp->scopeChain;
8127 do {
8128 /* Skip any With object that can wrap XML. */
8129 target = obj;
8130 while (OBJ_GET_CLASS(cx, target) == &js_WithClass) {
8131 proto = OBJ_GET_PROTO(cx, target);
8132 if (!proto)
8133 break;
8134 target = proto;
8137 if (OBJECT_IS_XML(cx, target)) {
8138 if (funid == 0) {
8139 xml = (JSXML *) JS_GetPrivate(cx, target);
8140 found = HasNamedProperty(xml, qn);
8141 } else {
8142 if (!HasFunctionProperty(cx, target, funid, &found))
8143 return JS_FALSE;
8145 if (found) {
8146 *idp = OBJECT_TO_JSID(nameobj);
8147 *objp = target;
8148 return JS_TRUE;
8150 } else if (funid != 0) {
8151 if (!OBJ_LOOKUP_PROPERTY(cx, target, funid, &pobj, &prop))
8152 return JS_FALSE;
8153 if (prop) {
8154 OBJ_DROP_PROPERTY(cx, pobj, prop);
8155 *idp = funid;
8156 *objp = target;
8157 return JS_TRUE;
8160 } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
8162 printable = js_ValueToPrintableString(cx, OBJECT_TO_JSVAL(nameobj));
8163 if (printable) {
8164 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
8165 js_GetErrorMessage, NULL,
8166 JSMSG_UNDEFINED_XML_NAME, printable);
8168 return JS_FALSE;
8171 JSBool
8172 js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
8174 JSObject *target;
8175 JSXML *xml;
8176 JSTempValueRooter tvr;
8177 JSBool ok;
8179 JS_ASSERT(OBJECT_IS_XML(cx, obj));
8181 /* After this point, control must flow through label out: to exit. */
8182 JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
8185 * See comments before xml_lookupProperty about the need for the proto
8186 * chain lookup.
8188 target = obj;
8189 for (;;) {
8190 ok = js_GetProperty(cx, target, id, vp);
8191 if (!ok)
8192 goto out;
8193 if (VALUE_IS_FUNCTION(cx, *vp)) {
8194 ok = JS_TRUE;
8195 goto out;
8197 target = OBJ_GET_PROTO(cx, target);
8198 if (target == NULL)
8199 break;
8200 tvr.u.object = target;
8203 xml = (JSXML *) JS_GetPrivate(cx, obj);
8204 if (HasSimpleContent(xml)) {
8205 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
8206 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
8207 &tvr.u.object);
8208 if (!ok)
8209 goto out;
8210 JS_ASSERT(tvr.u.object);
8211 ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp);
8214 out:
8215 JS_POP_TEMP_ROOT(cx, &tvr);
8216 return ok;
8219 static JSXML *
8220 GetPrivate(JSContext *cx, JSObject *obj, const char *method)
8222 JSXML *xml;
8224 xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
8225 if (!xml) {
8226 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
8227 JSMSG_INCOMPATIBLE_METHOD,
8228 js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name);
8230 return xml;
8233 JSBool
8234 js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
8236 JSXML *xml, *list;
8238 xml = GetPrivate(cx, obj, "descendants internal method");
8239 if (!xml)
8240 return JS_FALSE;
8242 list = Descendants(cx, xml, id);
8243 if (!list)
8244 return JS_FALSE;
8245 *vp = OBJECT_TO_JSVAL(list->object);
8246 return JS_TRUE;
8249 JSBool
8250 js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
8252 JSXML *list;
8253 uint32 n;
8255 list = (JSXML *) JS_GetPrivate(cx, listobj);
8256 for (n = list->xml_kids.length; n != 0; --n)
8257 DeleteListElement(cx, list, 0);
8259 return JS_TRUE;
8262 typedef struct JSXMLFilter {
8263 JSXML *list;
8264 JSXML *result;
8265 JSXML *kid;
8266 JSXMLArrayCursor cursor;
8268 } JSXMLFilter;
8270 static void
8271 xmlfilter_trace(JSTracer *trc, JSObject *obj)
8273 JSXMLFilter *filter;
8275 filter = (JSXMLFilter *) JS_GetPrivate(trc->context, obj);
8276 if (!filter)
8277 return;
8279 JS_ASSERT(filter->list);
8280 JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list");
8281 if (filter->result)
8282 JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result");
8283 if (filter->kid)
8284 JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid");
8287 * We do not need to trace the cursor as that would be done when
8288 * tracing the filter->list.
8292 static void
8293 xmlfilter_finalize(JSContext *cx, JSObject *obj)
8295 JSXMLFilter *filter;
8297 filter = (JSXMLFilter *) JS_GetPrivate(cx, obj);
8298 if (!filter)
8299 return;
8301 XMLArrayCursorFinish(&filter->cursor);
8302 JS_free(cx, filter);
8305 JSClass js_XMLFilterClass = {
8306 "XMLFilter",
8307 JSCLASS_HAS_PRIVATE |
8308 JSCLASS_IS_ANONYMOUS |
8309 JSCLASS_MARK_IS_TRACE |
8310 JSCLASS_HAS_CACHED_PROTO(JSProto_XMLFilter),
8311 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
8312 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xmlfilter_finalize,
8313 NULL, NULL, NULL, NULL,
8314 NULL, NULL, JS_CLASS_TRACE(xmlfilter_trace), NULL
8317 JSObject *
8318 js_InitXMLFilterClass(JSContext *cx, JSObject *obj)
8320 JSObject *proto;
8322 proto = JS_InitClass(cx, obj, NULL, &js_XMLFilterClass, NULL, 0, NULL,
8323 NULL, NULL, NULL);
8324 if (!proto)
8325 return NULL;
8327 OBJ_CLEAR_PROTO(cx, proto);
8328 return proto;
8331 JSBool
8332 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
8334 jsval *sp;
8335 JSObject *obj, *filterobj, *resobj, *kidobj;
8336 JSXML *xml, *list;
8337 JSXMLFilter *filter;
8339 sp = cx->fp->regs->sp;
8340 if (!initialized) {
8342 * We haven't iterated yet, so initialize the filter based on the
8343 * value stored in sp[-2].
8345 if (!VALUE_IS_XML(cx, sp[-2])) {
8346 js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL);
8347 return JS_FALSE;
8349 obj = JSVAL_TO_OBJECT(sp[-2]);
8350 xml = (JSXML *) JS_GetPrivate(cx, obj);
8352 if (xml->xml_class == JSXML_CLASS_LIST) {
8353 list = xml;
8354 } else {
8355 obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
8356 if (!obj)
8357 return JS_FALSE;
8360 * Root just-created obj. sp[-2] cannot be used yet for rooting
8361 * as it may be the only root holding xml.
8363 sp[-1] = OBJECT_TO_JSVAL(obj);
8364 list = (JSXML *) JS_GetPrivate(cx, obj);
8365 if (!Append(cx, list, xml))
8366 return JS_FALSE;
8369 filterobj = js_NewObject(cx, &js_XMLFilterClass, NULL, NULL, 0);
8370 if (!filterobj)
8371 return JS_FALSE;
8373 filter = (JSXMLFilter *) JS_malloc(cx, sizeof *filter);
8374 if (!filter)
8375 return JS_FALSE;
8378 * Init all filter fields before JS_SetPrivate exposes it to
8379 * xmlfilter_trace or xmlfilter_finalize.
8381 filter->list = list;
8382 filter->result = NULL;
8383 filter->kid = NULL;
8384 XMLArrayCursorInit(&filter->cursor, &list->xml_kids);
8385 JS_SetPrivate(cx, filterobj, filter);
8387 /* Store filterobj to use in the later iterations. */
8388 sp[-2] = OBJECT_TO_JSVAL(filterobj);
8390 resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
8391 if (!resobj)
8392 return JS_FALSE;
8394 /* This also roots resobj. */
8395 filter->result = (JSXML *) JS_GetPrivate(cx, resobj);
8396 } else {
8397 /* We have iterated at least once. */
8398 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
8399 JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(sp[-2])) ==
8400 &js_XMLFilterClass);
8401 filter = (JSXMLFilter *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(sp[-2]));
8402 JS_ASSERT(filter->kid);
8404 /* Check if the filter expression wants to append the element. */
8405 if (js_ValueToBoolean(sp[-1]) &&
8406 !Append(cx, filter->result, filter->kid)) {
8407 return JS_FALSE;
8411 /* Do the iteration. */
8412 filter->kid = (JSXML *) XMLArrayCursorNext(&filter->cursor);
8413 if (!filter->kid) {
8415 * Do not defer finishing the cursor until the next GC cycle to avoid
8416 * accumulation of dead cursors associated with filter->list.
8418 XMLArrayCursorFinish(&filter->cursor);
8419 JS_ASSERT(filter->result->object);
8420 sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
8421 kidobj = NULL;
8422 } else {
8423 kidobj = js_GetXMLObject(cx, filter->kid);
8424 if (!kidobj)
8425 return JS_FALSE;
8428 /* Null as kidobj at sp[-1] signals filter termination. */
8429 sp[-1] = OBJECT_TO_JSVAL(kidobj);
8430 return JS_TRUE;
8433 JSObject *
8434 js_ValueToXMLObject(JSContext *cx, jsval v)
8436 return ToXML(cx, v);
8439 JSObject *
8440 js_ValueToXMLListObject(JSContext *cx, jsval v)
8442 return ToXMLList(cx, v);
8445 JSObject *
8446 js_CloneXMLObject(JSContext *cx, JSObject *obj)
8448 uintN flags;
8449 JSXML *xml;
8451 if (!GetXMLSettingFlags(cx, &flags))
8452 return NULL;
8453 xml = (JSXML *) JS_GetPrivate(cx, obj);
8454 if (flags & (XSF_IGNORE_COMMENTS |
8455 XSF_IGNORE_PROCESSING_INSTRUCTIONS |
8456 XSF_IGNORE_WHITESPACE)) {
8457 xml = DeepCopy(cx, xml, NULL, flags);
8458 if (!xml)
8459 return NULL;
8460 return xml->object;
8462 return NewXMLObject(cx, xml);
8465 JSObject *
8466 js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
8467 JSString *value)
8469 uintN flags;
8470 JSObject *obj;
8471 JSXML *xml;
8472 JSXMLQName *qn;
8474 if (!GetXMLSettingFlags(cx, &flags))
8475 return NULL;
8477 if ((xml_class == JSXML_CLASS_COMMENT &&
8478 (flags & XSF_IGNORE_COMMENTS)) ||
8479 (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
8480 (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
8481 return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
8484 obj = js_NewXMLObject(cx, xml_class);
8485 if (!obj)
8486 return NULL;
8487 xml = (JSXML *) JS_GetPrivate(cx, obj);
8488 if (name) {
8489 qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name);
8490 if (!qn)
8491 return NULL;
8492 xml->name = qn;
8494 xml->xml_value = value;
8495 return obj;
8498 JSString *
8499 js_MakeXMLCDATAString(JSContext *cx, JSString *str)
8501 return MakeXMLCDATAString(cx, NULL, str);
8504 JSString *
8505 js_MakeXMLCommentString(JSContext *cx, JSString *str)
8507 return MakeXMLCommentString(cx, NULL, str);
8510 JSString *
8511 js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
8513 return MakeXMLPIString(cx, NULL, name, str);
8516 #endif /* JS_HAS_XML_SUPPORT */