Bug 469739 - Add support for displaying Vista UAC shield icon; r=joe sr=vladimir
[wine-gecko.git] / js / src / jsxml.cpp
blob9027340497b401ce4ff5536bffaf6d8bf1bb9009
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 "jsversion.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"
70 #include "jsstaticcheck.h"
72 #ifdef DEBUG
73 #include <string.h> /* for #ifdef DEBUG memset calls */
74 #endif
77 * NOTES
78 * - in the js shell, you must use the -x command line option, or call
79 * options('xml') before compiling anything that uses XML literals
81 * TODO
82 * - XXXbe patrol
83 * - Fuse objects and their JSXML* private data into single GC-things
84 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
85 * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
86 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
87 * - JS_TypeOfValue sure could use a cleaner interface to "types"
90 #ifdef XML_METERING
91 static struct {
92 jsrefcount qname;
93 jsrefcount xmlnamespace;
94 jsrefcount xml;
95 jsrefcount xmlobj;
96 } xml_stats;
98 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
99 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
100 #else
101 #define METER(x) /* nothing */
102 #define UNMETER(x) /* nothing */
103 #endif
106 * Random utilities and global functions.
108 const char js_isXMLName_str[] = "isXMLName";
109 const char js_XMLList_str[] = "XMLList";
110 const char js_localName_str[] = "localName";
111 const char js_xml_parent_str[] = "parent";
112 const char js_prefix_str[] = "prefix";
113 const char js_toXMLString_str[] = "toXMLString";
114 const char js_uri_str[] = "uri";
116 const char js_amp_entity_str[] = "&amp;";
117 const char js_gt_entity_str[] = "&gt;";
118 const char js_lt_entity_str[] = "&lt;";
119 const char js_quot_entity_str[] = "&quot;";
121 #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
122 #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
123 /* Slot indexes shared between Namespace and QName objects. */
124 const uint32 JSSLOT_PREFIX = JSSLOT_PRIVATE;
125 const uint32 JSSLOT_URI = JSSLOT_PRIVATE + 1;
127 /* Namespace-specific slot. */
128 const uint32 JSSLOT_DECLARED = JSSLOT_PRIVATE + 2;
130 /* QName-specific slot. */
131 const uint32 JSSLOT_LOCAL_NAME = JSSLOT_PRIVATE + 2;
133 const uint32 NAMESPACE_RESERVED_SLOTS = 3;
134 const uint32 QNAME_RESERVED_SLOTS = 3;
136 static JSBool
137 IsQNameClass(JSClass *clasp)
139 return clasp == &js_QNameClass.base ||
140 clasp == &js_AttributeNameClass ||
141 clasp == &js_AnyNameClass;
144 static JSString *
145 GetSlotString(const JSObject *obj, uint32 slot)
147 jsval v;
149 JS_ASSERT(slot == JSSLOT_PREFIX ||
150 slot == JSSLOT_URI ||
151 slot == JSSLOT_LOCAL_NAME);
152 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base ||
153 IsQNameClass(STOBJ_GET_CLASS(obj)));
154 JS_ASSERT_IF(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base,
155 slot != JSSLOT_LOCAL_NAME);
157 v = obj->fslots[slot];
158 if (JSVAL_IS_VOID(v))
159 return NULL;
160 JS_ASSERT(JSVAL_IS_STRING(v));
161 return JSVAL_TO_STRING(v);
164 static JS_INLINE JSString *
165 GetPrefix(const JSObject *obj)
167 return GetSlotString(obj, JSSLOT_PREFIX);
170 static JSString *
171 GetURI(const JSObject *obj)
173 return GetSlotString(obj, JSSLOT_URI);
176 static JSString *
177 GetLocalName(const JSObject *obj)
179 return GetSlotString(obj, JSSLOT_LOCAL_NAME);
182 static JSBool
183 IsDeclared(const JSObject *obj)
185 jsval v;
187 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base);
188 v = obj->fslots[JSSLOT_DECLARED];
189 JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
190 return v == JSVAL_TRUE;
193 static JSBool
194 xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
195 jsval *rval)
197 *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
198 return JS_TRUE;
202 * Namespace class and library functions.
204 enum namespace_tinyid {
205 NAMESPACE_PREFIX = -1,
206 NAMESPACE_URI = -2
209 static JSBool
210 namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
212 if (!JSVAL_IS_INT(id))
213 return JS_TRUE;
215 if (STOBJ_GET_CLASS(obj) != &js_NamespaceClass.base)
216 return JS_TRUE;
218 switch (JSVAL_TO_INT(id)) {
219 case NAMESPACE_PREFIX:
220 *vp = obj->fslots[JSSLOT_PREFIX];
221 break;
222 case NAMESPACE_URI:
223 *vp = obj->fslots[JSSLOT_URI];
224 break;
226 return JS_TRUE;
229 static void
230 namespace_finalize(JSContext *cx, JSObject *obj)
232 if (cx->runtime->functionNamespaceObject == obj)
233 cx->runtime->functionNamespaceObject = NULL;
236 static JSBool
237 namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
239 JSObject *obj2;
241 JS_ASSERT(JSVAL_IS_OBJECT(v));
242 obj2 = JSVAL_TO_OBJECT(v);
243 *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base)
244 ? JS_FALSE
245 : js_EqualStrings(GetURI(obj), GetURI(obj2));
246 return JS_TRUE;
249 JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
250 { "Namespace",
251 JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
252 JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS) |
253 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
254 JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL,
255 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize,
256 NULL, NULL, NULL, NULL,
257 NULL, NULL, NULL, NULL },
258 namespace_equality,NULL, NULL, NULL,
259 NULL, NULL, NULL, NULL
262 #define NAMESPACE_ATTRS \
263 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
265 static JSPropertySpec namespace_props[] = {
266 {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0},
267 {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0},
268 {0,0,0,0,0}
271 static JSBool
272 namespace_toString(JSContext *cx, uintN argc, jsval *vp)
274 JSObject *obj;
276 obj = JS_THIS_OBJECT(cx, vp);
277 if (!JS_InstanceOf(cx, obj, &js_NamespaceClass.base, vp))
278 return JS_FALSE;
279 *vp = obj->fslots[JSSLOT_URI];
280 return JS_TRUE;
283 static JSFunctionSpec namespace_methods[] = {
284 JS_FN(js_toString_str, namespace_toString, 0,0),
285 JS_FS_END
288 static JSObject *
289 NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
291 JSObject *obj;
293 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
294 if (!obj)
295 return JS_FALSE;
296 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
297 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
298 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_DECLARED]));
299 if (prefix)
300 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
301 if (uri)
302 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
303 if (declared)
304 obj->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
305 METER(xml_stats.xmlnamespace);
306 return obj;
310 * QName class and library functions.
312 enum qname_tinyid {
313 QNAME_URI = -1,
314 QNAME_LOCALNAME = -2
317 static JSBool
318 qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
320 if (!JSVAL_IS_INT(id))
321 return JS_TRUE;
323 if (STOBJ_GET_CLASS(obj) != &js_QNameClass.base)
324 return JS_TRUE;
326 switch (JSVAL_TO_INT(id)) {
327 case QNAME_URI:
328 *vp = obj->fslots[JSSLOT_URI];
329 if (*vp == JSVAL_VOID)
330 *vp = JSVAL_NULL;
331 break;
332 case QNAME_LOCALNAME:
333 *vp = obj->fslots[JSSLOT_LOCAL_NAME];
334 break;
336 return JS_TRUE;
339 static void
340 anyname_finalize(JSContext* cx, JSObject* obj)
342 /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
343 if (cx->runtime->anynameObject == obj)
344 cx->runtime->anynameObject = NULL;
347 static JSBool
348 qname_identity(JSObject *qna, JSObject *qnb)
350 JSString *uri1 = GetURI(qna);
351 JSString *uri2 = GetURI(qnb);
353 if (!uri1 ^ !uri2)
354 return JS_FALSE;
355 if (uri1 && !js_EqualStrings(uri1, uri2))
356 return JS_FALSE;
357 return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb));
360 static JSBool
361 qname_equality(JSContext *cx, JSObject *qn, jsval v, JSBool *bp)
363 JSObject *obj2;
365 JS_ASSERT(JSVAL_IS_OBJECT(v));
366 obj2 = JSVAL_TO_OBJECT(v);
367 *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base)
368 ? JS_FALSE
369 : qname_identity(qn, obj2);
370 return JS_TRUE;
373 JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
374 { "QName",
375 JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
376 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
377 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
378 JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL,
379 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
380 NULL, NULL, NULL, NULL,
381 NULL, NULL, NULL, NULL },
382 qname_equality, NULL, NULL, NULL,
383 NULL, NULL, NULL, NULL
387 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
388 * are like QName, except that they have no property getters. They share the
389 * qname_toString method, and therefore are exposed as constructable objects
390 * in this implementation.
392 JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
393 js_AttributeName_str,
394 JSCLASS_CONSTRUCT_PROTOTYPE |
395 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
396 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
397 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
398 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
399 NULL, NULL, NULL, NULL,
400 NULL, NULL, NULL, NULL
403 JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
404 js_AnyName_str,
405 JSCLASS_CONSTRUCT_PROTOTYPE |
406 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
407 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
408 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
409 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize,
410 NULL, NULL, NULL, NULL,
411 NULL, NULL, NULL, NULL
414 #define QNAME_ATTRS \
415 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
417 static JSPropertySpec qname_props[] = {
418 {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0},
419 {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0},
420 {0,0,0,0,0}
423 static JSBool
424 qname_toString(JSContext *cx, uintN argc, jsval *vp)
426 JSObject *obj;
427 JSClass *clasp;
428 JSString *uri, *str, *qualstr;
429 size_t length;
430 jschar *chars;
432 obj = JS_THIS_OBJECT(cx, vp);
433 if (!obj)
434 return JS_FALSE;
435 clasp = OBJ_GET_CLASS(cx, obj);
436 if (clasp != &js_AttributeNameClass &&
437 clasp != &js_AnyNameClass &&
438 !JS_InstanceOf(cx, obj, &js_QNameClass.base, vp + 2)) {
439 return JS_FALSE;
442 uri = GetURI(obj);
443 if (!uri) {
444 /* No uri means wildcard qualifier. */
445 str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
446 } else if (IS_EMPTY(uri)) {
447 /* Empty string for uri means localName is in no namespace. */
448 str = cx->runtime->emptyString;
449 } else {
450 qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
451 str = js_ConcatStrings(cx, uri, qualstr);
452 if (!str)
453 return JS_FALSE;
455 str = js_ConcatStrings(cx, str, GetLocalName(obj));
456 if (!str)
457 return JS_FALSE;
459 if (str && clasp == &js_AttributeNameClass) {
460 length = JSSTRING_LENGTH(str);
461 chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
462 if (!chars)
463 return JS_FALSE;
464 *chars = '@';
465 js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
466 chars[++length] = 0;
467 str = js_NewString(cx, chars, length);
468 if (!str) {
469 JS_free(cx, chars);
470 return JS_FALSE;
474 *vp = STRING_TO_JSVAL(str);
475 return JS_TRUE;
478 static JSFunctionSpec qname_methods[] = {
479 JS_FN(js_toString_str, qname_toString, 0,0),
480 JS_FS_END
484 static void
485 InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix,
486 JSString *localName)
488 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
489 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
490 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_LOCAL_NAME]));
491 if (uri)
492 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
493 if (prefix)
494 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
495 if (localName)
496 obj->fslots[JSSLOT_LOCAL_NAME] = STRING_TO_JSVAL(localName);
499 static JSObject *
500 NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
501 JSClass *clasp = &js_QNameClass.base)
503 JSObject *obj;
505 JS_ASSERT(IsQNameClass(clasp));
506 obj = js_NewObject(cx, clasp, NULL, NULL, 0);
507 if (!obj)
508 return NULL;
509 InitXMLQName(obj, uri, prefix, localName);
510 METER(xml_stats.qname);
511 return obj;
514 JSObject *
515 js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
517 jsval argv[2];
520 * ECMA-357 11.1.2,
521 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
522 * production, step 2.
524 if (!JSVAL_IS_PRIMITIVE(nsval) &&
525 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
526 nsval = JSVAL_NULL;
529 argv[0] = nsval;
530 argv[1] = lnval;
531 return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
534 static JSBool
535 IsXMLName(const jschar *cp, size_t n)
537 JSBool rv;
538 jschar c;
540 rv = JS_FALSE;
541 if (n != 0 && JS_ISXMLNSSTART(*cp)) {
542 while (--n != 0) {
543 c = *++cp;
544 if (!JS_ISXMLNS(c))
545 return rv;
547 rv = JS_TRUE;
549 return rv;
552 JSBool
553 js_IsXMLName(JSContext *cx, jsval v)
555 JSString *name;
556 JSErrorReporter older;
559 * Inline specialization of the QName constructor called with v passed as
560 * the only argument, to compute the localName for the constructed qname,
561 * without actually allocating the object or computing its uri and prefix.
562 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
564 if (!JSVAL_IS_PRIMITIVE(v) &&
565 IsQNameClass(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)))) {
566 name = GetLocalName(JSVAL_TO_OBJECT(v));
567 } else {
568 older = JS_SetErrorReporter(cx, NULL);
569 name = js_ValueToString(cx, v);
570 JS_SetErrorReporter(cx, older);
571 if (!name) {
572 JS_ClearPendingException(cx);
573 return JS_FALSE;
577 return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
581 * When argc is -1, it indicates argv is empty but the code should behave as
582 * if argc is 1 and argv[0] is JSVAL_VOID.
584 static JSBool
585 NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
586 jsval *rval)
588 jsval urival, prefixval;
589 JSObject *uriobj;
590 JSBool isNamespace, isQName;
591 JSClass *clasp;
592 JSString *empty, *uri, *prefix;
594 isNamespace = isQName = JS_FALSE;
595 #ifdef __GNUC__ /* suppress bogus gcc warnings */
596 uriobj = NULL;
597 #endif
598 if (argc <= 0) {
599 urival = JSVAL_VOID;
600 } else {
601 urival = argv[argc > 1];
602 if (!JSVAL_IS_PRIMITIVE(urival)) {
603 uriobj = JSVAL_TO_OBJECT(urival);
604 clasp = OBJ_GET_CLASS(cx, uriobj);
605 isNamespace = (clasp == &js_NamespaceClass.base);
606 isQName = (clasp == &js_QNameClass.base);
610 if (!obj) {
611 /* Namespace called as function. */
612 if (argc == 1 && isNamespace) {
613 /* Namespace called with one Namespace argument is identity. */
614 *rval = urival;
615 return JS_TRUE;
618 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
619 if (!obj)
620 return JS_FALSE;
621 *rval = OBJECT_TO_JSVAL(obj);
623 METER(xml_stats.xmlnamespace);
625 empty = cx->runtime->emptyString;
626 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(empty);
627 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(empty);
629 if (argc == 1 || argc == -1) {
630 if (isNamespace) {
631 obj->fslots[JSSLOT_URI] = uriobj->fslots[JSSLOT_URI];
632 obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
633 } else if (isQName && (uri = GetURI(uriobj))) {
634 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
635 obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
636 } else {
637 uri = js_ValueToString(cx, urival);
638 if (!uri)
639 return JS_FALSE;
640 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
641 if (!IS_EMPTY(uri))
642 obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
644 } else if (argc == 2) {
645 if (!isQName || !(uri = GetURI(uriobj))) {
646 uri = js_ValueToString(cx, urival);
647 if (!uri)
648 return JS_FALSE;
650 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
652 prefixval = argv[0];
653 if (IS_EMPTY(uri)) {
654 if (!JSVAL_IS_VOID(prefixval)) {
655 prefix = js_ValueToString(cx, prefixval);
656 if (!prefix)
657 return JS_FALSE;
658 if (!IS_EMPTY(prefix)) {
659 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
660 JSMSG_BAD_XML_NAMESPACE,
661 js_ValueToPrintableString(cx,
662 STRING_TO_JSVAL(prefix)));
663 return JS_FALSE;
666 } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
667 obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
668 } else {
669 prefix = js_ValueToString(cx, prefixval);
670 if (!prefix)
671 return JS_FALSE;
672 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
676 return JS_TRUE;
679 static JSBool
680 Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
682 return NamespaceHelper(cx,
683 JS_IsConstructing(cx) ? obj : NULL,
684 argc, argv, rval);
688 * When argc is -1, it indicates argv is empty but the code should behave as
689 * if argc is 1 and argv[0] is JSVAL_VOID.
691 static JSBool
692 QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
693 jsval *argv, jsval *rval)
695 jsval nameval, nsval;
696 JSBool isQName, isNamespace;
697 JSObject *qn;
698 JSString *uri, *prefix, *name;
699 JSObject *obj2;
701 JS_ASSERT(clasp == &js_QNameClass.base ||
702 clasp == &js_AttributeNameClass);
703 if (argc <= 0) {
704 nameval = JSVAL_VOID;
705 isQName = JS_FALSE;
706 } else {
707 nameval = argv[argc > 1];
708 isQName =
709 !JSVAL_IS_PRIMITIVE(nameval) &&
710 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
713 if (!obj) {
714 /* QName called as function. */
715 if (argc == 1 && isQName) {
716 /* QName called with one QName argument is identity. */
717 *rval = nameval;
718 return JS_TRUE;
722 * Create and return a new QName or AttributeName object exactly as if
723 * constructed.
725 obj = js_NewObject(cx, clasp, NULL, NULL, 0);
726 if (!obj)
727 return JS_FALSE;
728 *rval = OBJECT_TO_JSVAL(obj);
730 METER(xml_stats.qname);
732 if (isQName) {
733 /* If namespace is not specified and name is a QName, clone it. */
734 qn = JSVAL_TO_OBJECT(nameval);
735 if (argc == 1) {
736 uri = GetURI(qn);
737 prefix = GetPrefix(qn);
738 name = GetLocalName(qn);
739 goto out;
742 /* Namespace and qname were passed -- use the qname's localName. */
743 nameval = qn->fslots[JSSLOT_LOCAL_NAME];
746 if (argc == 0) {
747 name = cx->runtime->emptyString;
748 } else if (argc < 0) {
749 name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
750 } else {
751 name = js_ValueToString(cx, nameval);
752 if (!name)
753 return JS_FALSE;
754 argv[argc > 1] = STRING_TO_JSVAL(name);
757 if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
758 nsval = argv[0];
759 } else if (IS_STAR(name)) {
760 nsval = JSVAL_NULL;
761 } else {
762 if (!js_GetDefaultXMLNamespace(cx, &nsval))
763 return JS_FALSE;
764 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
765 JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) ==
766 &js_NamespaceClass.base);
769 if (JSVAL_IS_NULL(nsval)) {
770 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
771 uri = prefix = NULL;
772 } else {
774 * Inline specialization of the Namespace constructor called with
775 * nsval passed as the only argument, to compute the uri and prefix
776 * for the constructed namespace, without actually allocating the
777 * object or computing other members. See ECMA-357 13.3.2 6(a) and
778 * 13.2.2.
780 isNamespace = isQName = JS_FALSE;
781 if (!JSVAL_IS_PRIMITIVE(nsval)) {
782 obj2 = JSVAL_TO_OBJECT(nsval);
783 clasp = OBJ_GET_CLASS(cx, obj2);
784 isNamespace = (clasp == &js_NamespaceClass.base);
785 isQName = (clasp == &js_QNameClass.base);
787 #ifdef __GNUC__ /* suppress bogus gcc warnings */
788 else obj2 = NULL;
789 #endif
791 if (isNamespace) {
792 uri = GetURI(obj2);
793 prefix = GetPrefix(obj2);
794 } else if (isQName && (uri = GetURI(obj2))) {
795 JS_ASSERT(argc > 1);
796 prefix = GetPrefix(obj2);
797 } else {
798 JS_ASSERT(argc > 1);
799 uri = js_ValueToString(cx, nsval);
800 if (!uri)
801 return JS_FALSE;
802 argv[0] = STRING_TO_JSVAL(uri); /* local root */
804 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
805 prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
809 out:
810 InitXMLQName(obj, uri, prefix, name);
811 return JS_TRUE;
814 static JSBool
815 QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
817 return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL,
818 &js_QNameClass.base, argc, argv, rval);
821 static JSBool
822 AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
823 jsval *rval)
825 return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL,
826 &js_AttributeNameClass, argc, argv, rval);
830 * XMLArray library functions.
832 static JSBool
833 namespace_identity(const void *a, const void *b)
835 const JSObject *nsa = (const JSObject *) a;
836 const JSObject *nsb = (const JSObject *) b;
837 JSString *prefixa = GetPrefix(nsa);
838 JSString *prefixb = GetPrefix(nsb);
840 if (prefixa && prefixb) {
841 if (!js_EqualStrings(prefixa, prefixb))
842 return JS_FALSE;
843 } else {
844 if (prefixa || prefixb)
845 return JS_FALSE;
847 return js_EqualStrings(GetURI(nsa), GetURI(nsb));
850 static JSBool
851 attr_identity(const void *a, const void *b)
853 const JSXML *xmla = (const JSXML *) a;
854 const JSXML *xmlb = (const JSXML *) b;
856 return qname_identity(xmla->name, xmlb->name);
859 static void
860 XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
862 JSXMLArrayCursor *next;
864 cursor->array = array;
865 cursor->index = 0;
866 next = cursor->next = array->cursors;
867 if (next)
868 next->prevp = &cursor->next;
869 cursor->prevp = &array->cursors;
870 array->cursors = cursor;
871 cursor->root = NULL;
874 static void
875 XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
877 JSXMLArrayCursor *next;
879 if (!cursor->array)
880 return;
881 next = cursor->next;
882 if (next)
883 next->prevp = cursor->prevp;
884 *cursor->prevp = next;
885 cursor->array = NULL;
888 static void *
889 XMLArrayCursorNext(JSXMLArrayCursor *cursor)
891 JSXMLArray *array;
893 array = cursor->array;
894 if (!array || cursor->index >= array->length)
895 return NULL;
896 return cursor->root = array->vector[cursor->index++];
899 static void *
900 XMLArrayCursorItem(JSXMLArrayCursor *cursor)
902 JSXMLArray *array;
904 array = cursor->array;
905 if (!array || cursor->index >= array->length)
906 return NULL;
907 return cursor->root = array->vector[cursor->index];
910 static void
911 XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
913 void *root;
914 #ifdef DEBUG
915 size_t index = 0;
916 #endif
918 for (; cursor; cursor = cursor->next) {
919 root = cursor->root;
920 JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
921 js_CallValueTracerIfGCThing(trc, (jsval)root);
925 /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
926 static JSBool
927 XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
929 void **vector;
931 if (capacity == 0) {
932 /* We could let realloc(p, 0) free this, but purify gets confused. */
933 if (array->vector)
934 free(array->vector);
935 vector = NULL;
936 } else {
937 if (
938 #if JS_BITS_PER_WORD == 32
939 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
940 #endif
941 !(vector = (void **)
942 realloc(array->vector, capacity * sizeof(void *)))) {
943 if (cx)
944 JS_ReportOutOfMemory(cx);
945 return JS_FALSE;
948 array->capacity = JSXML_PRESET_CAPACITY | capacity;
949 array->vector = vector;
950 return JS_TRUE;
953 static void
954 XMLArrayTrim(JSXMLArray *array)
956 if (array->capacity & JSXML_PRESET_CAPACITY)
957 return;
958 if (array->length < array->capacity)
959 XMLArraySetCapacity(NULL, array, array->length);
962 static JSBool
963 XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
965 array->length = array->capacity = 0;
966 array->vector = NULL;
967 array->cursors = NULL;
968 return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
971 static void
972 XMLArrayFinish(JSContext *cx, JSXMLArray *array)
974 JSXMLArrayCursor *cursor;
976 JS_free(cx, array->vector);
978 while ((cursor = array->cursors) != NULL)
979 XMLArrayCursorFinish(cursor);
981 #ifdef DEBUG
982 memset(array, 0xd5, sizeof *array);
983 #endif
986 #define XML_NOT_FOUND ((uint32) -1)
988 static uint32
989 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
991 void **vector;
992 uint32 i, n;
994 /* The identity op must not reallocate array->vector. */
995 vector = array->vector;
996 if (identity) {
997 for (i = 0, n = array->length; i < n; i++) {
998 if (identity(vector[i], elt))
999 return i;
1001 } else {
1002 for (i = 0, n = array->length; i < n; i++) {
1003 if (vector[i] == elt)
1004 return i;
1007 return XML_NOT_FOUND;
1011 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1012 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1013 * should be greater than increment.
1015 #define LINEAR_THRESHOLD 256
1016 #define LINEAR_INCREMENT 32
1018 static JSBool
1019 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
1021 uint32 capacity, i;
1022 int log2;
1023 void **vector;
1025 if (index >= array->length) {
1026 if (index >= JSXML_CAPACITY(array)) {
1027 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1028 capacity = index + 1;
1029 if (index >= LINEAR_THRESHOLD) {
1030 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
1031 } else {
1032 JS_CEILING_LOG2(log2, capacity);
1033 capacity = JS_BIT(log2);
1035 if (
1036 #if JS_BITS_PER_WORD == 32
1037 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1038 #endif
1039 !(vector = (void **)
1040 realloc(array->vector, capacity * sizeof(void *)))) {
1041 JS_ReportOutOfMemory(cx);
1042 return JS_FALSE;
1044 array->capacity = capacity;
1045 array->vector = vector;
1046 for (i = array->length; i < index; i++)
1047 vector[i] = NULL;
1049 array->length = index + 1;
1052 array->vector[index] = elt;
1053 return JS_TRUE;
1056 static JSBool
1057 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1059 uint32 j;
1060 JSXMLArrayCursor *cursor;
1062 j = array->length;
1063 JS_ASSERT(i <= j);
1064 if (!XMLArraySetCapacity(cx, array, j + n))
1065 return JS_FALSE;
1067 array->length = j + n;
1068 JS_ASSERT(n != (uint32)-1);
1069 while (j != i) {
1070 --j;
1071 array->vector[j + n] = array->vector[j];
1074 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1075 if (cursor->index > i)
1076 cursor->index += n;
1078 return JS_TRUE;
1081 static void *
1082 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1084 uint32 length;
1085 void **vector, *elt;
1086 JSXMLArrayCursor *cursor;
1088 length = array->length;
1089 if (index >= length)
1090 return NULL;
1092 vector = array->vector;
1093 elt = vector[index];
1094 if (compress) {
1095 while (++index < length)
1096 vector[index-1] = vector[index];
1097 array->length = length - 1;
1098 array->capacity = JSXML_CAPACITY(array);
1099 } else {
1100 vector[index] = NULL;
1103 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1104 if (cursor->index > index)
1105 --cursor->index;
1107 return elt;
1110 static void
1111 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1113 void **vector;
1115 JS_ASSERT(!array->cursors);
1116 if (length >= array->length)
1117 return;
1119 if (length == 0) {
1120 if (array->vector)
1121 free(array->vector);
1122 vector = NULL;
1123 } else {
1124 vector = (void **) realloc(array->vector, length * sizeof(void *));
1125 if (!vector)
1126 return;
1129 if (array->length > length)
1130 array->length = length;
1131 array->capacity = length;
1132 array->vector = vector;
1135 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1136 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1137 XML_NOT_FOUND)
1138 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1139 ? (t *) (a)->vector[i] \
1140 : NULL)
1141 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1142 if ((a)->length <= (i)) \
1143 (a)->length = (i) + 1; \
1144 ((a)->vector[i] = (void *)(e)); \
1145 JS_END_MACRO
1146 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1147 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1148 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1149 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1150 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1153 * Define XML setting property strings and constants early, so everyone can
1154 * use the same names and their magic numbers (tinyids, flags).
1156 static const char js_ignoreComments_str[] = "ignoreComments";
1157 static const char js_ignoreProcessingInstructions_str[]
1158 = "ignoreProcessingInstructions";
1159 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1160 static const char js_prettyPrinting_str[] = "prettyPrinting";
1161 static const char js_prettyIndent_str[] = "prettyIndent";
1164 * NB: These XML static property tinyids must
1165 * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1166 * (b) index their corresponding xml_static_props array elements.
1167 * Don't change 'em!
1169 enum xml_static_tinyid {
1170 XML_IGNORE_COMMENTS,
1171 XML_IGNORE_PROCESSING_INSTRUCTIONS,
1172 XML_IGNORE_WHITESPACE,
1173 XML_PRETTY_PRINTING,
1174 XML_PRETTY_INDENT
1177 static JSBool
1178 xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1180 return JS_TRUE;
1183 static JSBool
1184 xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1186 uint8 flag;
1188 JS_ASSERT(JSVAL_IS_INT(id));
1190 flag = JS_BIT(JSVAL_TO_INT(id));
1191 if (js_ValueToBoolean(*vp))
1192 cx->xmlSettingFlags |= flag;
1193 else
1194 cx->xmlSettingFlags &= ~flag;
1195 return JS_TRUE;
1198 static JSPropertySpec xml_static_props[] = {
1199 {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT,
1200 xml_setting_getter, xml_setting_setter},
1201 {js_ignoreProcessingInstructions_str,
1202 XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
1203 xml_setting_getter, xml_setting_setter},
1204 {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
1205 xml_setting_getter, xml_setting_setter},
1206 {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT,
1207 xml_setting_getter, xml_setting_setter},
1208 {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT,
1209 xml_setting_getter, NULL},
1210 {0,0,0,0,0}
1213 /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1214 #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1215 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1216 JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1217 #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1218 #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1219 #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1222 * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1223 * This flag means a couple of things:
1225 * - The top JSXML created for a parse tree must have an object owning it.
1227 * - That the default namespace normally inherited from the temporary
1228 * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1229 * string must, in the case of a precompiled XML object tree, inherit via
1230 * ad-hoc code in ParseNodeToXML.
1232 * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1234 #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1236 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1237 #define IS_XML(str) \
1238 (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
1240 #define IS_XMLNS(str) \
1241 (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1243 #define IS_XML_CHARS(chars) \
1244 (JS_TOLOWER((chars)[0]) == 'x' && \
1245 JS_TOLOWER((chars)[1]) == 'm' && \
1246 JS_TOLOWER((chars)[2]) == 'l')
1248 #define HAS_NS_AFTER_XML(chars) \
1249 (JS_TOLOWER((chars)[3]) == 'n' && \
1250 JS_TOLOWER((chars)[4]) == 's')
1252 #define IS_XMLNS_CHARS(chars) \
1253 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1255 #define STARTS_WITH_XML(chars,length) \
1256 (length >= 3 && IS_XML_CHARS(chars))
1258 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1259 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1261 static JSObject *
1262 ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
1263 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1265 JSString *str, *uri, *prefix, *localName;
1266 size_t length, offset;
1267 const jschar *start, *limit, *colon;
1268 uint32 n;
1269 JSObject *ns;
1270 JSString *nsprefix;
1272 JS_ASSERT(pn->pn_arity == PN_NULLARY);
1273 str = ATOM_TO_STRING(pn->pn_atom);
1274 JSSTRING_CHARS_AND_LENGTH(str, start, length);
1275 JS_ASSERT(length != 0 && *start != '@');
1276 JS_ASSERT(length != 1 || *start != '*');
1278 uri = cx->runtime->emptyString;
1279 limit = start + length;
1280 colon = js_strchr_limit(start, ':', limit);
1281 if (colon) {
1282 offset = PTRDIFF(colon, start, jschar);
1283 prefix = js_NewDependentString(cx, str, 0, offset);
1284 if (!prefix)
1285 return NULL;
1287 if (STARTS_WITH_XML(start, offset)) {
1288 if (offset == 3) {
1289 uri = JS_InternString(cx, xml_namespace_str);
1290 if (!uri)
1291 return NULL;
1292 } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1293 uri = JS_InternString(cx, xmlns_namespace_str);
1294 if (!uri)
1295 return NULL;
1296 } else {
1297 uri = NULL;
1299 } else {
1300 uri = NULL;
1301 n = inScopeNSes->length;
1302 while (n != 0) {
1303 --n;
1304 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1305 nsprefix = GetPrefix(ns);
1306 if (nsprefix && js_EqualStrings(nsprefix, prefix)) {
1307 uri = GetURI(ns);
1308 break;
1313 if (!uri) {
1314 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
1315 JSREPORT_ERROR,
1316 JSMSG_BAD_XML_NAMESPACE,
1317 js_ValueToPrintableString(cx,
1318 STRING_TO_JSVAL(prefix)));
1319 return NULL;
1322 localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1));
1323 if (!localName)
1324 return NULL;
1325 } else {
1326 if (isAttributeName) {
1328 * An unprefixed attribute is not in any namespace, so set prefix
1329 * as well as uri to the empty string.
1331 prefix = uri;
1332 } else {
1334 * Loop from back to front looking for the closest declared default
1335 * namespace.
1337 n = inScopeNSes->length;
1338 while (n != 0) {
1339 --n;
1340 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1341 nsprefix = GetPrefix(ns);
1342 if (!nsprefix || IS_EMPTY(nsprefix)) {
1343 uri = GetURI(ns);
1344 break;
1347 prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
1349 localName = str;
1352 return NewXMLQName(cx, uri, prefix, localName);
1355 static JSString *
1356 ChompXMLWhitespace(JSContext *cx, JSString *str)
1358 size_t length, newlength, offset;
1359 const jschar *cp, *start, *end;
1360 jschar c;
1362 JSSTRING_CHARS_AND_LENGTH(str, start, length);
1363 for (cp = start, end = cp + length; cp < end; cp++) {
1364 c = *cp;
1365 if (!JS_ISXMLSPACE(c))
1366 break;
1368 while (end > cp) {
1369 c = end[-1];
1370 if (!JS_ISXMLSPACE(c))
1371 break;
1372 --end;
1374 newlength = PTRDIFF(end, cp, jschar);
1375 if (newlength == length)
1376 return str;
1377 offset = PTRDIFF(cp, start, jschar);
1378 return js_NewDependentString(cx, str, offset, newlength);
1381 static JSXML *
1382 ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
1383 JSXMLArray *inScopeNSes, uintN flags)
1385 JSXML *xml, *kid, *attr, *attrj;
1386 JSString *str;
1387 uint32 length, n, i, j;
1388 JSParseNode *pn2, *pn3, *head, **pnp;
1389 JSObject *ns;
1390 JSObject *qn, *attrjqn;
1391 JSXMLClass xml_class;
1392 int stackDummy;
1394 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1395 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1396 JSMSG_OVER_RECURSED);
1397 return NULL;
1400 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1403 * Cases return early to avoid common code that gets an outermost xml's
1404 * object, which protects GC-things owned by xml and its descendants from
1405 * garbage collection.
1407 xml = NULL;
1408 if (!js_EnterLocalRootScope(cx))
1409 return NULL;
1410 switch (pn->pn_type) {
1411 case TOK_XMLELEM:
1412 length = inScopeNSes->length;
1413 pn2 = pn->pn_head;
1414 xml = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1415 if (!xml)
1416 goto fail;
1418 flags &= ~XSF_PRECOMPILED_ROOT;
1419 n = pn->pn_count;
1420 JS_ASSERT(n >= 2);
1421 n -= 2;
1422 if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1423 goto fail;
1425 i = 0;
1426 while ((pn2 = pn2->pn_next) != NULL) {
1427 if (!pn2->pn_next) {
1428 /* Don't append the end tag! */
1429 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1430 break;
1433 if ((flags & XSF_IGNORE_WHITESPACE) &&
1434 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1435 --n;
1436 continue;
1439 kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1440 if (kid == PN2X_SKIP_CHILD) {
1441 --n;
1442 continue;
1445 if (!kid)
1446 goto fail;
1448 /* Store kid in xml right away, to protect it from GC. */
1449 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1450 kid->parent = xml;
1451 ++i;
1453 /* XXX where is this documented in an XML spec, or in E4X? */
1454 if ((flags & XSF_IGNORE_WHITESPACE) &&
1455 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1456 str = ChompXMLWhitespace(cx, kid->xml_value);
1457 if (!str)
1458 goto fail;
1459 kid->xml_value = str;
1463 JS_ASSERT(i == n);
1464 if (n < pn->pn_count - 2)
1465 XMLArrayTrim(&xml->xml_kids);
1466 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1467 break;
1469 case TOK_XMLLIST:
1470 xml = js_NewXML(cx, JSXML_CLASS_LIST);
1471 if (!xml)
1472 goto fail;
1474 n = pn->pn_count;
1475 if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1476 goto fail;
1478 i = 0;
1479 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1481 * Always ignore insignificant whitespace in lists -- we shouldn't
1482 * condition this on an XML.ignoreWhitespace setting when the list
1483 * constructor is XMLList (note XML/XMLList unification hazard).
1485 if (pn2->pn_type == TOK_XMLSPACE) {
1486 --n;
1487 continue;
1490 kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1491 if (kid == PN2X_SKIP_CHILD) {
1492 --n;
1493 continue;
1496 if (!kid)
1497 goto fail;
1499 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1500 ++i;
1503 if (n < pn->pn_count)
1504 XMLArrayTrim(&xml->xml_kids);
1505 break;
1507 case TOK_XMLSTAGO:
1508 case TOK_XMLPTAGC:
1509 length = inScopeNSes->length;
1510 pn2 = pn->pn_head;
1511 JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1512 if (pn2->pn_arity == PN_LIST)
1513 goto syntax;
1515 xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1516 if (!xml)
1517 goto fail;
1519 /* First pass: check syntax and process namespace declarations. */
1520 JS_ASSERT(pn->pn_count >= 1);
1521 n = pn->pn_count - 1;
1522 pnp = &pn2->pn_next;
1523 head = *pnp;
1524 while ((pn2 = *pnp) != NULL) {
1525 size_t length;
1526 const jschar *chars;
1528 if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1529 goto syntax;
1531 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1532 for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1533 if (pn3->pn_atom == pn2->pn_atom) {
1534 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
1535 JSREPORT_ERROR,
1536 JSMSG_DUPLICATE_XML_ATTR,
1537 js_ValueToPrintableString(cx,
1538 ATOM_KEY(pn2->pn_atom)));
1539 goto fail;
1543 str = ATOM_TO_STRING(pn2->pn_atom);
1544 pn2 = pn2->pn_next;
1545 JS_ASSERT(pn2);
1546 if (pn2->pn_type != TOK_XMLATTR)
1547 goto syntax;
1549 JSSTRING_CHARS_AND_LENGTH(str, chars, length);
1550 if (length >= 5 &&
1551 IS_XMLNS_CHARS(chars) &&
1552 (length == 5 || chars[5] == ':')) {
1553 JSString *uri, *prefix;
1555 uri = ATOM_TO_STRING(pn2->pn_atom);
1556 if (length == 5) {
1557 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1558 prefix = cx->runtime->emptyString;
1559 } else {
1560 prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1561 if (!prefix)
1562 goto fail;
1566 * Once the new ns is appended to xml->xml_namespaces, it is
1567 * protected from GC by the object that owns xml -- which is
1568 * either xml->object if outermost, or the object owning xml's
1569 * oldest ancestor if !outermost.
1571 ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1572 if (!ns)
1573 goto fail;
1576 * Don't add a namespace that's already in scope. If someone
1577 * extracts a child property from its parent via [[Get]], then
1578 * we enforce the invariant, noted many times in ECMA-357, that
1579 * the child's namespaces form a possibly-improper superset of
1580 * its ancestors' namespaces.
1582 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1583 if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1584 !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1585 goto fail;
1589 JS_ASSERT(n >= 2);
1590 n -= 2;
1591 *pnp = pn2->pn_next;
1592 /* XXXbe recycle pn2 */
1593 continue;
1596 pnp = &pn2->pn_next;
1600 * If called from js_ParseNodeToXMLObject, emulate the effect of the
1601 * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1602 * the String Type" (ECMA-357 10.3.1).
1604 if (flags & XSF_PRECOMPILED_ROOT) {
1605 JS_ASSERT(length >= 1);
1606 ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSObject);
1607 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
1608 namespace_identity));
1609 ns = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_FALSE);
1610 if (!ns)
1611 goto fail;
1612 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1613 goto fail;
1615 XMLArrayTrim(&xml->xml_namespaces);
1617 /* Second pass: process tag name and attributes, using namespaces. */
1618 pn2 = pn->pn_head;
1619 qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_FALSE);
1620 if (!qn)
1621 goto fail;
1622 xml->name = qn;
1624 JS_ASSERT((n & 1) == 0);
1625 n >>= 1;
1626 if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
1627 goto fail;
1629 for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1630 qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_TRUE);
1631 if (!qn) {
1632 xml->xml_attrs.length = i;
1633 goto fail;
1637 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1638 * this time checking local name and namespace URI.
1640 for (j = 0; j < i; j++) {
1641 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1642 attrjqn = attrj->name;
1643 if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
1644 js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
1645 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
1646 JSREPORT_ERROR,
1647 JSMSG_DUPLICATE_XML_ATTR,
1648 js_ValueToPrintableString(cx,
1649 ATOM_KEY(pn2->pn_atom)));
1650 goto fail;
1654 pn2 = pn2->pn_next;
1655 JS_ASSERT(pn2);
1656 JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1658 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1659 if (!attr)
1660 goto fail;
1662 XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1663 attr->parent = xml;
1664 attr->name = qn;
1665 attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1668 /* Point tag closes its own namespace scope. */
1669 if (pn->pn_type == TOK_XMLPTAGC)
1670 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1671 break;
1673 case TOK_XMLSPACE:
1674 case TOK_XMLTEXT:
1675 case TOK_XMLCDATA:
1676 case TOK_XMLCOMMENT:
1677 case TOK_XMLPI:
1678 str = ATOM_TO_STRING(pn->pn_atom);
1679 qn = NULL;
1680 if (pn->pn_type == TOK_XMLCOMMENT) {
1681 if (flags & XSF_IGNORE_COMMENTS)
1682 goto skip_child;
1683 xml_class = JSXML_CLASS_COMMENT;
1684 } else if (pn->pn_type == TOK_XMLPI) {
1685 if (IS_XML(str)) {
1686 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
1687 JSREPORT_ERROR,
1688 JSMSG_RESERVED_ID,
1689 js_ValueToPrintableString(cx,
1690 STRING_TO_JSVAL(str)));
1691 goto fail;
1694 if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1695 goto skip_child;
1697 qn = ParseNodeToQName(cx, pc, pn, inScopeNSes, JS_FALSE);
1698 if (!qn)
1699 goto fail;
1701 str = pn->pn_atom2
1702 ? ATOM_TO_STRING(pn->pn_atom2)
1703 : cx->runtime->emptyString;
1704 xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1705 } else {
1706 /* CDATA section content, or element text. */
1707 xml_class = JSXML_CLASS_TEXT;
1710 xml = js_NewXML(cx, xml_class);
1711 if (!xml)
1712 goto fail;
1713 xml->name = qn;
1714 if (pn->pn_type == TOK_XMLSPACE)
1715 xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1716 xml->xml_value = str;
1717 break;
1719 default:
1720 goto syntax;
1723 js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
1724 if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
1725 return NULL;
1726 return xml;
1728 skip_child:
1729 js_LeaveLocalRootScope(cx);
1730 return PN2X_SKIP_CHILD;
1732 #undef PN2X_SKIP_CHILD
1734 syntax:
1735 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1736 JSMSG_BAD_XML_MARKUP);
1737 fail:
1738 js_LeaveLocalRootScope(cx);
1739 return NULL;
1743 * XML helper, object-ops, and library functions. We start with the helpers,
1744 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1746 static JSBool
1747 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1749 jsval v;
1751 if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
1752 return JS_FALSE;
1753 if (!VALUE_IS_FUNCTION(cx, v)) {
1754 *vp = JSVAL_VOID;
1755 return JS_TRUE;
1757 return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1760 static JSBool
1761 FillSettingsCache(JSContext *cx)
1763 int i;
1764 const char *name;
1765 jsval v;
1767 /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1768 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
1769 name = xml_static_props[i].name;
1770 if (!GetXMLSetting(cx, name, &v))
1771 return JS_FALSE;
1772 if (js_ValueToBoolean(v))
1773 cx->xmlSettingFlags |= JS_BIT(i);
1774 else
1775 cx->xmlSettingFlags &= ~JS_BIT(i);
1778 cx->xmlSettingFlags |= XSF_CACHE_VALID;
1779 return JS_TRUE;
1782 static JSBool
1783 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1785 int i;
1787 if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
1788 return JS_FALSE;
1790 for (i = 0; xml_static_props[i].name; i++) {
1791 if (!strcmp(xml_static_props[i].name, name)) {
1792 *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
1793 return JS_TRUE;
1796 *bp = JS_FALSE;
1797 return JS_TRUE;
1800 static JSBool
1801 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1803 jsval v;
1805 return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1808 static JSBool
1809 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1811 JSBool flag;
1813 /* Just get the first flag to validate the setting flags cache. */
1814 if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
1815 return JS_FALSE;
1816 *flagsp = cx->xmlSettingFlags;
1817 return JS_TRUE;
1820 static JSXML *
1821 ParseXMLSource(JSContext *cx, JSString *src)
1823 jsval nsval;
1824 JSString *uri;
1825 size_t urilen, srclen, length, offset, dstlen;
1826 jschar *chars;
1827 const jschar *srcp, *endp;
1828 JSXML *xml;
1829 JSParseContext pc;
1830 const char *filename;
1831 uintN lineno;
1832 JSStackFrame *fp;
1833 JSOp op;
1834 JSParseNode *pn;
1835 JSXMLArray nsarray;
1836 uintN flags;
1838 static const char prefix[] = "<parent xmlns=\"";
1839 static const char middle[] = "\">";
1840 static const char suffix[] = "</parent>";
1842 #define constrlen(constr) (sizeof(constr) - 1)
1844 if (!js_GetDefaultXMLNamespace(cx, &nsval))
1845 return NULL;
1846 uri = GetURI(JSVAL_TO_OBJECT(nsval));
1847 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1849 urilen = JSSTRING_LENGTH(uri);
1850 srclen = JSSTRING_LENGTH(src);
1851 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1852 constrlen(suffix);
1854 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1855 if (!chars)
1856 return NULL;
1858 dstlen = length;
1859 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1860 offset = dstlen;
1861 js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
1862 offset += urilen;
1863 dstlen = length - offset + 1;
1864 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1865 &dstlen);
1866 offset += dstlen;
1867 srcp = JSSTRING_CHARS(src);
1868 js_strncpy(chars + offset, srcp, srclen);
1869 offset += srclen;
1870 dstlen = length - offset + 1;
1871 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1872 &dstlen);
1873 chars [offset + dstlen] = 0;
1875 xml = NULL;
1876 for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down)
1877 JS_ASSERT(!fp->script);
1878 filename = NULL;
1879 lineno = 1;
1880 if (fp) {
1881 op = (JSOp) *fp->regs->pc;
1882 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1883 filename = fp->script->filename;
1884 lineno = js_FramePCToLineNumber(cx, fp);
1885 for (endp = srcp + srclen; srcp < endp; srcp++) {
1886 if (*srcp == '\n')
1887 --lineno;
1892 if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
1893 filename, lineno))
1894 goto out;
1895 pn = js_ParseXMLText(cx, js_GetTopStackFrame(cx)->scopeChain, &pc,
1896 JS_FALSE);
1897 if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1898 if (GetXMLSettingFlags(cx, &flags))
1899 xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
1901 XMLArrayFinish(cx, &nsarray);
1903 js_FinishParseContext(cx, &pc);
1905 out:
1906 JS_free(cx, chars);
1907 return xml;
1909 #undef constrlen
1913 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1915 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1916 * the constraint:
1918 * for all x belonging to XML:
1919 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1921 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1922 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1924 * Same goes for 10.4.1 Step 7(a).
1926 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1927 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1928 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1929 * undeclared namespaces associated with x not belonging to ancestorNS.
1931 static JSXML *
1932 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1934 JSObject *ns;
1936 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1937 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1938 if (!ns || !xml)
1939 return xml;
1940 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1941 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1942 return NULL;
1943 ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1945 xml->parent = NULL;
1946 return xml;
1949 static JSObject *
1950 ToXML(JSContext *cx, jsval v)
1952 JSObject *obj;
1953 JSXML *xml;
1954 JSClass *clasp;
1955 JSString *str;
1956 uint32 length;
1958 if (JSVAL_IS_PRIMITIVE(v)) {
1959 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1960 goto bad;
1961 } else {
1962 obj = JSVAL_TO_OBJECT(v);
1963 if (OBJECT_IS_XML(cx, obj)) {
1964 xml = (JSXML *) JS_GetPrivate(cx, obj);
1965 if (xml->xml_class == JSXML_CLASS_LIST) {
1966 if (xml->xml_kids.length != 1)
1967 goto bad;
1968 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1969 if (xml) {
1970 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1971 return js_GetXMLObject(cx, xml);
1974 return obj;
1977 clasp = OBJ_GET_CLASS(cx, obj);
1978 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1979 JS_ASSERT(0);
1982 if (clasp != &js_StringClass &&
1983 clasp != &js_NumberClass &&
1984 clasp != &js_BooleanClass) {
1985 goto bad;
1989 str = js_ValueToString(cx, v);
1990 if (!str)
1991 return NULL;
1992 if (IS_EMPTY(str)) {
1993 length = 0;
1994 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1995 xml = NULL;
1996 #endif
1997 } else {
1998 xml = ParseXMLSource(cx, str);
1999 if (!xml)
2000 return NULL;
2001 length = JSXML_LENGTH(xml);
2004 if (length == 0) {
2005 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2006 if (!obj)
2007 return NULL;
2008 } else if (length == 1) {
2009 xml = OrphanXMLChild(cx, xml, 0);
2010 if (!xml)
2011 return NULL;
2012 obj = js_GetXMLObject(cx, xml);
2013 if (!obj)
2014 return NULL;
2015 } else {
2016 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2017 return NULL;
2019 return obj;
2021 bad:
2022 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2023 JSDVG_IGNORE_STACK, v, NULL);
2024 return NULL;
2027 static JSBool
2028 Append(JSContext *cx, JSXML *list, JSXML *kid);
2030 static JSObject *
2031 ToXMLList(JSContext *cx, jsval v)
2033 JSObject *obj, *listobj;
2034 JSXML *xml, *list, *kid;
2035 JSClass *clasp;
2036 JSString *str;
2037 uint32 i, length;
2039 if (JSVAL_IS_PRIMITIVE(v)) {
2040 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2041 goto bad;
2042 } else {
2043 obj = JSVAL_TO_OBJECT(v);
2044 if (OBJECT_IS_XML(cx, obj)) {
2045 xml = (JSXML *) JS_GetPrivate(cx, obj);
2046 if (xml->xml_class != JSXML_CLASS_LIST) {
2047 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2048 if (!listobj)
2049 return NULL;
2050 list = (JSXML *) JS_GetPrivate(cx, listobj);
2051 if (!Append(cx, list, xml))
2052 return NULL;
2053 return listobj;
2055 return obj;
2058 clasp = OBJ_GET_CLASS(cx, obj);
2059 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2060 JS_ASSERT(0);
2063 if (clasp != &js_StringClass &&
2064 clasp != &js_NumberClass &&
2065 clasp != &js_BooleanClass) {
2066 goto bad;
2070 str = js_ValueToString(cx, v);
2071 if (!str)
2072 return NULL;
2073 if (IS_EMPTY(str)) {
2074 xml = NULL;
2075 length = 0;
2076 } else {
2077 if (!js_EnterLocalRootScope(cx))
2078 return NULL;
2079 xml = ParseXMLSource(cx, str);
2080 if (!xml) {
2081 js_LeaveLocalRootScope(cx);
2082 return NULL;
2084 length = JSXML_LENGTH(xml);
2087 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2088 if (listobj) {
2089 list = (JSXML *) JS_GetPrivate(cx, listobj);
2090 for (i = 0; i < length; i++) {
2091 kid = OrphanXMLChild(cx, xml, i);
2092 if (!kid || !Append(cx, list, kid)) {
2093 listobj = NULL;
2094 break;
2099 if (xml)
2100 js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2101 return listobj;
2103 bad:
2104 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2105 JSDVG_IGNORE_STACK, v, NULL);
2106 return NULL;
2110 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2111 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2112 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2113 * MakeXMLSpecialString subroutine.
2115 * These functions take ownership of sb->base, if sb is non-null, in all cases
2116 * of success or failure.
2118 static JSString *
2119 MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2120 JSString *str, JSString *str2,
2121 const jschar *prefix, size_t prefixlength,
2122 const jschar *suffix, size_t suffixlength)
2124 JSStringBuffer localSB;
2125 size_t length, length2, newlength;
2126 jschar *bp, *base;
2128 if (!sb) {
2129 sb = &localSB;
2130 js_InitStringBuffer(sb);
2133 length = JSSTRING_LENGTH(str);
2134 length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2135 newlength = STRING_BUFFER_OFFSET(sb) +
2136 prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2137 suffixlength;
2138 bp = base = (jschar *)
2139 JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2140 if (!bp) {
2141 js_FinishStringBuffer(sb);
2142 return NULL;
2145 bp += STRING_BUFFER_OFFSET(sb);
2146 js_strncpy(bp, prefix, prefixlength);
2147 bp += prefixlength;
2148 js_strncpy(bp, JSSTRING_CHARS(str), length);
2149 bp += length;
2150 if (length2 != 0) {
2151 *bp++ = (jschar) ' ';
2152 js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2153 bp += length2;
2155 js_strncpy(bp, suffix, suffixlength);
2156 bp[suffixlength] = 0;
2158 str = js_NewString(cx, base, newlength);
2159 if (!str)
2160 free(base);
2161 return str;
2164 static JSString *
2165 MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2167 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2168 'C', 'D', 'A', 'T', 'A',
2169 '['};
2170 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2172 return MakeXMLSpecialString(cx, sb, str, NULL,
2173 cdata_prefix_ucNstr, 9,
2174 cdata_suffix_ucNstr, 3);
2177 static JSString *
2178 MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2180 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2181 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2183 return MakeXMLSpecialString(cx, sb, str, NULL,
2184 comment_prefix_ucNstr, 4,
2185 comment_suffix_ucNstr, 3);
2188 static JSString *
2189 MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2190 JSString *value)
2192 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2193 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2195 return MakeXMLSpecialString(cx, sb, name, value,
2196 pi_prefix_ucNstr, 2,
2197 pi_suffix_ucNstr, 2);
2201 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2202 * equals, a double quote, an attribute value, and a closing double quote.
2204 static void
2205 AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2207 js_AppendChar(sb, '=');
2208 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2209 if (!valstr) {
2210 if (STRING_BUFFER_OK(sb)) {
2211 free(sb->base);
2212 sb->base = STRING_BUFFER_ERROR_BASE;
2214 return;
2216 js_AppendJSString(sb, valstr);
2220 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2222 * This function takes ownership of sb->base, if sb is non-null, in all cases
2223 * of success or failure.
2225 static JSString *
2226 EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2228 size_t length, newlength;
2229 const jschar *cp, *start, *end;
2230 jschar c;
2232 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2233 newlength = length;
2234 for (cp = start, end = cp + length; cp < end; cp++) {
2235 c = *cp;
2236 if (c == '<' || c == '>')
2237 newlength += 3;
2238 else if (c == '&')
2239 newlength += 4;
2241 if (newlength < length) {
2242 js_ReportAllocationOverflow(cx);
2243 return NULL;
2246 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2247 JSStringBuffer localSB;
2248 if (!sb) {
2249 sb = &localSB;
2250 js_InitStringBuffer(sb);
2252 if (!sb->grow(sb, newlength)) {
2253 JS_ReportOutOfMemory(cx);
2254 return NULL;
2256 for (cp = start; cp < end; cp++) {
2257 c = *cp;
2258 if (c == '<')
2259 js_AppendCString(sb, js_lt_entity_str);
2260 else if (c == '>')
2261 js_AppendCString(sb, js_gt_entity_str);
2262 else if (c == '&')
2263 js_AppendCString(sb, js_amp_entity_str);
2264 else
2265 js_AppendChar(sb, c);
2267 JS_ASSERT(STRING_BUFFER_OK(sb));
2268 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2269 if (!str)
2270 js_FinishStringBuffer(sb);
2272 return str;
2276 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2277 * This function takes ownership of sb->base, if sb is non-null, in all cases.
2279 static JSString *
2280 EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str,
2281 JSBool quote)
2283 size_t length, newlength;
2284 const jschar *cp, *start, *end;
2285 jschar c;
2287 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2288 newlength = length + (quote ? 2 : 0);
2289 for (cp = start, end = cp + length; cp < end; cp++) {
2290 c = *cp;
2291 if (c == '"')
2292 newlength += 5;
2293 else if (c == '<')
2294 newlength += 3;
2295 else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2296 newlength += 4;
2298 if (newlength < length) {
2299 js_ReportAllocationOverflow(cx);
2300 return NULL;
2303 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2304 JSStringBuffer localSB;
2305 if (!sb) {
2306 sb = &localSB;
2307 js_InitStringBuffer(sb);
2309 if (!sb->grow(sb, newlength)) {
2310 JS_ReportOutOfMemory(cx);
2311 return NULL;
2313 if (quote)
2314 js_AppendChar(sb, '"');
2315 for (cp = start; cp < end; cp++) {
2316 c = *cp;
2317 if (c == '"')
2318 js_AppendCString(sb, js_quot_entity_str);
2319 else if (c == '<')
2320 js_AppendCString(sb, js_lt_entity_str);
2321 else if (c == '&')
2322 js_AppendCString(sb, js_amp_entity_str);
2323 else if (c == '\n')
2324 js_AppendCString(sb, "&#xA;");
2325 else if (c == '\r')
2326 js_AppendCString(sb, "&#xD;");
2327 else if (c == '\t')
2328 js_AppendCString(sb, "&#x9;");
2329 else
2330 js_AppendChar(sb, c);
2332 if (quote)
2333 js_AppendChar(sb, '"');
2334 JS_ASSERT(STRING_BUFFER_OK(sb));
2335 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2336 if (!str)
2337 js_FinishStringBuffer(sb);
2339 return str;
2342 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2343 static JSObject *
2344 GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2346 JSString *uri, *prefix, *nsprefix;
2347 JSObject *match, *ns;
2348 uint32 i, n;
2349 jsval argv[2];
2351 uri = GetURI(qn);
2352 prefix = GetPrefix(qn);
2353 JS_ASSERT(uri);
2354 if (!uri) {
2355 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2356 JSMSG_BAD_XML_NAMESPACE,
2357 prefix
2358 ? js_ValueToPrintableString(cx,
2359 STRING_TO_JSVAL(prefix))
2360 : js_undefined_str);
2361 return NULL;
2364 /* Look for a matching namespace in inScopeNSes, if provided. */
2365 match = NULL;
2366 if (inScopeNSes) {
2367 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2368 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2369 if (!ns)
2370 continue;
2373 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2374 * If we preserve prefixes, we must match null prefix against
2375 * an empty prefix of ns, in order to avoid generating redundant
2376 * prefixed and default namespaces for cases such as:
2378 * x = <t xmlns="http://foo.com"/>
2379 * print(x.toXMLString());
2381 * Per 10.3.2.1, the namespace attribute in t has an empty string
2382 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2384 * 1. If the [local name] property of a is "xmlns"
2385 * a. Map ns.prefix to the empty string
2387 * But t's name has a null prefix in this implementation, meaning
2388 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2389 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2390 * saying how "no value" maps to an ECMA-357 value -- but it must
2391 * map to the *undefined* prefix value).
2393 * Since "" != undefined (or null, in the current implementation)
2394 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2395 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2396 * This spec bug leads to ToXMLString results that duplicate the
2397 * declared namespace.
2399 if (js_EqualStrings(GetURI(ns), uri)) {
2400 nsprefix = GetPrefix(ns);
2401 if (nsprefix == prefix ||
2402 ((nsprefix && prefix)
2403 ? js_EqualStrings(nsprefix, prefix)
2404 : IS_EMPTY(nsprefix ? nsprefix : prefix))) {
2405 match = ns;
2406 break;
2412 /* If we didn't match, make a new namespace from qn. */
2413 if (!match) {
2414 argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2415 argv[1] = STRING_TO_JSVAL(uri);
2416 ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2417 2, argv);
2418 if (!ns)
2419 return NULL;
2420 match = ns;
2422 return match;
2425 static JSString *
2426 GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2428 const jschar *cp, *start, *end;
2429 size_t length, newlength, offset;
2430 uint32 i, n, m, serial;
2431 jschar *bp, *dp;
2432 JSBool done;
2433 JSObject *ns;
2434 JSString *nsprefix, *prefix;
2436 JS_ASSERT(!IS_EMPTY(uri));
2439 * If there are no *declared* namespaces, skip all collision detection and
2440 * return a short prefix quickly; an example of such a situation:
2442 * var x = <f/>;
2443 * var n = new Namespace("http://example.com/");
2444 * x.@n::att = "val";
2445 * x.toXMLString();
2447 * This is necessary for various log10 uses below to be valid.
2449 if (decls->length == 0)
2450 return JS_NewStringCopyZ(cx, "a");
2453 * Try peeling off the last filename suffix or pathname component till
2454 * we have a valid XML name. This heuristic will prefer "xul" given
2455 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2456 * likely URI of the form ".../xbl2/2005".
2458 JSSTRING_CHARS_AND_END(uri, start, end);
2459 cp = end;
2460 while (--cp > start) {
2461 if (*cp == '.' || *cp == '/' || *cp == ':') {
2462 ++cp;
2463 length = PTRDIFF(end, cp, jschar);
2464 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2465 break;
2466 end = --cp;
2469 length = PTRDIFF(end, cp, jschar);
2472 * If the namespace consisted only of non-XML names or names that begin
2473 * case-insensitively with "xml", arbitrarily create a prefix consisting
2474 * of 'a's of size length (allowing dp-calculating code to work with or
2475 * without this branch executing) plus the space for storing a hyphen and
2476 * the serial number (avoiding reallocation if a collision happens).
2478 bp = (jschar *) cp;
2479 newlength = length;
2480 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2481 newlength = length + 2 + (size_t) log10((double) decls->length);
2482 bp = (jschar *)
2483 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2484 if (!bp)
2485 return NULL;
2487 bp[newlength] = 0;
2488 for (i = 0; i < newlength; i++)
2489 bp[i] = 'a';
2493 * Now search through decls looking for a collision. If we collide with
2494 * an existing prefix, start tacking on a hyphen and a serial number.
2496 serial = 0;
2497 do {
2498 done = JS_TRUE;
2499 for (i = 0, n = decls->length; i < n; i++) {
2500 ns = XMLARRAY_MEMBER(decls, i, JSObject);
2501 if (ns && (nsprefix = GetPrefix(ns)) &&
2502 JSSTRING_LENGTH(nsprefix) == newlength &&
2503 !memcmp(JSSTRING_CHARS(nsprefix), bp,
2504 newlength * sizeof(jschar))) {
2505 if (bp == cp) {
2506 newlength = length + 2 + (size_t) log10((double) n);
2507 bp = (jschar *)
2508 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2509 if (!bp)
2510 return NULL;
2511 js_strncpy(bp, cp, length);
2514 ++serial;
2515 JS_ASSERT(serial <= n);
2516 dp = bp + length + 2 + (size_t) log10((double) serial);
2517 *dp = 0;
2518 for (m = serial; m != 0; m /= 10)
2519 *--dp = (jschar)('0' + m % 10);
2520 *--dp = '-';
2521 JS_ASSERT(dp == bp + length);
2523 done = JS_FALSE;
2524 break;
2527 } while (!done);
2529 if (bp == cp) {
2530 offset = PTRDIFF(cp, start, jschar);
2531 prefix = js_NewDependentString(cx, uri, offset, length);
2532 } else {
2533 prefix = js_NewString(cx, bp, newlength);
2534 if (!prefix)
2535 JS_free(cx, bp);
2537 return prefix;
2540 static JSBool
2541 namespace_match(const void *a, const void *b)
2543 const JSObject *nsa = (const JSObject *) a;
2544 const JSObject *nsb = (const JSObject *) b;
2545 JSString *prefixa, *prefixb = GetPrefix(nsb);
2547 if (prefixb) {
2548 prefixa = GetPrefix(nsa);
2549 return prefixa && js_EqualStrings(prefixa, prefixb);
2551 return js_EqualStrings(GetURI(nsa), GetURI(nsb));
2554 /* ECMA-357 10.2.1 and 10.2.2 */
2555 #define TO_SOURCE_FLAG 0x80000000
2557 static JSString *
2558 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2559 uint32 indentLevel)
2561 JSBool pretty, indentKids;
2562 JSStringBuffer sb;
2563 JSString *str, *prefix, *kidstr, *nsuri;
2564 JSXMLArrayCursor cursor;
2565 uint32 i, n, nextIndentLevel;
2566 JSXMLArray empty, decls, ancdecls;
2567 JSObject *ns, *ns2;
2568 JSXML *attr, *kid;
2570 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2571 return NULL;
2573 js_InitStringBuffer(&sb);
2574 if (pretty) {
2575 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2577 if (!STRING_BUFFER_OK(&sb)) {
2578 JS_ReportOutOfMemory(cx);
2579 return NULL;
2582 str = NULL;
2584 switch (xml->xml_class) {
2585 case JSXML_CLASS_TEXT:
2586 /* Step 4. */
2587 if (pretty) {
2588 str = ChompXMLWhitespace(cx, xml->xml_value);
2589 if (!str)
2590 return NULL;
2591 } else {
2592 str = xml->xml_value;
2594 return EscapeElementValue(cx, &sb, str);
2596 case JSXML_CLASS_ATTRIBUTE:
2597 /* Step 5. */
2598 return EscapeAttributeValue(cx, &sb, xml->xml_value,
2599 (indentLevel & TO_SOURCE_FLAG) != 0);
2601 case JSXML_CLASS_COMMENT:
2602 /* Step 6. */
2603 return MakeXMLCommentString(cx, &sb, xml->xml_value);
2605 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2606 /* Step 7. */
2607 return MakeXMLPIString(cx, &sb, GetLocalName(xml->name),
2608 xml->xml_value);
2610 case JSXML_CLASS_LIST:
2611 /* ECMA-357 10.2.2. */
2612 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2613 i = 0;
2614 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2615 if (pretty && i != 0)
2616 js_AppendChar(&sb, '\n');
2618 kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2619 if (!kidstr)
2620 break;
2622 js_AppendJSString(&sb, kidstr);
2623 ++i;
2625 XMLArrayCursorFinish(&cursor);
2626 if (kid)
2627 goto list_out;
2629 if (!sb.base)
2630 return cx->runtime->emptyString;
2632 if (!STRING_BUFFER_OK(&sb)) {
2633 JS_ReportOutOfMemory(cx);
2634 return NULL;
2637 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2638 list_out:
2639 if (!str && STRING_BUFFER_OK(&sb))
2640 js_FinishStringBuffer(&sb);
2641 return str;
2643 default:;
2646 /* After this point, control must flow through label out: to exit. */
2647 if (!js_EnterLocalRootScope(cx))
2648 return NULL;
2650 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2651 if (!ancestorNSes) {
2652 XMLArrayInit(cx, &empty, 0);
2653 ancestorNSes = &empty;
2655 XMLArrayInit(cx, &decls, 0);
2656 ancdecls.capacity = 0;
2658 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2659 XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
2660 while ((ns = (JSObject *) XMLArrayCursorNext(&cursor)) != NULL) {
2661 if (!IsDeclared(ns))
2662 continue;
2663 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2664 /* NOTE: may want to exclude unused namespaces here. */
2665 ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_TRUE);
2666 if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2))
2667 break;
2670 XMLArrayCursorFinish(&cursor);
2671 if (ns)
2672 goto out;
2675 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2676 * not own its member references. In the spec, ancdecls has no name, but
2677 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2679 if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
2680 goto out;
2681 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2682 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2683 if (!ns2)
2684 continue;
2685 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
2686 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2687 goto out;
2689 for (i = 0, n = decls.length; i < n; i++) {
2690 ns2 = XMLARRAY_MEMBER(&decls, i, JSObject);
2691 if (!ns2)
2692 continue;
2693 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
2694 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2695 goto out;
2698 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2699 ns = GetNamespace(cx, xml->name, &ancdecls);
2700 if (!ns)
2701 goto out;
2703 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2704 prefix = GetPrefix(ns);
2705 if (!prefix) {
2707 * Create a namespace prefix that isn't used by any member of decls.
2708 * Assign the new prefix to a copy of ns. Flag this namespace as if
2709 * it were declared, for assertion-testing's sake later below.
2711 * Erratum: if prefix and xml->name are both null (*undefined* in
2712 * ECMA-357), we know that xml was named using the default namespace
2713 * (proof: see GetNamespace and the Namespace constructor called with
2714 * two arguments). So we ought not generate a new prefix here, when
2715 * we can declare ns as the default namespace for xml.
2717 * This helps descendants inherit the namespace instead of redundantly
2718 * redeclaring it with generated prefixes in each descendant.
2720 nsuri = GetURI(ns);
2721 if (!GetPrefix(xml->name)) {
2722 prefix = cx->runtime->emptyString;
2723 } else {
2724 prefix = GeneratePrefix(cx, nsuri, &ancdecls);
2725 if (!prefix)
2726 goto out;
2728 ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2729 if (!ns)
2730 goto out;
2733 * If the xml->name was unprefixed, we must remove any declared default
2734 * namespace from decls before appending ns. How can you get a default
2735 * namespace in decls that doesn't match the one from name? Apparently
2736 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2737 * to fix this is to update x's in-scope namespaces when setNamespace
2738 * is called, but that's not specified by ECMA-357.
2740 * Likely Erratum here, depending on whether the lack of update to x's
2741 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2742 * erratum or not. Note that changing setNamespace to update the list
2743 * of in-scope namespaces will change x.namespaceDeclarations().
2745 if (IS_EMPTY(prefix)) {
2746 i = XMLArrayFindMember(&decls, ns, namespace_match);
2747 if (i != XML_NOT_FOUND)
2748 XMLArrayDelete(cx, &decls, i, JS_TRUE);
2752 * In the spec, ancdecls has no name, but is always written out as
2753 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2754 * that union in ancdecls, any time we append a namespace strong
2755 * ref to decls, we must also append a weak ref to ancdecls. Order
2756 * matters here: code at label out: releases strong refs in decls.
2758 if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
2759 !XMLARRAY_APPEND(cx, &decls, ns)) {
2760 goto out;
2764 /* Format the element or point-tag into sb. */
2765 js_AppendChar(&sb, '<');
2767 if (prefix && !IS_EMPTY(prefix)) {
2768 js_AppendJSString(&sb, prefix);
2769 js_AppendChar(&sb, ':');
2771 js_AppendJSString(&sb, GetLocalName(xml->name));
2774 * Step 16 makes a union to avoid writing two loops in step 17, to share
2775 * common attribute value appending spec-code. We prefer two loops for
2776 * faster code and less data overhead.
2779 /* Step 17(b): append attributes. */
2780 XMLArrayCursorInit(&cursor, &xml->xml_attrs);
2781 while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2782 js_AppendChar(&sb, ' ');
2783 ns2 = GetNamespace(cx, attr->name, &ancdecls);
2784 if (!ns2)
2785 break;
2787 /* 17(b)(ii): NULL means *undefined* here. */
2788 prefix = GetPrefix(ns2);
2789 if (!prefix) {
2790 prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls);
2791 if (!prefix)
2792 break;
2794 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2795 ns2 = NewXMLNamespace(cx, prefix, GetURI(ns2), JS_TRUE);
2796 if (!ns2)
2797 break;
2800 * In the spec, ancdecls has no name, but is always written out as
2801 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2802 * that union in ancdecls, any time we append a namespace strong
2803 * ref to decls, we must also append a weak ref to ancdecls. Order
2804 * matters here: code at label out: releases strong refs in decls.
2806 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
2807 !XMLARRAY_APPEND(cx, &decls, ns2)) {
2808 break;
2812 /* 17(b)(iii). */
2813 if (!IS_EMPTY(prefix)) {
2814 js_AppendJSString(&sb, prefix);
2815 js_AppendChar(&sb, ':');
2818 /* 17(b)(iv). */
2819 js_AppendJSString(&sb, GetLocalName(attr->name));
2821 /* 17(d-g). */
2822 AppendAttributeValue(cx, &sb, attr->xml_value);
2824 XMLArrayCursorFinish(&cursor);
2825 if (attr)
2826 goto out;
2828 /* Step 17(c): append XML namespace declarations. */
2829 XMLArrayCursorInit(&cursor, &decls);
2830 while ((ns2 = (JSObject *) XMLArrayCursorNext(&cursor)) != NULL) {
2831 JS_ASSERT(IsDeclared(ns2));
2833 js_AppendCString(&sb, " xmlns");
2835 /* 17(c)(ii): NULL means *undefined* here. */
2836 prefix = GetPrefix(ns2);
2837 if (!prefix) {
2838 prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls);
2839 if (!prefix)
2840 break;
2841 ns2->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
2844 /* 17(c)(iii). */
2845 if (!IS_EMPTY(prefix)) {
2846 js_AppendChar(&sb, ':');
2847 js_AppendJSString(&sb, prefix);
2850 /* 17(d-g). */
2851 AppendAttributeValue(cx, &sb, GetURI(ns2));
2853 XMLArrayCursorFinish(&cursor);
2854 if (ns2)
2855 goto out;
2857 /* Step 18: handle point tags. */
2858 n = xml->xml_kids.length;
2859 if (n == 0) {
2860 js_AppendCString(&sb, "/>");
2861 } else {
2862 /* Steps 19 through 25: handle element content, and open the end-tag. */
2863 js_AppendChar(&sb, '>');
2864 indentKids = n > 1 ||
2865 (n == 1 &&
2866 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2867 kid->xml_class != JSXML_CLASS_TEXT);
2869 if (pretty && indentKids) {
2870 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2871 goto out;
2872 nextIndentLevel = indentLevel + i;
2873 } else {
2874 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2877 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2878 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2879 if (pretty && indentKids)
2880 js_AppendChar(&sb, '\n');
2882 kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
2883 if (!kidstr)
2884 break;
2886 js_AppendJSString(&sb, kidstr);
2888 XMLArrayCursorFinish(&cursor);
2889 if (kid)
2890 goto out;
2892 if (pretty && indentKids) {
2893 js_AppendChar(&sb, '\n');
2894 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2896 js_AppendCString(&sb, "</");
2898 /* Step 26. */
2899 prefix = GetPrefix(ns);
2900 if (prefix && !IS_EMPTY(prefix)) {
2901 js_AppendJSString(&sb, prefix);
2902 js_AppendChar(&sb, ':');
2905 /* Step 27. */
2906 js_AppendJSString(&sb, GetLocalName(xml->name));
2907 js_AppendChar(&sb, '>');
2910 if (!STRING_BUFFER_OK(&sb)) {
2911 JS_ReportOutOfMemory(cx);
2912 goto out;
2915 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2916 out:
2917 js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
2918 if (!str && STRING_BUFFER_OK(&sb))
2919 js_FinishStringBuffer(&sb);
2920 XMLArrayFinish(cx, &decls);
2921 if (ancdecls.capacity != 0)
2922 XMLArrayFinish(cx, &ancdecls);
2923 return str;
2926 /* ECMA-357 10.2 */
2927 static JSString *
2928 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
2930 JSObject *obj;
2931 JSString *str;
2932 JSXML *xml;
2934 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2935 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2936 JSMSG_BAD_XML_CONVERSION,
2937 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2938 return NULL;
2941 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2942 return js_ValueToString(cx, v);
2944 if (JSVAL_IS_STRING(v))
2945 return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
2947 obj = JSVAL_TO_OBJECT(v);
2948 if (!OBJECT_IS_XML(cx, obj)) {
2949 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
2950 return NULL;
2951 str = js_ValueToString(cx, v);
2952 if (!str)
2953 return NULL;
2954 return EscapeElementValue(cx, NULL, str);
2957 /* Handle non-element cases in this switch, returning from each case. */
2958 xml = (JSXML *) JS_GetPrivate(cx, obj);
2959 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
2962 static JSObject *
2963 ToAttributeName(JSContext *cx, jsval v)
2965 JSString *name, *uri, *prefix;
2966 JSObject *obj;
2967 JSClass *clasp;
2968 JSObject *qn;
2970 if (JSVAL_IS_STRING(v)) {
2971 name = JSVAL_TO_STRING(v);
2972 uri = prefix = cx->runtime->emptyString;
2973 } else {
2974 if (JSVAL_IS_PRIMITIVE(v)) {
2975 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2976 JSDVG_IGNORE_STACK, v, NULL);
2977 return NULL;
2980 obj = JSVAL_TO_OBJECT(v);
2981 clasp = OBJ_GET_CLASS(cx, obj);
2982 if (clasp == &js_AttributeNameClass)
2983 return obj;
2985 if (clasp == &js_QNameClass.base) {
2986 qn = obj;
2987 uri = GetURI(qn);
2988 prefix = GetPrefix(qn);
2989 name = GetLocalName(qn);
2990 } else {
2991 if (clasp == &js_AnyNameClass) {
2992 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2993 } else {
2994 name = js_ValueToString(cx, v);
2995 if (!name)
2996 return NULL;
2998 uri = prefix = cx->runtime->emptyString;
3002 qn = NewXMLQName(cx, uri, prefix, name, &js_AttributeNameClass);
3003 if (!qn)
3004 return NULL;
3005 return qn;
3008 static void
3009 ReportBadXMLName(JSContext *cx, jsval id)
3011 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, id, NULL);
3014 static JSBool
3015 IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
3017 JSAtom *atom;
3018 JSString *uri;
3020 atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom;
3021 uri = GetURI(qn);
3022 if (uri && atom &&
3023 (uri == ATOM_TO_STRING(atom) ||
3024 js_EqualStrings(uri, ATOM_TO_STRING(atom)))) {
3025 return JS_ValueToId(cx, STRING_TO_JSVAL(GetLocalName(qn)), funidp);
3027 *funidp = 0;
3028 return JS_TRUE;
3031 JSBool
3032 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
3034 if (OBJ_GET_CLASS(cx, obj) == &js_QNameClass.base)
3035 return IsFunctionQName(cx, obj, funidp);
3036 *funidp = 0;
3037 return JS_TRUE;
3040 static JSObject *
3041 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
3043 JSString *name;
3044 JSObject *obj;
3045 JSClass *clasp;
3046 uint32 index;
3048 if (JSVAL_IS_STRING(v)) {
3049 name = JSVAL_TO_STRING(v);
3050 } else {
3051 if (JSVAL_IS_PRIMITIVE(v)) {
3052 ReportBadXMLName(cx, v);
3053 return NULL;
3056 obj = JSVAL_TO_OBJECT(v);
3057 clasp = OBJ_GET_CLASS(cx, obj);
3058 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
3059 goto out;
3060 if (clasp == &js_AnyNameClass) {
3061 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3062 goto construct;
3064 name = js_ValueToString(cx, v);
3065 if (!name)
3066 return NULL;
3070 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3072 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3074 * First, _P_ should be _s_, to refer to the given string.
3076 * Second, why does ToXMLName applied to the string type throw TypeError
3077 * only for numeric literals without any leading or trailing whitespace?
3079 * If the idea is to reject uint32 property names, then the check needs to
3080 * be stricter, to exclude hexadecimal and floating point literals.
3082 if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
3083 goto bad;
3085 if (*JSSTRING_CHARS(name) == '@') {
3086 name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1);
3087 if (!name)
3088 return NULL;
3089 *funidp = 0;
3090 return ToAttributeName(cx, STRING_TO_JSVAL(name));
3093 construct:
3094 v = STRING_TO_JSVAL(name);
3095 obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
3096 if (!obj)
3097 return NULL;
3099 out:
3100 if (!IsFunctionQName(cx, obj, funidp))
3101 return NULL;
3102 return obj;
3104 bad:
3105 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3106 JSMSG_BAD_XML_NAME,
3107 js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
3108 return NULL;
3111 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3112 static JSBool
3113 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
3115 JSString *prefix, *prefix2;
3116 JSObject *match, *ns2;
3117 uint32 i, n, m;
3119 if (xml->xml_class != JSXML_CLASS_ELEMENT)
3120 return JS_TRUE;
3122 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
3123 prefix = GetPrefix(ns);
3124 if (!prefix) {
3125 match = NULL;
3126 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3127 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3128 if (ns2 && js_EqualStrings(GetURI(ns2), GetURI(ns))) {
3129 match = ns2;
3130 break;
3133 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
3134 return JS_FALSE;
3135 } else {
3136 if (IS_EMPTY(prefix) && IS_EMPTY(GetURI(xml->name)))
3137 return JS_TRUE;
3138 match = NULL;
3139 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3140 m = XML_NOT_FOUND;
3141 #endif
3142 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3143 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3144 if (ns2 && (prefix2 = GetPrefix(ns2)) &&
3145 js_EqualStrings(prefix2, prefix)) {
3146 match = ns2;
3147 m = i;
3148 break;
3151 if (match && !js_EqualStrings(GetURI(match), GetURI(ns))) {
3152 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
3153 JSObject);
3154 JS_ASSERT(ns2 == match);
3155 match->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
3156 if (!AddInScopeNamespace(cx, xml, match))
3157 return JS_FALSE;
3159 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
3160 return JS_FALSE;
3163 /* OPTION: enforce that descendants have superset namespaces. */
3164 return JS_TRUE;
3167 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3168 static JSBool
3169 Append(JSContext *cx, JSXML *list, JSXML *xml)
3171 uint32 i, j, k, n;
3172 JSXML *kid;
3174 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
3175 i = list->xml_kids.length;
3176 n = 1;
3177 if (xml->xml_class == JSXML_CLASS_LIST) {
3178 list->xml_target = xml->xml_target;
3179 list->xml_targetprop = xml->xml_targetprop;
3180 n = JSXML_LENGTH(xml);
3181 k = i + n;
3182 if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
3183 return JS_FALSE;
3184 for (j = 0; j < n; j++) {
3185 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
3186 if (kid)
3187 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3189 return JS_TRUE;
3192 list->xml_target = xml->parent;
3193 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3194 list->xml_targetprop = NULL;
3195 else
3196 list->xml_targetprop = xml->name;
3197 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3198 return JS_FALSE;
3199 return JS_TRUE;
3202 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3203 static JSXML *
3204 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3206 static JSXML *
3207 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3209 JSXML *copy;
3210 JSBool ok;
3212 /* Our caller may not be protecting newborns with a local root scope. */
3213 if (!js_EnterLocalRootScope(cx))
3214 return NULL;
3215 copy = DeepCopyInLRS(cx, xml, flags);
3216 if (copy) {
3217 if (obj) {
3218 /* Caller provided the object for this copy, hook 'em up. */
3219 ok = JS_SetPrivate(cx, obj, copy);
3220 if (ok)
3221 copy->object = obj;
3222 } else {
3223 ok = js_GetXMLObject(cx, copy) != NULL;
3225 if (!ok)
3226 copy = NULL;
3228 js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
3229 return copy;
3233 * (i) We must be in a local root scope (InLRS).
3234 * (ii) parent must have a rooted object.
3235 * (iii) from's owning object must be locked if not thread-local.
3237 static JSBool
3238 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3239 uintN flags)
3241 uint32 j, n;
3242 JSXMLArrayCursor cursor;
3243 JSBool ok;
3244 JSXML *kid, *kid2;
3245 JSString *str;
3247 JS_ASSERT(cx->localRootStack);
3249 n = from->length;
3250 if (!XMLArraySetCapacity(cx, to, n))
3251 return JS_FALSE;
3253 XMLArrayCursorInit(&cursor, from);
3254 j = 0;
3255 ok = JS_TRUE;
3256 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3257 if ((flags & XSF_IGNORE_COMMENTS) &&
3258 kid->xml_class == JSXML_CLASS_COMMENT) {
3259 continue;
3261 if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3262 kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3263 continue;
3265 if ((flags & XSF_IGNORE_WHITESPACE) &&
3266 (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3267 continue;
3269 kid2 = DeepCopyInLRS(cx, kid, flags);
3270 if (!kid2) {
3271 to->length = j;
3272 ok = JS_FALSE;
3273 break;
3276 if ((flags & XSF_IGNORE_WHITESPACE) &&
3277 n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3278 str = ChompXMLWhitespace(cx, kid2->xml_value);
3279 if (!str) {
3280 to->length = j;
3281 ok = JS_FALSE;
3282 break;
3284 kid2->xml_value = str;
3287 XMLARRAY_SET_MEMBER(to, j, kid2);
3288 ++j;
3289 if (parent->xml_class != JSXML_CLASS_LIST)
3290 kid2->parent = parent;
3292 XMLArrayCursorFinish(&cursor);
3293 if (!ok)
3294 return JS_FALSE;
3296 if (j < n)
3297 XMLArrayTrim(to);
3298 return JS_TRUE;
3301 static JSXML *
3302 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
3304 JSXML *copy;
3305 JSObject *qn;
3306 JSBool ok;
3307 uint32 i, n;
3308 JSObject *ns, *ns2;
3310 /* Our caller must be protecting newborn objects. */
3311 JS_ASSERT(cx->localRootStack);
3313 JS_CHECK_RECURSION(cx, return NULL);
3315 copy = js_NewXML(cx, (JSXMLClass) xml->xml_class);
3316 if (!copy)
3317 return NULL;
3318 qn = xml->name;
3319 if (qn) {
3320 qn = NewXMLQName(cx, GetURI(qn), GetPrefix(qn), GetLocalName(qn));
3321 if (!qn) {
3322 ok = JS_FALSE;
3323 goto out;
3326 copy->name = qn;
3327 copy->xml_flags = xml->xml_flags;
3329 if (JSXML_HAS_VALUE(xml)) {
3330 copy->xml_value = xml->xml_value;
3331 ok = JS_TRUE;
3332 } else {
3333 ok = DeepCopySetInLRS(cx, &xml->xml_kids, &copy->xml_kids, copy, flags);
3334 if (!ok)
3335 goto out;
3337 if (xml->xml_class == JSXML_CLASS_LIST) {
3338 copy->xml_target = xml->xml_target;
3339 copy->xml_targetprop = xml->xml_targetprop;
3340 } else {
3341 n = xml->xml_namespaces.length;
3342 ok = XMLArraySetCapacity(cx, &copy->xml_namespaces, n);
3343 if (!ok)
3344 goto out;
3345 for (i = 0; i < n; i++) {
3346 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3347 if (!ns)
3348 continue;
3349 ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns),
3350 IsDeclared(ns));
3351 if (!ns2) {
3352 copy->xml_namespaces.length = i;
3353 ok = JS_FALSE;
3354 goto out;
3356 XMLARRAY_SET_MEMBER(&copy->xml_namespaces, i, ns2);
3359 ok = DeepCopySetInLRS(cx, &xml->xml_attrs, &copy->xml_attrs, copy,
3361 if (!ok)
3362 goto out;
3366 out:
3367 if (!ok)
3368 return NULL;
3369 return copy;
3372 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3373 static void
3374 DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index)
3376 JSXML *kid;
3378 if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3379 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3380 if (kid)
3381 kid->parent = NULL;
3382 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3386 typedef JSBool (*JSXMLNameMatcher)(JSObject *nameqn, JSXML *xml);
3388 static JSBool
3389 MatchAttrName(JSObject *nameqn, JSXML *attr)
3391 JSObject *attrqn = attr->name;
3392 JSString *localName = GetLocalName(nameqn);
3393 JSString *uri;
3395 return (IS_STAR(localName) ||
3396 js_EqualStrings(GetLocalName(attrqn), localName)) &&
3397 (!(uri = GetURI(nameqn)) ||
3398 js_EqualStrings(GetURI(attrqn), uri));
3401 static JSBool
3402 MatchElemName(JSObject *nameqn, JSXML *elem)
3404 JSString *localName = GetLocalName(nameqn);
3405 JSString *uri;
3407 return (IS_STAR(localName) ||
3408 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3409 js_EqualStrings(GetLocalName(elem->name), localName))) &&
3410 (!(uri = GetURI(nameqn)) ||
3411 (elem->xml_class == JSXML_CLASS_ELEMENT &&
3412 js_EqualStrings(GetURI(elem->name), uri)));
3415 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3416 static JSBool
3417 DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list)
3419 uint32 i, n;
3420 JSXML *attr, *kid;
3422 JS_CHECK_RECURSION(cx, return JS_FALSE);
3424 if (xml->xml_class == JSXML_CLASS_ELEMENT &&
3425 OBJ_GET_CLASS(cx, nameqn) == &js_AttributeNameClass) {
3426 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
3427 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3428 if (attr && MatchAttrName(nameqn, attr)) {
3429 if (!Append(cx, list, attr))
3430 return JS_FALSE;
3435 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3436 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3437 if (!kid)
3438 continue;
3439 if (OBJ_GET_CLASS(cx, nameqn) != &js_AttributeNameClass &&
3440 MatchElemName(nameqn, kid)) {
3441 if (!Append(cx, list, kid))
3442 return JS_FALSE;
3444 if (!DescendantsHelper(cx, kid, nameqn, list))
3445 return JS_FALSE;
3447 return JS_TRUE;
3450 static JSXML *
3451 Descendants(JSContext *cx, JSXML *xml, jsval id)
3453 jsid funid;
3454 JSObject *nameqn;
3455 JSObject *listobj;
3456 JSXML *list, *kid;
3457 uint32 i, n;
3458 JSBool ok;
3460 nameqn = ToXMLName(cx, id, &funid);
3461 if (!nameqn)
3462 return NULL;
3464 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3465 if (!listobj)
3466 return NULL;
3467 list = (JSXML *) JS_GetPrivate(cx, listobj);
3468 if (funid)
3469 return list;
3472 * Protect nameqn's object and strings from GC by linking list to it
3473 * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj,
3474 * which protects list. Any other object allocations occuring beneath
3475 * DescendantsHelper use local roots.
3477 list->name = nameqn;
3478 if (!js_EnterLocalRootScope(cx))
3479 return NULL;
3480 if (xml->xml_class == JSXML_CLASS_LIST) {
3481 ok = JS_TRUE;
3482 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
3483 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3484 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
3485 ok = DescendantsHelper(cx, kid, nameqn, list);
3486 if (!ok)
3487 break;
3490 } else {
3491 ok = DescendantsHelper(cx, xml, nameqn, list);
3493 js_LeaveLocalRootScopeWithResult(cx, (jsval) list);
3494 if (!ok)
3495 return NULL;
3496 list->name = NULL;
3497 return list;
3500 static JSBool
3501 xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
3503 /* Recursive (JSXML *) parameterized version of Equals. */
3504 static JSBool
3505 XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3507 JSObject *qn, *vqn;
3508 uint32 i, j, n;
3509 JSXMLArrayCursor cursor, vcursor;
3510 JSXML *kid, *vkid, *attr, *vattr;
3511 JSBool ok;
3512 JSObject *xobj, *vobj;
3514 retry:
3515 if (xml->xml_class != vxml->xml_class) {
3516 if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
3517 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3518 if (xml)
3519 goto retry;
3521 if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3522 vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3523 if (vxml)
3524 goto retry;
3526 *bp = JS_FALSE;
3527 return JS_TRUE;
3530 qn = xml->name;
3531 vqn = vxml->name;
3532 if (qn) {
3533 *bp = vqn &&
3534 js_EqualStrings(GetLocalName(qn), GetLocalName(vqn)) &&
3535 js_EqualStrings(GetURI(qn), GetURI(vqn));
3536 } else {
3537 *bp = vqn == NULL;
3539 if (!*bp)
3540 return JS_TRUE;
3542 if (JSXML_HAS_VALUE(xml)) {
3543 *bp = js_EqualStrings(xml->xml_value, vxml->xml_value);
3544 } else if (xml->xml_kids.length != vxml->xml_kids.length) {
3545 *bp = JS_FALSE;
3546 } else {
3547 XMLArrayCursorInit(&cursor, &xml->xml_kids);
3548 XMLArrayCursorInit(&vcursor, &vxml->xml_kids);
3549 for (;;) {
3550 kid = (JSXML *) XMLArrayCursorNext(&cursor);
3551 vkid = (JSXML *) XMLArrayCursorNext(&vcursor);
3552 if (!kid || !vkid) {
3553 *bp = !kid && !vkid;
3554 ok = JS_TRUE;
3555 break;
3557 xobj = js_GetXMLObject(cx, kid);
3558 vobj = js_GetXMLObject(cx, vkid);
3559 ok = xobj && vobj &&
3560 xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp);
3561 if (!ok || !*bp)
3562 break;
3564 XMLArrayCursorFinish(&vcursor);
3565 XMLArrayCursorFinish(&cursor);
3566 if (!ok)
3567 return JS_FALSE;
3569 if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3570 n = xml->xml_attrs.length;
3571 if (n != vxml->xml_attrs.length)
3572 *bp = JS_FALSE;
3573 for (i = 0; *bp && i < n; i++) {
3574 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3575 if (!attr)
3576 continue;
3577 j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3578 if (j == XML_NOT_FOUND) {
3579 *bp = JS_FALSE;
3580 break;
3582 vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3583 if (!vattr)
3584 continue;
3585 *bp = js_EqualStrings(attr->xml_value, vattr->xml_value);
3590 return JS_TRUE;
3593 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3594 static JSBool
3595 Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3597 JSObject *vobj;
3598 JSXML *vxml;
3600 if (JSVAL_IS_PRIMITIVE(v)) {
3601 *bp = JS_FALSE;
3602 if (xml->xml_class == JSXML_CLASS_LIST) {
3603 if (xml->xml_kids.length == 1) {
3604 vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3605 if (!vxml)
3606 return JS_TRUE;
3607 vobj = js_GetXMLObject(cx, vxml);
3608 if (!vobj)
3609 return JS_FALSE;
3610 return js_XMLObjectOps.equality(cx, vobj, v, bp);
3612 if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3613 *bp = JS_TRUE;
3615 } else {
3616 vobj = JSVAL_TO_OBJECT(v);
3617 if (!OBJECT_IS_XML(cx, vobj)) {
3618 *bp = JS_FALSE;
3619 } else {
3620 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3621 if (!XMLEquals(cx, xml, vxml, bp))
3622 return JS_FALSE;
3625 return JS_TRUE;
3628 static JSBool
3629 CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3631 JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3633 do {
3634 if (xml == kid) {
3635 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3636 JSMSG_CYCLIC_VALUE, js_XML_str);
3637 return JS_FALSE;
3639 } while ((xml = xml->parent) != NULL);
3641 return JS_TRUE;
3644 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3645 static JSBool
3646 Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3648 uint32 j, n;
3649 JSXML *vxml, *kid;
3650 JSObject *vobj;
3651 JSString *str;
3653 if (!JSXML_HAS_KIDS(xml))
3654 return JS_TRUE;
3656 n = 1;
3657 vxml = NULL;
3658 if (!JSVAL_IS_PRIMITIVE(v)) {
3659 vobj = JSVAL_TO_OBJECT(v);
3660 if (OBJECT_IS_XML(cx, vobj)) {
3661 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3662 if (vxml->xml_class == JSXML_CLASS_LIST) {
3663 n = vxml->xml_kids.length;
3664 if (n == 0)
3665 return JS_TRUE;
3666 for (j = 0; j < n; j++) {
3667 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3668 if (!kid)
3669 continue;
3670 if (!CheckCycle(cx, xml, kid))
3671 return JS_FALSE;
3673 } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3674 /* OPTION: enforce that descendants have superset namespaces. */
3675 if (!CheckCycle(cx, xml, vxml))
3676 return JS_FALSE;
3680 if (!vxml) {
3681 str = js_ValueToString(cx, v);
3682 if (!str)
3683 return JS_FALSE;
3685 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3686 if (!vxml)
3687 return JS_FALSE;
3688 vxml->xml_value = str;
3691 if (i > xml->xml_kids.length)
3692 i = xml->xml_kids.length;
3694 if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3695 return JS_FALSE;
3697 if (vxml->xml_class == JSXML_CLASS_LIST) {
3698 for (j = 0; j < n; j++) {
3699 kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3700 if (!kid)
3701 continue;
3702 kid->parent = xml;
3703 XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3705 /* OPTION: enforce that descendants have superset namespaces. */
3707 } else {
3708 vxml->parent = xml;
3709 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3711 return JS_TRUE;
3714 static JSBool
3715 IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp)
3717 JSString *str;
3719 if (index <= JSVAL_INT_MAX) {
3720 *idvp = INT_TO_JSVAL(index);
3721 } else {
3722 str = js_NumberToString(cx, (jsdouble) index);
3723 if (!str)
3724 return JS_FALSE;
3725 *idvp = STRING_TO_JSVAL(str);
3727 return JS_TRUE;
3730 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3731 static JSBool
3732 Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3734 uint32 n;
3735 JSXML *vxml, *kid;
3736 JSObject *vobj;
3737 JSString *str;
3739 if (!JSXML_HAS_KIDS(xml))
3740 return JS_TRUE;
3743 * 9.1.1.12
3744 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3745 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3747 n = xml->xml_kids.length;
3748 if (i > n)
3749 i = n;
3751 vxml = NULL;
3752 if (!JSVAL_IS_PRIMITIVE(v)) {
3753 vobj = JSVAL_TO_OBJECT(v);
3754 if (OBJECT_IS_XML(cx, vobj))
3755 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3758 switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) {
3759 case JSXML_CLASS_ELEMENT:
3760 /* OPTION: enforce that descendants have superset namespaces. */
3761 if (!CheckCycle(cx, xml, vxml))
3762 return JS_FALSE;
3763 case JSXML_CLASS_COMMENT:
3764 case JSXML_CLASS_PROCESSING_INSTRUCTION:
3765 case JSXML_CLASS_TEXT:
3766 goto do_replace;
3768 case JSXML_CLASS_LIST:
3769 if (i < n)
3770 DeleteByIndex(cx, xml, i);
3771 if (!Insert(cx, xml, i, v))
3772 return JS_FALSE;
3773 break;
3775 default:
3776 str = js_ValueToString(cx, v);
3777 if (!str)
3778 return JS_FALSE;
3780 vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3781 if (!vxml)
3782 return JS_FALSE;
3783 vxml->xml_value = str;
3785 do_replace:
3786 vxml->parent = xml;
3787 if (i < n) {
3788 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3789 if (kid)
3790 kid->parent = NULL;
3792 if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3793 return JS_FALSE;
3794 break;
3797 return JS_TRUE;
3800 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3801 static void
3802 DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn,
3803 JSBool attributes)
3805 JSXMLArray *array;
3806 uint32 index, deleteCount;
3807 JSXML *kid;
3808 JSXMLNameMatcher matcher;
3810 if (xml->xml_class == JSXML_CLASS_LIST) {
3811 array = &xml->xml_kids;
3812 for (index = 0; index < array->length; index++) {
3813 kid = XMLARRAY_MEMBER(array, index, JSXML);
3814 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT)
3815 DeleteNamedProperty(cx, kid, nameqn, attributes);
3817 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3818 if (attributes) {
3819 array = &xml->xml_attrs;
3820 matcher = MatchAttrName;
3821 } else {
3822 array = &xml->xml_kids;
3823 matcher = MatchElemName;
3825 deleteCount = 0;
3826 for (index = 0; index < array->length; index++) {
3827 kid = XMLARRAY_MEMBER(array, index, JSXML);
3828 if (kid && matcher(nameqn, kid)) {
3829 kid->parent = NULL;
3830 XMLArrayDelete(cx, array, index, JS_FALSE);
3831 ++deleteCount;
3832 } else if (deleteCount != 0) {
3833 XMLARRAY_SET_MEMBER(array,
3834 index - deleteCount,
3835 array->vector[index]);
3838 array->length -= deleteCount;
3842 /* ECMA-357 9.2.1.3 index case. */
3843 static void
3844 DeleteListElement(JSContext *cx, JSXML *xml, uint32 index)
3846 JSXML *kid, *parent;
3847 uint32 kidIndex;
3849 JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST);
3851 if (index < xml->xml_kids.length) {
3852 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3853 if (kid) {
3854 parent = kid->parent;
3855 if (parent) {
3856 JS_ASSERT(parent != xml);
3857 JS_ASSERT(JSXML_HAS_KIDS(parent));
3859 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
3860 DeleteNamedProperty(cx, parent, kid->name, JS_TRUE);
3861 } else {
3862 kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
3863 NULL);
3864 JS_ASSERT(kidIndex != XML_NOT_FOUND);
3865 DeleteByIndex(cx, parent, kidIndex);
3868 XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3873 static JSBool
3874 SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
3876 JSXMLArray *nsarray;
3877 uint32 i, n;
3878 JSObject *ns;
3880 nsarray = &xml->xml_namespaces;
3881 while ((xml = xml->parent) != NULL) {
3882 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3883 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3884 if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
3885 if (!XMLARRAY_APPEND(cx, nsarray, ns))
3886 return JS_FALSE;
3890 return JS_TRUE;
3893 static JSBool
3894 GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list)
3896 JSXMLArray *array;
3897 JSXMLNameMatcher matcher;
3898 JSXMLArrayCursor cursor;
3899 JSXML *kid;
3900 JSBool attrs;
3902 if (xml->xml_class == JSXML_CLASS_LIST) {
3903 XMLArrayCursorInit(&cursor, &xml->xml_kids);
3904 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3905 if (kid->xml_class == JSXML_CLASS_ELEMENT &&
3906 !GetNamedProperty(cx, kid, nameqn, list)) {
3907 break;
3910 XMLArrayCursorFinish(&cursor);
3911 if (kid)
3912 return JS_FALSE;
3913 } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3914 attrs = (OBJ_GET_CLASS(cx, nameqn) == &js_AttributeNameClass);
3915 if (attrs) {
3916 array = &xml->xml_attrs;
3917 matcher = MatchAttrName;
3918 } else {
3919 array = &xml->xml_kids;
3920 matcher = MatchElemName;
3923 XMLArrayCursorInit(&cursor, array);
3924 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3925 if (matcher(nameqn, kid)) {
3926 if (!attrs &&
3927 kid->xml_class == JSXML_CLASS_ELEMENT &&
3928 !SyncInScopeNamespaces(cx, kid)) {
3929 break;
3931 if (!Append(cx, list, kid))
3932 break;
3935 XMLArrayCursorFinish(&cursor);
3936 if (kid)
3937 return JS_FALSE;
3940 return JS_TRUE;
3943 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3944 static JSBool
3945 GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3947 JSXML *xml, *list, *kid;
3948 uint32 index;
3949 JSObject *kidobj, *listobj;
3950 JSObject *nameqn;
3951 jsid funid;
3952 jsval roots[2];
3953 JSTempValueRooter tvr;
3955 xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
3956 if (!xml)
3957 return JS_TRUE;
3959 if (js_IdIsIndex(id, &index)) {
3960 if (xml->xml_class != JSXML_CLASS_LIST) {
3961 *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
3962 } else {
3964 * ECMA-357 9.2.1.1 starts here.
3966 * Erratum: 9.2 is not completely clear that indexed properties
3967 * correspond to kids, but that's what it seems to say, and it's
3968 * what any sane user would want.
3970 if (index < xml->xml_kids.length) {
3971 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3972 if (!kid) {
3973 *vp = JSVAL_VOID;
3974 return JS_TRUE;
3976 kidobj = js_GetXMLObject(cx, kid);
3977 if (!kidobj)
3978 return JS_FALSE;
3980 *vp = OBJECT_TO_JSVAL(kidobj);
3981 } else {
3982 *vp = JSVAL_VOID;
3985 return JS_TRUE;
3989 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3991 nameqn = ToXMLName(cx, id, &funid);
3992 if (!nameqn)
3993 return JS_FALSE;
3994 if (funid)
3995 return js_GetXMLFunction(cx, obj, funid, vp);
3997 roots[0] = OBJECT_TO_JSVAL(nameqn);
3998 JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr);
4000 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
4001 if (listobj) {
4002 roots[1] = OBJECT_TO_JSVAL(listobj);
4003 tvr.count++;
4005 list = (JSXML *) JS_GetPrivate(cx, listobj);
4006 if (!GetNamedProperty(cx, xml, nameqn, list)) {
4007 listobj = NULL;
4008 } else {
4010 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
4011 * given list's [[TargetProperty]] to the property that is being
4012 * appended. This means that any use of the internal [[Get]]
4013 * property returns a list which, when used by e.g. [[Insert]]
4014 * duplicates the last element matched by id. See bug 336921.
4016 list->xml_target = xml;
4017 list->xml_targetprop = nameqn;
4018 *vp = OBJECT_TO_JSVAL(listobj);
4022 JS_POP_TEMP_ROOT(cx, &tvr);
4023 return listobj != NULL;
4026 static JSXML *
4027 CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
4029 JS_ASSERT(xml->object != obj);
4031 xml = DeepCopy(cx, xml, obj, 0);
4032 if (!xml)
4033 return NULL;
4035 JS_ASSERT(xml->object == obj);
4036 return xml;
4039 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
4040 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
4042 static JSString *
4043 KidToString(JSContext *cx, JSXML *xml, uint32 index)
4045 JSXML *kid;
4046 JSObject *kidobj;
4048 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
4049 if (!kid)
4050 return cx->runtime->emptyString;
4051 kidobj = js_GetXMLObject(cx, kid);
4052 if (!kidobj)
4053 return NULL;
4054 return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj));
4057 /* Forward declared -- its implementation uses other statics that call it. */
4058 static JSBool
4059 ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
4061 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
4062 static JSBool
4063 PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4065 JSBool ok, primitiveAssign;
4066 enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
4067 jsval roots[3];
4068 JSTempValueRooter tvr;
4069 JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
4070 JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
4071 JSObject *targetprop, *nameqn, *attrqn;
4072 uint32 index, i, j, k, n, q, matchIndex;
4073 jsval attrval, nsval;
4074 jsid funid;
4075 JSString *left, *right, *space, *uri;
4076 JSObject *ns;
4078 xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
4079 if (!xml)
4080 return JS_TRUE;
4082 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
4083 if (!xml)
4084 return JS_FALSE;
4086 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
4087 vxml = NULL;
4088 if (!JSVAL_IS_PRIMITIVE(*vp)) {
4089 vobj = JSVAL_TO_OBJECT(*vp);
4090 if (OBJECT_IS_XML(cx, vobj))
4091 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4094 ok = js_EnterLocalRootScope(cx);
4095 if (!ok)
4096 return JS_FALSE;
4097 MUST_FLOW_THROUGH("out");
4098 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4099 roots[ID_ROOT] = id;
4100 roots[VAL_ROOT] = *vp;
4101 JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr);
4103 if (js_IdIsIndex(id, &index)) {
4104 if (xml->xml_class != JSXML_CLASS_LIST) {
4105 /* See NOTE in spec: this variation is reserved for future use. */
4106 ReportBadXMLName(cx, id);
4107 goto bad;
4111 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
4113 i = index;
4115 /* 2(a-b). */
4116 if (xml->xml_target) {
4117 ok = ResolveValue(cx, xml->xml_target, &rxml);
4118 if (!ok)
4119 goto out;
4120 if (!rxml)
4121 goto out;
4122 JS_ASSERT(rxml->object);
4123 } else {
4124 rxml = NULL;
4127 /* 2(c). */
4128 if (index >= xml->xml_kids.length) {
4129 /* 2(c)(i). */
4130 if (rxml) {
4131 if (rxml->xml_class == JSXML_CLASS_LIST) {
4132 if (rxml->xml_kids.length != 1)
4133 goto out;
4134 rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
4135 if (!rxml)
4136 goto out;
4137 ok = js_GetXMLObject(cx, rxml) != NULL;
4138 if (!ok)
4139 goto out;
4143 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
4144 * _y.[[Parent]] = r_ where _r_ is the result of
4145 * [[ResolveValue]] called on _x.[[TargetObject]] in
4146 * 2(a)(i). This can result in text parenting text:
4148 * var MYXML = new XML();
4149 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
4151 * (testcase from Werner Sharp <wsharp@macromedia.com>).
4153 * To match insertChildAfter, insertChildBefore,
4154 * prependChild, and setChildren, we should silently
4155 * do nothing in this case.
4157 if (!JSXML_HAS_KIDS(rxml))
4158 goto out;
4161 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
4162 targetprop = xml->xml_targetprop;
4163 if (!targetprop || IS_STAR(GetLocalName(targetprop))) {
4164 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
4165 kid = js_NewXML(cx, JSXML_CLASS_TEXT);
4166 if (!kid)
4167 goto bad;
4168 } else {
4169 nameobj = targetprop;
4170 if (!nameobj)
4171 goto bad;
4172 if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
4174 * 2(c)(iii)(1-3).
4175 * Note that rxml can't be null here, because target
4176 * and targetprop are non-null.
4178 ok = GetProperty(cx, rxml->object, id, &attrval);
4179 if (!ok)
4180 goto out;
4181 if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
4182 goto out;
4183 attrobj = JSVAL_TO_OBJECT(attrval);
4184 attr = (JSXML *) JS_GetPrivate(cx, attrobj);
4185 if (JSXML_LENGTH(attr) != 0)
4186 goto out;
4188 kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4189 } else {
4190 /* 2(c)(v). */
4191 kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
4193 if (!kid)
4194 goto bad;
4196 /* An important bit of 2(c)(ii). */
4197 kid->name = targetprop;
4200 /* Final important bit of 2(c)(ii). */
4201 kid->parent = rxml;
4203 /* 2(c)(vi-vii). */
4204 i = xml->xml_kids.length;
4205 if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
4207 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
4208 * y.[[Parent]] is here called kid->parent, which we know
4209 * from 2(c)(ii) is _r_, here called rxml. So let's just
4210 * test that! Erratum, the spec should be simpler here.
4212 if (rxml) {
4213 JS_ASSERT(JSXML_HAS_KIDS(rxml));
4214 n = rxml->xml_kids.length;
4215 j = n - 1;
4216 if (n != 0 && i != 0) {
4217 for (n = j, j = 0; j < n; j++) {
4218 if (rxml->xml_kids.vector[j] ==
4219 xml->xml_kids.vector[i-1]) {
4220 break;
4225 kidobj = js_GetXMLObject(cx, kid);
4226 if (!kidobj)
4227 goto bad;
4228 ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4229 if (!ok)
4230 goto out;
4234 * 2(c)(vii)(2-3).
4235 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4236 * typo for [[TargetProperty]].
4238 if (vxml) {
4239 kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4240 ? vxml->xml_targetprop
4241 : vxml->name;
4245 /* 2(c)(viii). */
4246 ok = Append(cx, xml, kid);
4247 if (!ok)
4248 goto out;
4251 /* 2(d). */
4252 if (!vxml ||
4253 vxml->xml_class == JSXML_CLASS_TEXT ||
4254 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4255 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4256 if (!ok)
4257 goto out;
4258 roots[VAL_ROOT] = *vp;
4261 /* 2(e). */
4262 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4263 if (!kid)
4264 goto out;
4265 parent = kid->parent;
4266 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4267 nameobj = kid->name;
4268 if (OBJ_GET_CLASS(cx, nameobj) != &js_AttributeNameClass) {
4269 nameobj = NewXMLQName(cx, GetURI(nameobj), GetPrefix(nameobj),
4270 GetLocalName(nameobj),
4271 &js_AttributeNameClass);
4272 if (!nameobj)
4273 goto bad;
4275 id = OBJECT_TO_JSVAL(nameobj);
4277 if (parent) {
4278 /* 2(e)(i). */
4279 parentobj = js_GetXMLObject(cx, parent);
4280 if (!parentobj)
4281 goto bad;
4282 ok = PutProperty(cx, parentobj, id, vp);
4283 if (!ok)
4284 goto out;
4286 /* 2(e)(ii). */
4287 ok = GetProperty(cx, parentobj, id, vp);
4288 if (!ok)
4289 goto out;
4290 attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
4292 /* 2(e)(iii). */
4293 xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
4297 /* 2(f). */
4298 else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4300 * 2(f)(i)
4302 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4303 * if we do that we never change the parent of each child in the
4304 * list. Since [[Put]] when called on an XML object deeply copies
4305 * the provided list _V_, we also do so here. Perhaps the shallow
4306 * copy was a misguided optimization?
4308 copy = DeepCopyInLRS(cx, vxml, 0);
4309 if (!copy)
4310 goto bad;
4311 copyobj = js_GetXMLObject(cx, copy);
4312 if (!copyobj)
4313 goto bad;
4315 JS_ASSERT(parent != xml);
4316 if (parent) {
4317 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4318 JS_ASSERT(q != XML_NOT_FOUND);
4319 ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj));
4320 if (!ok)
4321 goto out;
4323 #ifdef DEBUG
4324 /* Erratum: this loop in the spec is useless. */
4325 for (j = 0, n = copy->xml_kids.length; j < n; j++) {
4326 kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
4327 JS_ASSERT(XMLARRAY_MEMBER(&copy->xml_kids, j, JSXML)
4328 == kid2);
4330 #endif
4334 * 2(f)(iv-vi).
4335 * Erratum: notice the unhandled zero-length V basis case and
4336 * the off-by-one errors for the n != 0 cases in the spec.
4338 n = copy->xml_kids.length;
4339 if (n == 0) {
4340 XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4341 } else {
4342 ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4343 if (!ok)
4344 goto out;
4346 for (j = 0; j < n; j++)
4347 xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4351 /* 2(g). */
4352 else if (vxml || JSXML_HAS_VALUE(kid)) {
4353 if (parent) {
4354 q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4355 JS_ASSERT(q != XML_NOT_FOUND);
4356 ok = Replace(cx, parent, q, *vp);
4357 if (!ok)
4358 goto out;
4360 vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4361 if (!vxml)
4362 goto out;
4363 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
4367 * 2(g)(iii).
4368 * Erratum: _V_ may not be of type XML, but all index-named
4369 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4370 * according to 9.2.1.1 Overview and other places in the spec.
4372 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4373 * or an XML/XMLList object. If *vp is a string, call ToXML
4374 * on it to satisfy the constraint.
4376 if (!vxml) {
4377 JS_ASSERT(JSVAL_IS_STRING(*vp));
4378 vobj = ToXML(cx, *vp);
4379 if (!vobj)
4380 goto bad;
4381 roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4382 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4384 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4387 /* 2(h). */
4388 else {
4389 kidobj = js_GetXMLObject(cx, kid);
4390 if (!kidobj)
4391 goto bad;
4392 id = ATOM_KEY(cx->runtime->atomState.starAtom);
4393 ok = PutProperty(cx, kidobj, id, vp);
4394 if (!ok)
4395 goto out;
4397 } else {
4399 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4401 nameqn = ToXMLName(cx, id, &funid);
4402 if (!nameqn)
4403 goto bad;
4404 if (funid) {
4405 ok = js_SetProperty(cx, obj, funid, vp);
4406 goto out;
4408 nameobj = nameqn;
4409 roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
4411 if (xml->xml_class == JSXML_CLASS_LIST) {
4413 * Step 3 of 9.2.1.2.
4414 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4415 * or an r with r.[[Length]] != 1, throw TypeError.
4417 n = JSXML_LENGTH(xml);
4418 if (n > 1)
4419 goto type_error;
4420 if (n == 0) {
4421 ok = ResolveValue(cx, xml, &rxml);
4422 if (!ok)
4423 goto out;
4424 if (!rxml || JSXML_LENGTH(rxml) != 1)
4425 goto type_error;
4426 ok = Append(cx, xml, rxml);
4427 if (!ok)
4428 goto out;
4430 JS_ASSERT(JSXML_LENGTH(xml) == 1);
4431 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4432 if (!xml)
4433 goto out;
4434 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
4435 obj = js_GetXMLObject(cx, xml);
4436 if (!obj)
4437 goto bad;
4438 roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4440 /* FALL THROUGH to non-list case */
4444 * ECMA-357 9.1.1.2.
4445 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4446 * effort in ToString or [[DeepCopy]].
4449 if (JSXML_HAS_VALUE(xml))
4450 goto out;
4452 if (!vxml ||
4453 vxml->xml_class == JSXML_CLASS_TEXT ||
4454 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4455 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4456 if (!ok)
4457 goto out;
4458 } else {
4459 rxml = DeepCopyInLRS(cx, vxml, 0);
4460 if (!rxml || !js_GetXMLObject(cx, rxml))
4461 goto bad;
4462 vxml = rxml;
4463 *vp = OBJECT_TO_JSVAL(vxml->object);
4465 roots[VAL_ROOT] = *vp;
4468 * 6.
4469 * Erratum: why is this done here, so early? use is way later....
4471 ok = js_GetDefaultXMLNamespace(cx, &nsval);
4472 if (!ok)
4473 goto out;
4475 if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
4476 /* 7(a). */
4477 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4478 goto out;
4480 /* 7(b-c). */
4481 if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4482 n = vxml->xml_kids.length;
4483 if (n == 0) {
4484 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4485 } else {
4486 left = KidToString(cx, vxml, 0);
4487 if (!left)
4488 goto bad;
4490 space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom);
4491 for (i = 1; i < n; i++) {
4492 left = js_ConcatStrings(cx, left, space);
4493 if (!left)
4494 goto bad;
4495 right = KidToString(cx, vxml, i);
4496 if (!right)
4497 goto bad;
4498 left = js_ConcatStrings(cx, left, right);
4499 if (!left)
4500 goto bad;
4503 roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4505 } else {
4506 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4507 if (!ok)
4508 goto out;
4509 roots[VAL_ROOT] = *vp;
4512 /* 7(d-e). */
4513 match = NULL;
4514 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4515 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4516 if (!attr)
4517 continue;
4518 attrqn = attr->name;
4519 if (js_EqualStrings(GetLocalName(attrqn),
4520 GetLocalName(nameqn))) {
4521 uri = GetURI(nameqn);
4522 if (!uri || js_EqualStrings(GetURI(attrqn), uri)) {
4523 if (!match) {
4524 match = attr;
4525 } else {
4526 DeleteNamedProperty(cx, xml, attrqn, JS_TRUE);
4527 --i;
4533 /* 7(f). */
4534 attr = match;
4535 if (!attr) {
4536 /* 7(f)(i-ii). */
4537 uri = GetURI(nameqn);
4538 if (!uri) {
4539 left = right = cx->runtime->emptyString;
4540 } else {
4541 left = uri;
4542 right = GetPrefix(nameqn);
4544 nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn));
4545 if (!nameqn)
4546 goto bad;
4548 /* 7(f)(iii). */
4549 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4550 if (!attr)
4551 goto bad;
4552 attr->parent = xml;
4553 attr->name = nameqn;
4555 /* 7(f)(iv). */
4556 ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4557 if (!ok)
4558 goto out;
4560 /* 7(f)(v-vi). */
4561 ns = GetNamespace(cx, nameqn, NULL);
4562 if (!ns)
4563 goto bad;
4564 ok = AddInScopeNamespace(cx, xml, ns);
4565 if (!ok)
4566 goto out;
4569 /* 7(g). */
4570 attr->xml_value = JSVAL_TO_STRING(*vp);
4571 goto out;
4574 /* 8-9. */
4575 if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4576 !IS_STAR(GetLocalName(nameqn))) {
4577 goto out;
4580 /* 10-11. */
4581 id = JSVAL_VOID;
4582 primitiveAssign = !vxml && !IS_STAR(GetLocalName(nameqn));
4584 /* 12. */
4585 k = n = xml->xml_kids.length;
4586 matchIndex = XML_NOT_FOUND;
4587 kid2 = NULL;
4588 while (k != 0) {
4589 --k;
4590 kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
4591 if (kid && MatchElemName(nameqn, kid)) {
4592 if (matchIndex != XML_NOT_FOUND)
4593 DeleteByIndex(cx, xml, matchIndex);
4594 matchIndex = k;
4595 kid2 = kid;
4600 * Erratum: ECMA-357 specified child insertion inconsistently:
4601 * insertChildBefore and insertChildAfter insert an arbitrary XML
4602 * instance, and therefore can create cycles, but appendChild as
4603 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4604 * its argument. But the "Semantics" in 13.4.4.3 do not include
4605 * any [[DeepCopy]] call.
4607 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4608 * required adding cycle detection, and allowing duplicate kids to
4609 * be created (see comment 6 in the bug). Allowing duplicate kid
4610 * references means the loop above will delete all but the lowest
4611 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4612 * parent. Thus the need to restore parent here. This is covered
4613 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4615 if (kid2) {
4616 JS_ASSERT(kid2->parent == xml || !kid2->parent);
4617 if (!kid2->parent)
4618 kid2->parent = xml;
4621 /* 13. */
4622 if (matchIndex == XML_NOT_FOUND) {
4623 /* 13(a). */
4624 matchIndex = n;
4626 /* 13(b). */
4627 if (primitiveAssign) {
4628 uri = GetURI(nameqn);
4629 if (!uri) {
4630 ns = JSVAL_TO_OBJECT(nsval);
4631 left = GetURI(ns);
4632 right = GetPrefix(ns);
4633 } else {
4634 left = uri;
4635 right = GetPrefix(nameqn);
4637 nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn));
4638 if (!nameqn)
4639 goto bad;
4641 /* 13(b)(iii). */
4642 vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4643 if (!vobj)
4644 goto bad;
4645 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4646 vxml->parent = xml;
4647 vxml->name = nameqn;
4649 /* 13(b)(iv-vi). */
4650 ns = GetNamespace(cx, nameqn, NULL);
4651 if (!ns)
4652 goto bad;
4653 ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj));
4654 if (!ok)
4655 goto out;
4656 ok = AddInScopeNamespace(cx, vxml, ns);
4657 if (!ok)
4658 goto out;
4662 /* 14. */
4663 if (primitiveAssign) {
4664 JSXMLArrayCursor cursor;
4666 XMLArrayCursorInit(&cursor, &xml->xml_kids);
4667 cursor.index = matchIndex;
4668 kid = (JSXML *) XMLArrayCursorItem(&cursor);
4669 if (JSXML_HAS_KIDS(kid)) {
4670 XMLArrayFinish(cx, &kid->xml_kids);
4671 ok = XMLArrayInit(cx, &kid->xml_kids, 1);
4674 /* 14(b-c). */
4675 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4676 if (ok) {
4677 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4678 if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) {
4679 roots[VAL_ROOT] = *vp;
4680 if ((JSXML *) XMLArrayCursorItem(&cursor) == kid)
4681 ok = Replace(cx, kid, 0, *vp);
4684 XMLArrayCursorFinish(&cursor);
4685 } else {
4686 /* 15(a). */
4687 ok = Replace(cx, xml, matchIndex, *vp);
4691 out:
4692 JS_POP_TEMP_ROOT(cx, &tvr);
4693 js_LeaveLocalRootScope(cx);
4694 return ok;
4696 type_error:
4697 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4698 JSMSG_BAD_XMLLIST_PUT,
4699 js_ValueToPrintableString(cx, id));
4700 bad:
4701 ok = JS_FALSE;
4702 goto out;
4705 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4706 static JSBool
4707 ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4709 JSXML *target, *base;
4710 JSObject *targetprop;
4711 jsval id, tv;
4713 /* Our caller must be protecting newborn objects. */
4714 JS_ASSERT(cx->localRootStack);
4716 if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4717 if (!js_GetXMLObject(cx, list))
4718 return JS_FALSE;
4719 *result = list;
4720 return JS_TRUE;
4723 target = list->xml_target;
4724 targetprop = list->xml_targetprop;
4725 if (!target || !targetprop || IS_STAR(GetLocalName(targetprop))) {
4726 *result = NULL;
4727 return JS_TRUE;
4730 if (OBJ_GET_CLASS(cx, targetprop) == &js_AttributeNameClass) {
4731 *result = NULL;
4732 return JS_TRUE;
4735 if (!ResolveValue(cx, target, &base))
4736 return JS_FALSE;
4737 if (!base) {
4738 *result = NULL;
4739 return JS_TRUE;
4741 if (!js_GetXMLObject(cx, base))
4742 return JS_FALSE;
4744 id = OBJECT_TO_JSVAL(targetprop);
4745 if (!GetProperty(cx, base->object, id, &tv))
4746 return JS_FALSE;
4747 target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
4749 if (JSXML_LENGTH(target) == 0) {
4750 if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4751 *result = NULL;
4752 return JS_TRUE;
4754 tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4755 if (!PutProperty(cx, base->object, id, &tv))
4756 return JS_FALSE;
4757 if (!GetProperty(cx, base->object, id, &tv))
4758 return JS_FALSE;
4759 target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
4762 *result = target;
4763 return JS_TRUE;
4766 static JSBool
4767 HasNamedProperty(JSXML *xml, JSObject *nameqn)
4769 JSBool found;
4770 JSXMLArrayCursor cursor;
4771 JSXML *kid;
4772 JSXMLArray *array;
4773 JSXMLNameMatcher matcher;
4774 uint32 i, n;
4776 if (xml->xml_class == JSXML_CLASS_LIST) {
4777 found = JS_FALSE;
4778 XMLArrayCursorInit(&cursor, &xml->xml_kids);
4779 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
4780 found = HasNamedProperty(kid, nameqn);
4781 if (found)
4782 break;
4784 XMLArrayCursorFinish(&cursor);
4785 return found;
4788 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4789 if (OBJ_GET_CLASS(cx, nameqn) == &js_AttributeNameClass) {
4790 array = &xml->xml_attrs;
4791 matcher = MatchAttrName;
4792 } else {
4793 array = &xml->xml_kids;
4794 matcher = MatchElemName;
4796 for (i = 0, n = array->length; i < n; i++) {
4797 kid = XMLARRAY_MEMBER(array, i, JSXML);
4798 if (kid && matcher(nameqn, kid))
4799 return JS_TRUE;
4803 return JS_FALSE;
4806 static JSBool
4807 HasIndexedProperty(JSXML *xml, uint32 i)
4809 if (xml->xml_class == JSXML_CLASS_LIST)
4810 return i < JSXML_LENGTH(xml);
4812 if (xml->xml_class == JSXML_CLASS_ELEMENT)
4813 return i == 0;
4815 return JS_FALSE;
4818 static JSBool
4819 HasSimpleContent(JSXML *xml);
4821 static JSBool
4822 HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
4824 JSObject *pobj;
4825 JSProperty *prop;
4826 JSXML *xml;
4827 JSTempValueRooter tvr;
4828 JSBool ok;
4830 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_XMLClass);
4832 if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
4833 return JS_FALSE;
4834 if (prop) {
4835 OBJ_DROP_PROPERTY(cx, pobj, prop);
4836 } else {
4837 xml = (JSXML *) JS_GetPrivate(cx, obj);
4838 if (HasSimpleContent(xml)) {
4840 * Search in String.prototype to set found whenever
4841 * js_GetXMLFunction returns existing function.
4843 JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
4844 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
4845 &tvr.u.object);
4846 JS_ASSERT(tvr.u.object);
4847 if (ok) {
4848 ok = js_LookupProperty(cx, tvr.u.object, funid, &pobj, &prop);
4849 if (ok && prop)
4850 OBJ_DROP_PROPERTY(cx, pobj, prop);
4852 JS_POP_TEMP_ROOT(cx, &tvr);
4853 if (!ok)
4854 return JS_FALSE;
4857 *found = (prop != NULL);
4858 return JS_TRUE;
4861 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4862 static JSBool
4863 HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found)
4865 JSXML *xml;
4866 uint32 i;
4867 JSObject *qn;
4868 jsid funid;
4870 xml = (JSXML *) JS_GetPrivate(cx, obj);
4871 if (js_IdIsIndex(id, &i)) {
4872 *found = HasIndexedProperty(xml, i);
4873 } else {
4874 qn = ToXMLName(cx, id, &funid);
4875 if (!qn)
4876 return JS_FALSE;
4877 if (funid) {
4878 if (!HasFunctionProperty(cx, obj, funid, found))
4879 return JS_FALSE;
4880 } else {
4881 *found = HasNamedProperty(xml, qn);
4884 return JS_TRUE;
4887 static void
4888 xml_finalize(JSContext *cx, JSObject *obj)
4890 JSXML *xml;
4892 xml = (JSXML *) JS_GetPrivate(cx, obj);
4893 if (!xml)
4894 return;
4895 if (xml->object == obj)
4896 xml->object = NULL;
4899 static void
4900 xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
4902 uint32 i;
4903 JSXML *xml;
4905 for (i = 0; i < len; i++) {
4906 xml = vec[i];
4907 if (xml) {
4908 JS_SET_TRACING_INDEX(trc, "xml_vector", i);
4909 JS_CallTracer(trc, xml, JSTRACE_XML);
4915 * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
4916 * be native. Therefore, xml_lookupProperty must return a valid JSProperty
4917 * pointer parameter via *propp to signify "property found". Since the only
4918 * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
4919 * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case
4920 * in the interpreter, the only time we add a JSScopeProperty here is when an
4921 * unqualified name is being accessed or when "name in xml" is called.
4923 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4924 * giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
4926 * NB: xml_deleteProperty must take care to remove any property added here.
4928 * FIXME This clashes with the function namespace implementation which also
4929 * uses native properties. Effectively after xml_lookupProperty any property
4930 * stored previously using assignments to xml.function::name will be removed.
4931 * We partially workaround the problem in js_GetXMLFunction. There we take
4932 * advantage of the fact that typically function:: is used to access the
4933 * functions from XML.prototype. So when js_GetProperty returns a non-function
4934 * property, we assume that it represents the result of GetProperty setter
4935 * hiding the function and use an extra prototype chain lookup to recover it.
4936 * For a proper solution see bug 355257.
4938 static JSBool
4939 xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4940 JSProperty **propp)
4942 jsval v;
4943 JSBool found;
4944 JSXML *xml;
4945 uint32 i;
4946 JSObject *qn;
4947 jsid funid;
4948 JSScopeProperty *sprop;
4950 v = ID_TO_VALUE(id);
4951 xml = (JSXML *) JS_GetPrivate(cx, obj);
4952 if (js_IdIsIndex(v, &i)) {
4953 found = HasIndexedProperty(xml, i);
4954 } else {
4955 qn = ToXMLName(cx, v, &funid);
4956 if (!qn)
4957 return JS_FALSE;
4958 if (funid)
4959 return js_LookupProperty(cx, obj, funid, objp, propp);
4960 found = HasNamedProperty(xml, qn);
4962 if (!found) {
4963 *objp = NULL;
4964 *propp = NULL;
4965 } else {
4966 sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
4967 SPROP_INVALID_SLOT, JSPROP_ENUMERATE,
4968 0, 0);
4969 if (!sprop)
4970 return JS_FALSE;
4972 JS_LOCK_OBJ(cx, obj);
4973 *objp = obj;
4974 *propp = (JSProperty *) sprop;
4976 return JS_TRUE;
4979 static JSBool
4980 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
4981 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
4982 JSProperty **propp)
4984 if (VALUE_IS_FUNCTION(cx, value) || getter || setter ||
4985 (attrs & JSPROP_ENUMERATE) == 0 ||
4986 (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
4987 return js_DefineProperty(cx, obj, id, value, getter, setter, attrs,
4988 propp);
4991 if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value))
4992 return JS_FALSE;
4993 if (propp)
4994 *propp = NULL;
4995 return JS_TRUE;
4998 static JSBool
4999 xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5001 if (id == JS_DEFAULT_XML_NAMESPACE_ID) {
5002 *vp = JSVAL_VOID;
5003 return JS_TRUE;
5006 return GetProperty(cx, obj, ID_TO_VALUE(id), vp);
5009 static JSBool
5010 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5012 return PutProperty(cx, obj, ID_TO_VALUE(id), vp);
5015 static JSBool
5016 FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5017 JSBool *foundp)
5019 if (!prop)
5020 return HasProperty(cx, obj, ID_TO_VALUE(id), foundp);
5022 *foundp = JS_TRUE;
5023 return JS_TRUE;
5026 static JSBool
5027 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5028 uintN *attrsp)
5030 JSBool found;
5032 if (!FoundProperty(cx, obj, id, prop, &found))
5033 return JS_FALSE;
5034 *attrsp = found ? JSPROP_ENUMERATE : 0;
5035 return JS_TRUE;
5038 static JSBool
5039 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5040 uintN *attrsp)
5042 JSBool found;
5044 if (!FoundProperty(cx, obj, id, prop, &found))
5045 return JS_FALSE;
5046 if (found) {
5047 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5048 JSMSG_CANT_SET_XML_ATTRS);
5050 return !found;
5053 static JSBool
5054 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
5056 JSXML *xml;
5057 jsval idval;
5058 uint32 index;
5059 JSObject *nameqn;
5060 jsid funid;
5062 idval = ID_TO_VALUE(id);
5063 xml = (JSXML *) JS_GetPrivate(cx, obj);
5064 if (js_IdIsIndex(idval, &index)) {
5065 if (xml->xml_class != JSXML_CLASS_LIST) {
5066 /* See NOTE in spec: this variation is reserved for future use. */
5067 ReportBadXMLName(cx, id);
5068 return JS_FALSE;
5071 /* ECMA-357 9.2.1.3. */
5072 DeleteListElement(cx, xml, index);
5073 } else {
5074 nameqn = ToXMLName(cx, idval, &funid);
5075 if (!nameqn)
5076 return JS_FALSE;
5077 if (funid)
5078 return js_DeleteProperty(cx, obj, funid, rval);
5080 DeleteNamedProperty(cx, xml, nameqn,
5081 OBJ_GET_CLASS(cx, nameqn) ==
5082 &js_AttributeNameClass);
5086 * If this object has its own (mutable) scope, then we may have added a
5087 * property to the scope in xml_lookupProperty for it to return to mean
5088 * "found" and to provide a handle for access operations to call the
5089 * property's getter or setter. But now it's time to remove any such
5090 * property, to purge the property cache and remove the scope entry.
5092 if (OBJ_SCOPE(obj)->object == obj && !js_DeleteProperty(cx, obj, id, rval))
5093 return JS_FALSE;
5095 *rval = JSVAL_TRUE;
5096 return JS_TRUE;
5099 static JSBool
5100 xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
5102 JSXML *xml;
5104 if (hint == JSTYPE_OBJECT) {
5105 /* Called from for..in code in js_Interpret: return an XMLList. */
5106 xml = (JSXML *) JS_GetPrivate(cx, obj);
5107 if (xml->xml_class != JSXML_CLASS_LIST) {
5108 obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj));
5109 if (!obj)
5110 return JS_FALSE;
5112 *vp = OBJECT_TO_JSVAL(obj);
5113 return JS_TRUE;
5116 return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp);
5119 static JSBool
5120 xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
5121 jsval *statep, jsid *idp)
5123 JSXML *xml;
5124 uint32 length, index;
5125 JSXMLArrayCursor *cursor;
5127 xml = (JSXML *) JS_GetPrivate(cx, obj);
5128 length = JSXML_LENGTH(xml);
5130 switch (enum_op) {
5131 case JSENUMERATE_INIT:
5132 if (length == 0) {
5133 cursor = NULL;
5134 } else {
5135 cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
5136 if (!cursor)
5137 return JS_FALSE;
5138 XMLArrayCursorInit(cursor, &xml->xml_kids);
5140 *statep = PRIVATE_TO_JSVAL(cursor);
5141 if (idp)
5142 *idp = INT_TO_JSID(length);
5143 break;
5145 case JSENUMERATE_NEXT:
5146 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5147 if (cursor && cursor->array && (index = cursor->index) < length) {
5148 *idp = INT_TO_JSID(index);
5149 cursor->index = index + 1;
5150 break;
5152 /* FALL THROUGH */
5154 case JSENUMERATE_DESTROY:
5155 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5156 if (cursor) {
5157 XMLArrayCursorFinish(cursor);
5158 JS_free(cx, cursor);
5160 *statep = JSVAL_NULL;
5161 break;
5163 return JS_TRUE;
5166 static JSBool
5167 xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
5169 return JS_TRUE;
5172 static void
5173 xml_trace(JSTracer *trc, JSObject *obj)
5175 JSXML *xml;
5177 xml = (JSXML *) JS_GetPrivate(trc->context, obj);
5178 if (xml)
5179 JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private");
5182 static void
5183 xml_clear(JSContext *cx, JSObject *obj)
5187 static JSBool
5188 HasSimpleContent(JSXML *xml)
5190 JSXML *kid;
5191 JSBool simple;
5192 uint32 i, n;
5194 again:
5195 switch (xml->xml_class) {
5196 case JSXML_CLASS_COMMENT:
5197 case JSXML_CLASS_PROCESSING_INSTRUCTION:
5198 return JS_FALSE;
5199 case JSXML_CLASS_LIST:
5200 if (xml->xml_kids.length == 0)
5201 return JS_TRUE;
5202 if (xml->xml_kids.length == 1) {
5203 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5204 if (kid) {
5205 xml = kid;
5206 goto again;
5209 /* FALL THROUGH */
5210 default:
5211 simple = JS_TRUE;
5212 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5213 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5214 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5215 simple = JS_FALSE;
5216 break;
5219 return simple;
5224 * 11.2.2.1 Step 3(d) onward.
5226 static JSObject *
5227 xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5229 JSTempValueRooter tvr;
5231 JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL));
5234 * As our callers have a bad habit of passing a pointer to an unrooted
5235 * local value as vp, we use a proper root here.
5237 JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
5238 if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value))
5239 obj = NULL;
5240 *vp = tvr.u.value;
5241 JS_POP_TEMP_ROOT(cx, &tvr);
5242 return obj;
5245 static JSBool
5246 xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5248 return js_SetProperty(cx, obj, id, vp);
5251 static JSBool
5252 xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
5253 jsval *statep, jsid *idp, jsval *vp)
5255 JSXML *xml, *kid;
5256 uint32 length, index;
5257 JSXMLArrayCursor *cursor;
5258 JSObject *kidobj;
5260 xml = (JSXML *) JS_GetPrivate(cx, obj);
5261 length = JSXML_LENGTH(xml);
5262 JS_ASSERT(INT_FITS_IN_JSVAL(length));
5264 switch (enum_op) {
5265 case JSENUMERATE_INIT:
5266 if (length == 0) {
5267 cursor = NULL;
5268 } else {
5269 cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
5270 if (!cursor)
5271 return JS_FALSE;
5272 XMLArrayCursorInit(cursor, &xml->xml_kids);
5274 *statep = PRIVATE_TO_JSVAL(cursor);
5275 if (idp)
5276 *idp = INT_TO_JSID(length);
5277 if (vp)
5278 *vp = JSVAL_VOID;
5279 break;
5281 case JSENUMERATE_NEXT:
5282 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5283 if (cursor && cursor->array && (index = cursor->index) < length) {
5284 while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) {
5285 if (++index == length)
5286 goto destroy;
5288 kidobj = js_GetXMLObject(cx, kid);
5289 if (!kidobj)
5290 return JS_FALSE;
5291 JS_ASSERT(INT_FITS_IN_JSVAL(index));
5292 *idp = INT_TO_JSID(index);
5293 *vp = OBJECT_TO_JSVAL(kidobj);
5294 cursor->index = index + 1;
5295 break;
5297 /* FALL THROUGH */
5299 case JSENUMERATE_DESTROY:
5300 cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
5301 if (cursor) {
5302 destroy:
5303 XMLArrayCursorFinish(cursor);
5304 JS_free(cx, cursor);
5306 *statep = JSVAL_NULL;
5307 break;
5309 return JS_TRUE;
5312 static JSBool
5313 xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
5315 JSXML *xml, *vxml;
5316 JSObject *vobj;
5317 JSBool ok;
5318 JSString *str, *vstr;
5319 jsdouble d, d2;
5321 xml = (JSXML *) JS_GetPrivate(cx, obj);
5322 vxml = NULL;
5323 if (!JSVAL_IS_PRIMITIVE(v)) {
5324 vobj = JSVAL_TO_OBJECT(v);
5325 if (OBJECT_IS_XML(cx, vobj))
5326 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
5329 if (xml->xml_class == JSXML_CLASS_LIST) {
5330 ok = Equals(cx, xml, v, bp);
5331 } else if (vxml) {
5332 if (vxml->xml_class == JSXML_CLASS_LIST) {
5333 ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
5334 } else {
5335 if (((xml->xml_class == JSXML_CLASS_TEXT ||
5336 xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5337 HasSimpleContent(vxml)) ||
5338 ((vxml->xml_class == JSXML_CLASS_TEXT ||
5339 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5340 HasSimpleContent(xml))) {
5341 ok = js_EnterLocalRootScope(cx);
5342 if (ok) {
5343 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5344 vstr = js_ValueToString(cx, v);
5345 ok = str && vstr;
5346 if (ok)
5347 *bp = js_EqualStrings(str, vstr);
5348 js_LeaveLocalRootScope(cx);
5350 } else {
5351 ok = XMLEquals(cx, xml, vxml, bp);
5354 } else {
5355 ok = js_EnterLocalRootScope(cx);
5356 if (ok) {
5357 if (HasSimpleContent(xml)) {
5358 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5359 vstr = js_ValueToString(cx, v);
5360 ok = str && vstr;
5361 if (ok)
5362 *bp = js_EqualStrings(str, vstr);
5363 } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
5364 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5365 if (!str) {
5366 ok = JS_FALSE;
5367 } else if (JSVAL_IS_STRING(v)) {
5368 *bp = js_EqualStrings(str, JSVAL_TO_STRING(v));
5369 } else {
5370 ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5371 if (ok) {
5372 d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5373 : *JSVAL_TO_DOUBLE(v);
5374 *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
5377 } else {
5378 *bp = JS_FALSE;
5380 js_LeaveLocalRootScope(cx);
5383 return ok;
5386 static JSBool
5387 xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp)
5389 JSBool ok;
5390 JSObject *listobj, *robj;
5391 JSXML *list, *lxml, *rxml;
5393 ok = js_EnterLocalRootScope(cx);
5394 if (!ok)
5395 return JS_FALSE;
5397 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5398 if (!listobj) {
5399 ok = JS_FALSE;
5400 goto out;
5403 list = (JSXML *) JS_GetPrivate(cx, listobj);
5404 lxml = (JSXML *) JS_GetPrivate(cx, obj);
5405 ok = Append(cx, list, lxml);
5406 if (!ok)
5407 goto out;
5409 if (VALUE_IS_XML(cx, v)) {
5410 rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5411 } else {
5412 robj = ToXML(cx, v);
5413 if (!robj) {
5414 ok = JS_FALSE;
5415 goto out;
5417 rxml = (JSXML *) JS_GetPrivate(cx, robj);
5419 ok = Append(cx, list, rxml);
5420 if (!ok)
5421 goto out;
5423 *vp = OBJECT_TO_JSVAL(listobj);
5424 out:
5425 js_LeaveLocalRootScopeWithResult(cx, *vp);
5426 return ok;
5429 /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */
5430 JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = {
5431 { js_NewObjectMap, js_DestroyObjectMap,
5432 xml_lookupProperty, xml_defineProperty,
5433 xml_getProperty, xml_setProperty,
5434 xml_getAttributes, xml_setAttributes,
5435 xml_deleteProperty, xml_defaultValue,
5436 xml_enumerate, js_CheckAccess,
5437 NULL, NULL,
5438 NULL, NULL,
5439 NULL, xml_hasInstance,
5440 js_SetProtoOrParent, js_SetProtoOrParent,
5441 js_TraceObject, xml_clear,
5442 NULL, NULL },
5443 xml_getMethod, xml_setMethod,
5444 xml_enumerateValues, xml_equality,
5445 xml_concatenate
5448 static JSObjectOps *
5449 xml_getObjectOps(JSContext *cx, JSClass *clasp)
5451 return &js_XMLObjectOps.base;
5454 JS_FRIEND_DATA(JSClass) js_XMLClass = {
5455 js_XML_str,
5456 JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
5457 JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
5458 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
5459 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize,
5460 xml_getObjectOps, NULL, NULL, NULL,
5461 NULL, NULL, JS_CLASS_TRACE(xml_trace), NULL
5464 static JSXML *
5465 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
5467 JSXML *xml;
5468 JSFunction *fun;
5469 char numBuf[12];
5471 JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
5473 *objp = JS_THIS_OBJECT(cx, vp);
5474 xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, vp + 2);
5475 if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5476 return xml;
5478 if (xml->xml_kids.length == 1) {
5479 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5480 if (xml) {
5481 *objp = js_GetXMLObject(cx, xml);
5482 if (!*objp)
5483 return NULL;
5484 vp[1] = OBJECT_TO_JSVAL(*objp);
5485 return xml;
5489 fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp));
5490 JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
5491 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5492 JSMSG_NON_LIST_XML_METHOD,
5493 JS_GetFunctionName(fun), numBuf);
5494 return NULL;
5497 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5498 #define XML_METHOD_PROLOG \
5499 JSObject *obj = JS_THIS_OBJECT(cx, vp); \
5500 JSXML *xml = (JSXML *)JS_GetInstancePrivate(cx, obj, &js_XMLClass, vp+2); \
5501 if (!xml) \
5502 return JS_FALSE
5504 #define NON_LIST_XML_METHOD_PROLOG \
5505 JSObject *obj; \
5506 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5507 if (!xml) \
5508 return JS_FALSE; \
5509 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5511 static JSBool
5512 xml_addNamespace(JSContext *cx, uintN argc, jsval *vp)
5514 JSObject *ns;
5516 NON_LIST_XML_METHOD_PROLOG;
5517 if (xml->xml_class != JSXML_CLASS_ELEMENT)
5518 goto done;
5519 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5520 if (!xml)
5521 return JS_FALSE;
5523 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
5524 return JS_FALSE;
5525 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5527 ns = JSVAL_TO_OBJECT(*vp);
5528 if (!AddInScopeNamespace(cx, xml, ns))
5529 return JS_FALSE;
5530 ns->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
5532 done:
5533 *vp = OBJECT_TO_JSVAL(obj);
5534 return JS_TRUE;
5537 static JSBool
5538 xml_appendChild(JSContext *cx, uintN argc, jsval *vp)
5540 jsval name, v;
5541 JSObject *vobj;
5542 JSXML *vxml;
5544 NON_LIST_XML_METHOD_PROLOG;
5545 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5546 if (!xml)
5547 return JS_FALSE;
5549 if (!js_GetAnyName(cx, &name))
5550 return JS_FALSE;
5552 if (!GetProperty(cx, obj, name, &v))
5553 return JS_FALSE;
5555 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5556 vobj = JSVAL_TO_OBJECT(v);
5557 JS_ASSERT(OBJECT_IS_XML(cx, vobj));
5558 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
5559 JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
5561 if (!IndexToIdVal(cx, vxml->xml_kids.length, &name))
5562 return JS_FALSE;
5563 *vp = (argc != 0) ? vp[2] : JSVAL_VOID;
5564 if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, vp))
5565 return JS_FALSE;
5567 *vp = OBJECT_TO_JSVAL(obj);
5568 return JS_TRUE;
5571 /* XML and XMLList */
5572 static JSBool
5573 xml_attribute(JSContext *cx, uintN argc, jsval *vp)
5575 JSObject *qn;
5577 if (argc == 0) {
5578 js_ReportMissingArg(cx, vp, 0);
5579 return JS_FALSE;
5582 qn = ToAttributeName(cx, vp[2]);
5583 if (!qn)
5584 return JS_FALSE;
5585 vp[2] = OBJECT_TO_JSVAL(qn); /* local root */
5587 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), vp[2], vp);
5590 /* XML and XMLList */
5591 static JSBool
5592 xml_attributes(JSContext *cx, uintN argc, jsval *vp)
5594 jsval name;
5595 JSObject *qn;
5596 JSTempValueRooter tvr;
5597 JSBool ok;
5599 name = ATOM_KEY(cx->runtime->atomState.starAtom);
5600 qn = ToAttributeName(cx, name);
5601 if (!qn)
5602 return JS_FALSE;
5603 name = OBJECT_TO_JSVAL(qn);
5604 JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr);
5605 ok = GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
5606 JS_POP_TEMP_ROOT(cx, &tvr);
5607 return ok;
5610 static JSXML *
5611 xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5613 JSObject *listobj;
5614 JSXML *list;
5616 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5617 if (!listobj)
5618 return NULL;
5620 *rval = OBJECT_TO_JSVAL(listobj);
5621 list = (JSXML *) JS_GetPrivate(cx, listobj);
5622 list->xml_target = xml;
5623 return list;
5626 static JSBool
5627 xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5628 jsval *rval)
5630 uint32 index;
5631 JSXML *kid;
5632 JSObject *kidobj;
5634 /* ECMA-357 13.4.4.6 */
5635 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5637 if (js_IdIsIndex(name, &index)) {
5638 if (index >= JSXML_LENGTH(xml)) {
5639 *rval = JSVAL_VOID;
5640 } else {
5641 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5642 if (!kid) {
5643 *rval = JSVAL_VOID;
5644 } else {
5645 kidobj = js_GetXMLObject(cx, kid);
5646 if (!kidobj)
5647 return JS_FALSE;
5648 *rval = OBJECT_TO_JSVAL(kidobj);
5651 return JS_TRUE;
5654 return GetProperty(cx, obj, name, rval);
5657 /* XML and XMLList */
5658 static JSBool
5659 xml_child(JSContext *cx, uintN argc, jsval *vp)
5661 jsval name, v;
5662 JSXML *list, *kid, *vxml;
5663 JSXMLArrayCursor cursor;
5664 JSObject *kidobj;
5666 XML_METHOD_PROLOG;
5667 name = argc != 0 ? vp[2] : JSVAL_VOID;
5668 if (xml->xml_class == JSXML_CLASS_LIST) {
5669 /* ECMA-357 13.5.4.4 */
5670 list = xml_list_helper(cx, xml, vp);
5671 if (!list)
5672 return JS_FALSE;
5674 XMLArrayCursorInit(&cursor, &xml->xml_kids);
5675 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
5676 kidobj = js_GetXMLObject(cx, kid);
5677 if (!kidobj)
5678 break;
5679 if (!xml_child_helper(cx, kidobj, kid, name, &v))
5680 break;
5681 if (JSVAL_IS_VOID(v)) {
5682 /* The property didn't exist in this kid. */
5683 continue;
5686 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5687 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5688 if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
5689 !Append(cx, list, vxml)) {
5690 break;
5693 XMLArrayCursorFinish(&cursor);
5694 return !kid;
5697 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5698 if (!xml_child_helper(cx, obj, xml, name, vp))
5699 return JS_FALSE;
5700 if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp))
5701 return JS_FALSE;
5702 return JS_TRUE;
5705 static JSBool
5706 xml_childIndex(JSContext *cx, uintN argc, jsval *vp)
5708 JSXML *parent;
5709 uint32 i, n;
5711 NON_LIST_XML_METHOD_PROLOG;
5712 parent = xml->parent;
5713 if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
5714 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
5715 return JS_TRUE;
5717 for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5718 if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5719 break;
5721 JS_ASSERT(i < n);
5722 return js_NewNumberInRootedValue(cx, i, vp);
5725 /* XML and XMLList */
5726 static JSBool
5727 xml_children(JSContext *cx, uintN argc, jsval *vp)
5729 jsval name;
5731 name = ATOM_KEY(cx->runtime->atomState.starAtom);
5732 return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
5735 /* XML and XMLList */
5736 static JSBool
5737 xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
5739 JSXML *list, *kid, *vxml;
5740 JSBool ok;
5741 uint32 i, n;
5742 JSObject *kidobj;
5743 jsval v;
5745 list = xml_list_helper(cx, xml, vp);
5746 if (!list)
5747 return JS_FALSE;
5749 ok = JS_TRUE;
5751 if (xml->xml_class == JSXML_CLASS_LIST) {
5752 /* 13.5.4.6 Step 2. */
5753 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5754 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5755 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5756 ok = js_EnterLocalRootScope(cx);
5757 if (!ok)
5758 break;
5759 kidobj = js_GetXMLObject(cx, kid);
5760 if (kidobj) {
5761 ok = xml_comments_helper(cx, kidobj, kid, &v);
5762 } else {
5763 ok = JS_FALSE;
5764 v = JSVAL_NULL;
5766 js_LeaveLocalRootScopeWithResult(cx, v);
5767 if (!ok)
5768 break;
5769 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5770 if (JSXML_LENGTH(vxml) != 0) {
5771 ok = Append(cx, list, vxml);
5772 if (!ok)
5773 break;
5777 } else {
5778 /* 13.4.4.9 Step 2. */
5779 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5780 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5781 if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
5782 ok = Append(cx, list, kid);
5783 if (!ok)
5784 break;
5789 return ok;
5792 static JSBool
5793 xml_comments(JSContext *cx, uintN argc, jsval *vp)
5795 XML_METHOD_PROLOG;
5796 return xml_comments_helper(cx, obj, xml, vp);
5799 /* XML and XMLList */
5800 static JSBool
5801 xml_contains(JSContext *cx, uintN argc, jsval *vp)
5803 jsval value;
5804 JSBool eq;
5805 JSXMLArrayCursor cursor;
5806 JSXML *kid;
5807 JSObject *kidobj;
5809 XML_METHOD_PROLOG;
5810 value = argc != 0 ? vp[2] : JSVAL_VOID;
5811 if (xml->xml_class == JSXML_CLASS_LIST) {
5812 eq = JS_FALSE;
5813 XMLArrayCursorInit(&cursor, &xml->xml_kids);
5814 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
5815 kidobj = js_GetXMLObject(cx, kid);
5816 if (!kidobj || !xml_equality(cx, kidobj, value, &eq))
5817 break;
5818 if (eq)
5819 break;
5821 XMLArrayCursorFinish(&cursor);
5822 if (kid && !eq)
5823 return JS_FALSE;
5824 } else {
5825 if (!xml_equality(cx, obj, value, &eq))
5826 return JS_FALSE;
5828 *vp = BOOLEAN_TO_JSVAL(eq);
5829 return JS_TRUE;
5832 /* XML and XMLList */
5833 static JSBool
5834 xml_copy(JSContext *cx, uintN argc, jsval *vp)
5836 JSXML *copy;
5838 XML_METHOD_PROLOG;
5839 copy = DeepCopy(cx, xml, NULL, 0);
5840 if (!copy)
5841 return JS_FALSE;
5842 *vp = OBJECT_TO_JSVAL(copy->object);
5843 return JS_TRUE;
5846 /* XML and XMLList */
5847 static JSBool
5848 xml_descendants(JSContext *cx, uintN argc, jsval *vp)
5850 jsval name;
5851 JSXML *list;
5853 XML_METHOD_PROLOG;
5854 name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2];
5855 list = Descendants(cx, xml, name);
5856 if (!list)
5857 return JS_FALSE;
5858 *vp = OBJECT_TO_JSVAL(list->object);
5859 return JS_TRUE;
5862 /* XML and XMLList */
5863 static JSBool
5864 xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml,
5865 JSObject *nameqn, jsval *vp)
5867 JSXML *list, *kid, *vxml;
5868 jsval v;
5869 JSBool ok;
5870 JSXMLArrayCursor cursor;
5871 JSObject *kidobj;
5872 uint32 i, n;
5874 list = xml_list_helper(cx, xml, vp);
5875 if (!list)
5876 return JS_FALSE;
5878 list->xml_targetprop = nameqn;
5879 ok = JS_TRUE;
5881 if (xml->xml_class == JSXML_CLASS_LIST) {
5882 /* 13.5.4.6 */
5883 XMLArrayCursorInit(&cursor, &xml->xml_kids);
5884 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
5885 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
5886 ok = js_EnterLocalRootScope(cx);
5887 if (!ok)
5888 break;
5889 kidobj = js_GetXMLObject(cx, kid);
5890 if (kidobj) {
5891 ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v);
5892 } else {
5893 ok = JS_FALSE;
5894 v = JSVAL_NULL;
5896 js_LeaveLocalRootScopeWithResult(cx, v);
5897 if (!ok)
5898 break;
5899 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5900 if (JSXML_LENGTH(vxml) != 0) {
5901 ok = Append(cx, list, vxml);
5902 if (!ok)
5903 break;
5907 XMLArrayCursorFinish(&cursor);
5908 } else {
5909 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5910 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5911 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
5912 MatchElemName(nameqn, kid)) {
5913 ok = Append(cx, list, kid);
5914 if (!ok)
5915 break;
5920 return ok;
5923 static JSBool
5924 xml_elements(JSContext *cx, uintN argc, jsval *vp)
5926 jsval name;
5927 JSObject *nameqn;
5928 jsid funid;
5930 XML_METHOD_PROLOG;
5932 name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2];
5933 nameqn = ToXMLName(cx, name, &funid);
5934 if (!nameqn)
5935 return JS_FALSE;
5936 vp[2] = OBJECT_TO_JSVAL(nameqn);
5938 if (funid)
5939 return xml_list_helper(cx, xml, vp) != NULL;
5941 return xml_elements_helper(cx, obj, xml, nameqn, vp);
5944 /* XML and XMLList */
5945 static JSBool
5946 xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
5948 JSObject *obj;
5949 jsval name;
5950 JSBool found;
5952 obj = JS_THIS_OBJECT(cx, vp);
5953 if (!JS_InstanceOf(cx, obj, &js_XMLClass, vp + 2))
5954 return JS_FALSE;
5956 name = argc != 0 ? vp[2] : JSVAL_VOID;
5957 if (!HasProperty(cx, obj, name, &found))
5958 return JS_FALSE;
5959 if (found) {
5960 *vp = JSVAL_TRUE;
5961 return JS_TRUE;
5963 return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, vp);
5966 /* XML and XMLList */
5967 static JSBool
5968 xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp)
5970 JSXML *kid;
5971 JSObject *kidobj;
5972 uint32 i, n;
5974 XML_METHOD_PROLOG;
5975 again:
5976 switch (xml->xml_class) {
5977 case JSXML_CLASS_ATTRIBUTE:
5978 case JSXML_CLASS_COMMENT:
5979 case JSXML_CLASS_PROCESSING_INSTRUCTION:
5980 case JSXML_CLASS_TEXT:
5981 *vp = JSVAL_FALSE;
5982 break;
5983 case JSXML_CLASS_LIST:
5984 if (xml->xml_kids.length == 0) {
5985 *vp = JSVAL_TRUE;
5986 } else if (xml->xml_kids.length == 1) {
5987 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5988 if (kid) {
5989 kidobj = js_GetXMLObject(cx, kid);
5990 if (!kidobj)
5991 return JS_FALSE;
5992 obj = kidobj;
5993 xml = (JSXML *) JS_GetPrivate(cx, obj);
5994 goto again;
5997 /* FALL THROUGH */
5998 default:
5999 *vp = JSVAL_FALSE;
6000 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6001 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6002 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6003 *vp = JSVAL_TRUE;
6004 break;
6007 break;
6009 return JS_TRUE;
6012 /* XML and XMLList */
6013 static JSBool
6014 xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp)
6016 XML_METHOD_PROLOG;
6017 *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
6018 return JS_TRUE;
6021 typedef struct JSTempRootedNSArray {
6022 JSTempValueRooter tvr;
6023 JSXMLArray array;
6024 jsval value; /* extra root for temporaries */
6025 } JSTempRootedNSArray;
6027 static void
6028 TraceObjectVector(JSTracer *trc, JSObject **vec, uint32 len)
6030 uint32 i;
6031 JSObject *obj;
6033 for (i = 0; i < len; i++) {
6034 obj = vec[i];
6035 if (obj) {
6036 JS_SET_TRACING_INDEX(trc, "vector", i);
6037 JS_CallTracer(trc, obj, JSTRACE_OBJECT);
6042 static void
6043 trace_temp_ns_array(JSTracer *trc, JSTempValueRooter *tvr)
6045 JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr;
6047 TraceObjectVector(trc,
6048 (JSObject **) tmp->array.vector,
6049 tmp->array.length);
6050 XMLArrayCursorTrace(trc, tmp->array.cursors);
6051 JS_CALL_VALUE_TRACER(trc, tmp->value, "temp_ns_array_value");
6054 static void
6055 InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
6057 XMLArrayInit(cx, &tmp->array, 0);
6058 tmp->value = JSVAL_NULL;
6059 JS_PUSH_TEMP_ROOT_TRACE(cx, trace_temp_ns_array, &tmp->tvr);
6062 static void
6063 FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
6065 JS_ASSERT(tmp->tvr.u.trace == trace_temp_ns_array);
6066 JS_POP_TEMP_ROOT(cx, &tmp->tvr);
6067 XMLArrayFinish(cx, &tmp->array);
6071 * Populate a new JS array with elements of JSTempRootedNSArray.array and
6072 * place the result into rval. rval must point to a rooted location.
6074 static JSBool
6075 TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval)
6077 JSObject *arrayobj;
6078 uint32 i, n;
6079 JSObject *ns;
6081 arrayobj = js_NewArrayObject(cx, 0, NULL);
6082 if (!arrayobj)
6083 return JS_FALSE;
6084 *rval = OBJECT_TO_JSVAL(arrayobj);
6085 for (i = 0, n = tmp->array.length; i < n; i++) {
6086 ns = XMLARRAY_MEMBER(&tmp->array, i, JSObject);
6087 if (!ns)
6088 continue;
6089 tmp->value = OBJECT_TO_JSVAL(ns);
6090 if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value))
6091 return JS_FALSE;
6093 return JS_TRUE;
6096 static JSBool
6097 FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
6099 uint32 length, i, j, n;
6100 JSObject *ns, *ns2;
6101 JSString *prefix, *prefix2;
6103 length = nsarray->length;
6104 do {
6105 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6106 continue;
6107 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6108 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
6109 if (!ns)
6110 continue;
6112 prefix = GetPrefix(ns);
6113 for (j = 0; j < length; j++) {
6114 ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject);
6115 if (ns2) {
6116 prefix2 = GetPrefix(ns2);
6117 if ((prefix2 && prefix)
6118 ? js_EqualStrings(prefix2, prefix)
6119 : js_EqualStrings(GetURI(ns2), GetURI(ns))) {
6120 break;
6125 if (j == length) {
6126 if (!XMLARRAY_APPEND(cx, nsarray, ns))
6127 return JS_FALSE;
6128 ++length;
6131 } while ((xml = xml->parent) != NULL);
6132 JS_ASSERT(length == nsarray->length);
6134 return JS_TRUE;
6137 static JSBool
6138 xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp)
6140 JSTempRootedNSArray namespaces;
6141 JSBool ok;
6143 NON_LIST_XML_METHOD_PROLOG;
6145 InitTempNSArray(cx, &namespaces);
6146 ok = FindInScopeNamespaces(cx, xml, &namespaces.array) &&
6147 TempNSArrayToJSArray(cx, &namespaces, vp);
6148 FinishTempNSArray(cx, &namespaces);
6149 return ok;
6152 static JSBool
6153 xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp)
6155 jsval arg;
6156 JSXML *kid;
6157 uint32 i;
6159 NON_LIST_XML_METHOD_PROLOG;
6160 *vp = OBJECT_TO_JSVAL(obj);
6161 if (!JSXML_HAS_KIDS(xml) || argc == 0)
6162 return JS_TRUE;
6164 arg = vp[2];
6165 if (JSVAL_IS_NULL(arg)) {
6166 kid = NULL;
6167 i = 0;
6168 } else {
6169 if (!VALUE_IS_XML(cx, arg))
6170 return JS_TRUE;
6171 kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
6172 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
6173 if (i == XML_NOT_FOUND)
6174 return JS_TRUE;
6175 ++i;
6178 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6179 if (!xml)
6180 return JS_FALSE;
6181 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
6184 static JSBool
6185 xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp)
6187 jsval arg;
6188 JSXML *kid;
6189 uint32 i;
6191 NON_LIST_XML_METHOD_PROLOG;
6192 *vp = OBJECT_TO_JSVAL(obj);
6193 if (!JSXML_HAS_KIDS(xml) || argc == 0)
6194 return JS_TRUE;
6196 arg = vp[2];
6197 if (JSVAL_IS_NULL(arg)) {
6198 kid = NULL;
6199 i = xml->xml_kids.length;
6200 } else {
6201 if (!VALUE_IS_XML(cx, arg))
6202 return JS_TRUE;
6203 kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
6204 i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
6205 if (i == XML_NOT_FOUND)
6206 return JS_TRUE;
6209 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6210 if (!xml)
6211 return JS_FALSE;
6212 return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
6215 /* XML and XMLList */
6216 static JSBool
6217 xml_length(JSContext *cx, uintN argc, jsval *vp)
6219 XML_METHOD_PROLOG;
6220 if (xml->xml_class != JSXML_CLASS_LIST) {
6221 *vp = JSVAL_ONE;
6222 } else {
6223 if (!js_NewNumberInRootedValue(cx, xml->xml_kids.length, vp))
6224 return JS_FALSE;
6226 return JS_TRUE;
6229 static JSBool
6230 xml_localName(JSContext *cx, uintN argc, jsval *vp)
6232 NON_LIST_XML_METHOD_PROLOG;
6233 *vp = xml->name ? xml->name->fslots[JSSLOT_LOCAL_NAME] : JSVAL_NULL;
6234 return JS_TRUE;
6237 static JSBool
6238 xml_name(JSContext *cx, uintN argc, jsval *vp)
6240 NON_LIST_XML_METHOD_PROLOG;
6241 *vp = OBJECT_TO_JSVAL(xml->name);
6242 return JS_TRUE;
6245 static JSBool
6246 xml_namespace(JSContext *cx, uintN argc, jsval *vp)
6248 JSString *prefix, *nsprefix;
6249 JSTempRootedNSArray inScopeNSes;
6250 JSBool ok;
6251 jsuint i, length;
6252 JSObject *ns;
6254 NON_LIST_XML_METHOD_PROLOG;
6255 if (argc == 0 && !JSXML_HAS_NAME(xml)) {
6256 *vp = JSVAL_NULL;
6257 return JS_TRUE;
6260 if (argc == 0) {
6261 prefix = NULL;
6262 } else {
6263 prefix = js_ValueToString(cx, vp[2]);
6264 if (!prefix)
6265 return JS_FALSE;
6266 vp[2] = STRING_TO_JSVAL(prefix); /* local root */
6269 InitTempNSArray(cx, &inScopeNSes);
6270 MUST_FLOW_THROUGH("out");
6271 ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array);
6272 if (!ok)
6273 goto out;
6275 if (!prefix) {
6276 ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
6277 if (!ns) {
6278 ok = JS_FALSE;
6279 goto out;
6281 } else {
6282 ns = NULL;
6283 for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
6284 ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject);
6285 if (ns) {
6286 nsprefix = GetPrefix(ns);
6287 if (nsprefix && js_EqualStrings(nsprefix, prefix))
6288 break;
6289 ns = NULL;
6294 *vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns);
6296 out:
6297 FinishTempNSArray(cx, &inScopeNSes);
6298 return JS_TRUE;
6301 static JSBool
6302 xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp)
6304 JSBool ok;
6305 JSTempRootedNSArray ancestors, declared;
6306 JSXML *yml;
6307 uint32 i, n;
6308 JSObject *ns;
6310 NON_LIST_XML_METHOD_PROLOG;
6311 if (JSXML_HAS_VALUE(xml))
6312 return JS_TRUE;
6314 /* From here, control flow must goto out to finish these arrays. */
6315 ok = JS_TRUE;
6316 InitTempNSArray(cx, &ancestors);
6317 InitTempNSArray(cx, &declared);
6318 yml = xml;
6320 while ((yml = yml->parent) != NULL) {
6321 JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
6322 for (i = 0, n = yml->xml_namespaces.length; i < n; i++) {
6323 ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
6324 if (ns &&
6325 !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6326 ok = XMLARRAY_APPEND(cx, &ancestors.array, ns);
6327 if (!ok)
6328 goto out;
6333 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6334 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
6335 if (!ns)
6336 continue;
6337 if (!IsDeclared(ns))
6338 continue;
6339 if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6340 ok = XMLARRAY_APPEND(cx, &declared.array, ns);
6341 if (!ok)
6342 goto out;
6346 ok = TempNSArrayToJSArray(cx, &declared, vp);
6348 out:
6349 /* Finishing must be in reverse order of initialization to follow LIFO. */
6350 FinishTempNSArray(cx, &declared);
6351 FinishTempNSArray(cx, &ancestors);
6352 return ok;
6355 static const char js_attribute_str[] = "attribute";
6356 static const char js_text_str[] = "text";
6358 /* Exported to jsgc.c #ifdef DEBUG. */
6359 const char *js_xml_class_str[] = {
6360 "list",
6361 "element",
6362 js_attribute_str,
6363 "processing-instruction",
6364 js_text_str,
6365 "comment"
6368 static JSBool
6369 xml_nodeKind(JSContext *cx, uintN argc, jsval *vp)
6371 JSString *str;
6373 NON_LIST_XML_METHOD_PROLOG;
6374 str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6375 if (!str)
6376 return JS_FALSE;
6377 *vp = STRING_TO_JSVAL(str);
6378 return JS_TRUE;
6381 static void
6382 NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index)
6384 if (xml->xml_class == JSXML_CLASS_LIST)
6385 DeleteListElement(cx, xml, index);
6386 else
6387 DeleteByIndex(cx, xml, index);
6390 /* XML and XMLList */
6391 static JSBool
6392 xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml)
6394 JSXML *kid, *kid2;
6395 uint32 i, n;
6396 JSObject *kidobj;
6397 JSString *str;
6399 if (!JSXML_HAS_KIDS(xml))
6400 return JS_TRUE;
6402 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6403 if (!xml)
6404 return JS_FALSE;
6406 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6407 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6408 if (!kid)
6409 continue;
6410 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6411 kidobj = js_GetXMLObject(cx, kid);
6412 if (!kidobj || !xml_normalize_helper(cx, kidobj, kid))
6413 return JS_FALSE;
6414 } else if (kid->xml_class == JSXML_CLASS_TEXT) {
6415 while (i + 1 < n &&
6416 (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
6417 kid2->xml_class == JSXML_CLASS_TEXT) {
6418 str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
6419 if (!str)
6420 return JS_FALSE;
6421 NormalizingDelete(cx, xml, i + 1);
6422 n = xml->xml_kids.length;
6423 kid->xml_value = str;
6425 if (IS_EMPTY(kid->xml_value)) {
6426 NormalizingDelete(cx, xml, i);
6427 n = xml->xml_kids.length;
6428 --i;
6433 return JS_TRUE;
6436 static JSBool
6437 xml_normalize(JSContext *cx, uintN argc, jsval *vp)
6439 XML_METHOD_PROLOG;
6440 *vp = OBJECT_TO_JSVAL(obj);
6441 return xml_normalize_helper(cx, obj, xml);
6444 /* XML and XMLList */
6445 static JSBool
6446 xml_parent(JSContext *cx, uintN argc, jsval *vp)
6448 JSXML *parent, *kid;
6449 uint32 i, n;
6450 JSObject *parentobj;
6452 XML_METHOD_PROLOG;
6453 parent = xml->parent;
6454 if (xml->xml_class == JSXML_CLASS_LIST) {
6455 *vp = JSVAL_VOID;
6456 n = xml->xml_kids.length;
6457 if (n == 0)
6458 return JS_TRUE;
6460 kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6461 if (!kid)
6462 return JS_TRUE;
6463 parent = kid->parent;
6464 for (i = 1; i < n; i++) {
6465 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6466 if (kid && kid->parent != parent)
6467 return JS_TRUE;
6471 if (!parent) {
6472 *vp = JSVAL_NULL;
6473 return JS_TRUE;
6476 parentobj = js_GetXMLObject(cx, parent);
6477 if (!parentobj)
6478 return JS_FALSE;
6479 *vp = OBJECT_TO_JSVAL(parentobj);
6480 return JS_TRUE;
6483 /* XML and XMLList */
6484 static JSBool
6485 xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6486 JSObject *nameqn, jsval *vp)
6488 JSXML *list, *kid, *vxml;
6489 JSBool ok;
6490 JSXMLArrayCursor cursor;
6491 JSObject *kidobj;
6492 jsval v;
6493 uint32 i, n;
6494 JSString *localName;
6496 list = xml_list_helper(cx, xml, vp);
6497 if (!list)
6498 return JS_FALSE;
6500 list->xml_targetprop = nameqn;
6501 ok = JS_TRUE;
6503 if (xml->xml_class == JSXML_CLASS_LIST) {
6504 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6505 XMLArrayCursorInit(&cursor, &xml->xml_kids);
6506 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
6507 if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6508 ok = js_EnterLocalRootScope(cx);
6509 if (!ok)
6510 break;
6511 kidobj = js_GetXMLObject(cx, kid);
6512 if (kidobj) {
6513 ok = xml_processingInstructions_helper(cx, kidobj, kid,
6514 nameqn, &v);
6515 } else {
6516 ok = JS_FALSE;
6517 v = JSVAL_NULL;
6519 js_LeaveLocalRootScopeWithResult(cx, v);
6520 if (!ok)
6521 break;
6522 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
6523 if (JSXML_LENGTH(vxml) != 0) {
6524 ok = Append(cx, list, vxml);
6525 if (!ok)
6526 break;
6530 XMLArrayCursorFinish(&cursor);
6531 } else {
6532 /* 13.4.4.28 Step 4. */
6533 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6534 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6535 if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
6536 localName = GetLocalName(nameqn);
6537 if (IS_STAR(localName) ||
6538 js_EqualStrings(localName, GetLocalName(kid->name))) {
6539 ok = Append(cx, list, kid);
6540 if (!ok)
6541 break;
6547 return ok;
6550 static JSBool
6551 xml_processingInstructions(JSContext *cx, uintN argc, jsval *vp)
6553 jsval name;
6554 JSObject *nameqn;
6555 jsid funid;
6557 XML_METHOD_PROLOG;
6559 name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2];
6560 nameqn = ToXMLName(cx, name, &funid);
6561 if (!nameqn)
6562 return JS_FALSE;
6563 vp[2] = OBJECT_TO_JSVAL(nameqn);
6565 if (funid)
6566 return xml_list_helper(cx, xml, vp) != NULL;
6568 return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp);
6571 static JSBool
6572 xml_prependChild(JSContext *cx, uintN argc, jsval *vp)
6574 NON_LIST_XML_METHOD_PROLOG;
6575 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6576 if (!xml)
6577 return JS_FALSE;
6578 *vp = OBJECT_TO_JSVAL(obj);
6579 return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID);
6582 /* XML and XMLList */
6583 static JSBool
6584 xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
6586 uint32 index;
6588 XML_METHOD_PROLOG;
6589 *vp = JSVAL_FALSE;
6590 if (argc != 0 && js_IdIsIndex(vp[2], &index)) {
6591 if (xml->xml_class == JSXML_CLASS_LIST) {
6592 /* 13.5.4.18. */
6593 *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6594 } else {
6595 /* 13.4.4.30. */
6596 *vp = BOOLEAN_TO_JSVAL(index == 0);
6599 return JS_TRUE;
6602 static JSBool
6603 namespace_full_match(const void *a, const void *b)
6605 const JSObject *nsa = (const JSObject *) a;
6606 const JSObject *nsb = (const JSObject *) b;
6607 JSString *prefixa = GetPrefix(nsa);
6608 JSString *prefixb;
6610 if (prefixa) {
6611 prefixb = GetPrefix(nsb);
6612 if (prefixb && !js_EqualStrings(prefixa, prefixb))
6613 return JS_FALSE;
6615 return js_EqualStrings(GetURI(nsa), GetURI(nsb));
6618 static JSBool
6619 xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns)
6621 JSObject *thisns, *attrns;
6622 uint32 i, n;
6623 JSXML *attr, *kid;
6625 thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6626 JS_ASSERT(thisns);
6627 if (thisns == ns)
6628 return JS_TRUE;
6630 for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6631 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6632 if (!attr)
6633 continue;
6634 attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
6635 JS_ASSERT(attrns);
6636 if (attrns == ns)
6637 return JS_TRUE;
6640 i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
6641 if (i != XML_NOT_FOUND)
6642 XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
6644 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6645 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6646 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6647 if (!xml_removeNamespace_helper(cx, kid, ns))
6648 return JS_FALSE;
6651 return JS_TRUE;
6654 static JSBool
6655 xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp)
6657 JSObject *ns;
6659 NON_LIST_XML_METHOD_PROLOG;
6660 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6661 goto done;
6662 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6663 if (!xml)
6664 return JS_FALSE;
6666 if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
6667 return JS_FALSE;
6668 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6669 ns = JSVAL_TO_OBJECT(*vp);
6671 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6672 if (!xml_removeNamespace_helper(cx, xml, ns))
6673 return JS_FALSE;
6674 done:
6675 *vp = OBJECT_TO_JSVAL(obj);
6676 return JS_TRUE;
6679 static JSBool
6680 xml_replace(JSContext *cx, uintN argc, jsval *vp)
6682 jsval value;
6683 JSXML *vxml, *kid;
6684 uint32 index, i;
6685 JSObject *nameqn;
6687 NON_LIST_XML_METHOD_PROLOG;
6688 if (xml->xml_class != JSXML_CLASS_ELEMENT)
6689 goto done;
6691 if (argc <= 1) {
6692 value = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.
6693 typeAtoms[JSTYPE_VOID]));
6694 } else {
6695 value = vp[3];
6696 vxml = VALUE_IS_XML(cx, value)
6697 ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value))
6698 : NULL;
6699 if (!vxml) {
6700 if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
6701 return JS_FALSE;
6702 value = vp[3];
6703 } else {
6704 vxml = DeepCopy(cx, vxml, NULL, 0);
6705 if (!vxml)
6706 return JS_FALSE;
6707 value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
6711 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6712 if (!xml)
6713 return JS_FALSE;
6715 if (argc == 0 || !js_IdIsIndex(vp[2], &index)) {
6717 * Call function QName per spec, not ToXMLName, to avoid attribute
6718 * names.
6720 if (!QNameHelper(cx, NULL, &js_QNameClass.base, argc == 0 ? -1 : 1,
6721 vp + 2, vp)) {
6722 return JS_FALSE;
6724 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6725 nameqn = JSVAL_TO_OBJECT(*vp);
6727 i = xml->xml_kids.length;
6728 index = XML_NOT_FOUND;
6729 while (i != 0) {
6730 --i;
6731 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6732 if (kid && MatchElemName(nameqn, kid)) {
6733 if (i != XML_NOT_FOUND)
6734 DeleteByIndex(cx, xml, i);
6735 index = i;
6739 if (index == XML_NOT_FOUND)
6740 goto done;
6743 if (!Replace(cx, xml, index, value))
6744 return JS_FALSE;
6746 done:
6747 *vp = OBJECT_TO_JSVAL(obj);
6748 return JS_TRUE;
6751 static JSBool
6752 xml_setChildren(JSContext *cx, uintN argc, jsval *vp)
6754 JSObject *obj;
6756 if (!StartNonListXMLMethod(cx, vp, &obj))
6757 return JS_FALSE;
6759 *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */
6760 if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), vp))
6761 return JS_FALSE;
6763 *vp = OBJECT_TO_JSVAL(obj);
6764 return JS_TRUE;
6767 static JSBool
6768 xml_setLocalName(JSContext *cx, uintN argc, jsval *vp)
6770 jsval name;
6771 JSObject *nameqn;
6772 JSString *namestr;
6774 NON_LIST_XML_METHOD_PROLOG;
6775 if (!JSXML_HAS_NAME(xml))
6776 return JS_TRUE;
6778 if (argc == 0) {
6779 namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6780 } else {
6781 name = vp[2];
6782 if (!JSVAL_IS_PRIMITIVE(name) &&
6783 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) {
6784 nameqn = JSVAL_TO_OBJECT(name);
6785 namestr = GetLocalName(nameqn);
6786 } else {
6787 if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
6788 return JS_FALSE;
6789 name = vp[2];
6790 namestr = JSVAL_TO_STRING(name);
6794 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6795 if (!xml)
6796 return JS_FALSE;
6797 xml->name->fslots[JSSLOT_LOCAL_NAME] = namestr
6798 ? STRING_TO_JSVAL(namestr)
6799 : JSVAL_VOID;
6800 return JS_TRUE;
6803 static JSBool
6804 xml_setName(JSContext *cx, uintN argc, jsval *vp)
6806 jsval name;
6807 JSObject *nameqn;
6808 JSXML *nsowner;
6809 JSXMLArray *nsarray;
6810 uint32 i, n;
6811 JSObject *ns;
6813 NON_LIST_XML_METHOD_PROLOG;
6814 if (!JSXML_HAS_NAME(xml))
6815 return JS_TRUE;
6817 if (argc == 0) {
6818 name = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.
6819 typeAtoms[JSTYPE_VOID]));
6820 } else {
6821 name = vp[2];
6822 if (!JSVAL_IS_PRIMITIVE(name) &&
6823 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base &&
6824 !GetURI(nameqn = JSVAL_TO_OBJECT(name))) {
6825 name = vp[2] = nameqn->fslots[JSSLOT_LOCAL_NAME];
6829 nameqn = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name);
6830 if (!nameqn)
6831 return JS_FALSE;
6833 /* ECMA-357 13.4.4.35 Step 4. */
6834 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6835 nameqn->fslots[JSSLOT_URI] = STRING_TO_JSVAL(cx->runtime->emptyString);
6837 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6838 if (!xml)
6839 return JS_FALSE;
6840 xml->name = nameqn;
6843 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6844 * in-scope namespaces, either by finding an in-scope namespace with a
6845 * matching uri and setting the new name's prefix to that namespace's
6846 * prefix, or by extending the in-scope namespaces for xml (which are in
6847 * xml->parent if xml is an attribute or a PI).
6849 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6850 nsowner = xml;
6851 } else {
6852 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6853 return JS_TRUE;
6854 nsowner = xml->parent;
6857 if (GetPrefix(nameqn)) {
6859 * The name being set has a prefix, which originally came from some
6860 * namespace object (which may be the null namespace, where both the
6861 * prefix and uri are the empty string). We must go through a full
6862 * GetNamespace in case that namespace is in-scope in nsowner.
6864 * If we find such an in-scope namespace, we return true right away,
6865 * in this block. Otherwise, we fall through to the final return of
6866 * AddInScopeNamespace(cx, nsowner, ns).
6868 ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
6869 if (!ns)
6870 return JS_FALSE;
6872 /* XXXbe have to test membership to see whether GetNamespace added */
6873 if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
6874 return JS_TRUE;
6875 } else {
6877 * At this point, we know prefix of nameqn is null, so its uri can't
6878 * be the empty string (the null namespace always uses the empty string
6879 * for both prefix and uri).
6881 * This means we must inline GetNamespace and specialize it to match
6882 * uri only, never prefix. If we find a namespace with nameqn's uri
6883 * already in nsowner->xml_namespaces, then all that we need do is set
6884 * prefix of nameqn to that namespace's prefix.
6886 * If no such namespace exists, we can create one without going through
6887 * the constructor, because we know uri of nameqn is non-empty (so
6888 * prefix does not need to be converted from null to empty by QName).
6890 JS_ASSERT(!IS_EMPTY(GetURI(nameqn)));
6892 nsarray = &nsowner->xml_namespaces;
6893 for (i = 0, n = nsarray->length; i < n; i++) {
6894 ns = XMLARRAY_MEMBER(nsarray, i, JSObject);
6895 if (ns && js_EqualStrings(GetURI(ns), GetURI(nameqn))) {
6896 nameqn->fslots[JSSLOT_PREFIX] = ns->fslots[JSSLOT_PREFIX];
6897 return JS_TRUE;
6901 ns = NewXMLNamespace(cx, NULL, GetURI(nameqn), JS_TRUE);
6902 if (!ns)
6903 return JS_FALSE;
6906 if (!AddInScopeNamespace(cx, nsowner, ns))
6907 return JS_FALSE;
6908 vp[0] = JSVAL_VOID;
6909 return JS_TRUE;
6912 static JSBool
6913 xml_setNamespace(JSContext *cx, uintN argc, jsval *vp)
6915 JSObject *qn;
6916 JSObject *ns;
6917 jsval qnargv[2];
6918 JSXML *nsowner;
6920 NON_LIST_XML_METHOD_PROLOG;
6921 if (!JSXML_HAS_NAME(xml))
6922 return JS_TRUE;
6924 xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6925 if (!xml)
6926 return JS_FALSE;
6928 ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj,
6929 argc == 0 ? 0 : 1, vp + 2);
6930 if (!ns)
6931 return JS_FALSE;
6932 vp[0] = OBJECT_TO_JSVAL(ns);
6933 ns->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
6935 qnargv[0] = vp[2] = OBJECT_TO_JSVAL(ns);
6936 qnargv[1] = OBJECT_TO_JSVAL(xml->name);
6937 qn = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv);
6938 if (!qn)
6939 return JS_FALSE;
6941 xml->name = qn;
6944 * Erratum: the spec fails to update the governing in-scope namespaces.
6945 * See the erratum noted in xml_setName, above.
6947 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6948 nsowner = xml;
6949 } else {
6950 if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6951 return JS_TRUE;
6952 nsowner = xml->parent;
6954 if (!AddInScopeNamespace(cx, nsowner, ns))
6955 return JS_FALSE;
6956 vp[0] = JSVAL_VOID;
6957 return JS_TRUE;
6960 /* XML and XMLList */
6961 static JSBool
6962 xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
6964 JSXML *list, *kid, *vxml;
6965 uint32 i, n;
6966 JSBool ok;
6967 JSObject *kidobj;
6968 jsval v;
6970 list = xml_list_helper(cx, xml, vp);
6971 if (!list)
6972 return JS_FALSE;
6974 if (xml->xml_class == JSXML_CLASS_LIST) {
6975 ok = JS_TRUE;
6976 for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6977 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6978 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6979 ok = js_EnterLocalRootScope(cx);
6980 if (!ok)
6981 break;
6982 kidobj = js_GetXMLObject(cx, kid);
6983 if (kidobj) {
6984 ok = xml_text_helper(cx, kidobj, kid, &v);
6985 } else {
6986 ok = JS_FALSE;
6987 v = JSVAL_NULL;
6989 js_LeaveLocalRootScopeWithResult(cx, v);
6990 if (!ok)
6991 return JS_FALSE;
6992 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
6993 if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
6994 return JS_FALSE;
6997 } else {
6998 for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6999 kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
7000 if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
7001 if (!Append(cx, list, kid))
7002 return JS_FALSE;
7006 return JS_TRUE;
7009 static JSBool
7010 xml_text(JSContext *cx, uintN argc, jsval *vp)
7012 XML_METHOD_PROLOG;
7013 return xml_text_helper(cx, obj, xml, vp);
7016 /* XML and XMLList */
7017 static JSString *
7018 xml_toString_helper(JSContext *cx, JSXML *xml)
7020 JSString *str, *kidstr;
7021 JSXML *kid;
7022 JSXMLArrayCursor cursor;
7024 if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
7025 xml->xml_class == JSXML_CLASS_TEXT) {
7026 return xml->xml_value;
7029 if (!HasSimpleContent(xml))
7030 return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0);
7032 str = cx->runtime->emptyString;
7033 if (!js_EnterLocalRootScope(cx))
7034 return NULL;
7035 XMLArrayCursorInit(&cursor, &xml->xml_kids);
7036 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
7037 if (kid->xml_class != JSXML_CLASS_COMMENT &&
7038 kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
7039 kidstr = xml_toString_helper(cx, kid);
7040 if (!kidstr) {
7041 str = NULL;
7042 break;
7044 str = js_ConcatStrings(cx, str, kidstr);
7045 if (!str)
7046 break;
7049 XMLArrayCursorFinish(&cursor);
7050 js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
7051 return str;
7054 static JSBool
7055 xml_toSource(JSContext *cx, uintN argc, jsval *vp)
7057 jsval thisv;
7058 JSString *str;
7060 thisv = JS_THIS(cx, vp);
7061 if (JSVAL_IS_NULL(thisv))
7062 return JS_FALSE;
7063 str = ToXMLString(cx, thisv, TO_SOURCE_FLAG);
7064 if (!str)
7065 return JS_FALSE;
7066 *vp = STRING_TO_JSVAL(str);
7067 return JS_TRUE;
7070 static JSBool
7071 xml_toString(JSContext *cx, uintN argc, jsval *vp)
7073 JSString *str;
7075 XML_METHOD_PROLOG;
7076 str = xml_toString_helper(cx, xml);
7077 if (!str)
7078 return JS_FALSE;
7079 *vp = STRING_TO_JSVAL(str);
7080 return JS_TRUE;
7083 /* XML and XMLList */
7084 static JSBool
7085 xml_toXMLString(JSContext *cx, uintN argc, jsval *vp)
7087 jsval thisv;
7088 JSString *str;
7090 thisv = JS_THIS(cx, vp);
7091 if (JSVAL_IS_NULL(thisv))
7092 return JS_FALSE;
7093 str = ToXMLString(cx, thisv, 0);
7094 if (!str)
7095 return JS_FALSE;
7096 *vp = STRING_TO_JSVAL(str);
7097 return JS_TRUE;
7100 /* XML and XMLList */
7101 static JSBool
7102 xml_valueOf(JSContext *cx, uintN argc, jsval *vp)
7104 *vp = JS_THIS(cx, vp);
7105 return !JSVAL_IS_NULL(*vp);
7108 static JSFunctionSpec xml_methods[] = {
7109 JS_FN("addNamespace", xml_addNamespace, 1,0),
7110 JS_FN("appendChild", xml_appendChild, 1,0),
7111 JS_FN(js_attribute_str, xml_attribute, 1,0),
7112 JS_FN("attributes", xml_attributes, 0,0),
7113 JS_FN("child", xml_child, 1,0),
7114 JS_FN("childIndex", xml_childIndex, 0,0),
7115 JS_FN("children", xml_children, 0,0),
7116 JS_FN("comments", xml_comments, 0,0),
7117 JS_FN("contains", xml_contains, 1,0),
7118 JS_FN("copy", xml_copy, 0,0),
7119 JS_FN("descendants", xml_descendants, 1,0),
7120 JS_FN("elements", xml_elements, 1,0),
7121 JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0),
7122 JS_FN("hasComplexContent", xml_hasComplexContent, 1,0),
7123 JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0),
7124 JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0),
7125 JS_FN("insertChildAfter", xml_insertChildAfter, 2,0),
7126 JS_FN("insertChildBefore", xml_insertChildBefore, 2,0),
7127 JS_FN(js_length_str, xml_length, 0,0),
7128 JS_FN(js_localName_str, xml_localName, 0,0),
7129 JS_FN(js_name_str, xml_name, 0,0),
7130 JS_FN(js_namespace_str, xml_namespace, 1,0),
7131 JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0),
7132 JS_FN("nodeKind", xml_nodeKind, 0,0),
7133 JS_FN("normalize", xml_normalize, 0,0),
7134 JS_FN(js_xml_parent_str, xml_parent, 0,0),
7135 JS_FN("processingInstructions",xml_processingInstructions,1,0),
7136 JS_FN("prependChild", xml_prependChild, 1,0),
7137 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0),
7138 JS_FN("removeNamespace", xml_removeNamespace, 1,0),
7139 JS_FN("replace", xml_replace, 2,0),
7140 JS_FN("setChildren", xml_setChildren, 1,0),
7141 JS_FN("setLocalName", xml_setLocalName, 1,0),
7142 JS_FN("setName", xml_setName, 1,0),
7143 JS_FN("setNamespace", xml_setNamespace, 1,0),
7144 JS_FN(js_text_str, xml_text, 0,0),
7145 JS_FN(js_toSource_str, xml_toSource, 0,0),
7146 JS_FN(js_toString_str, xml_toString, 0,0),
7147 JS_FN(js_toXMLString_str, xml_toXMLString, 0,0),
7148 JS_FN(js_valueOf_str, xml_valueOf, 0,0),
7149 JS_FS_END
7152 static JSBool
7153 CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
7155 int i;
7156 const char *name;
7157 jsval v;
7159 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
7160 name = xml_static_props[i].name;
7161 if (!JS_GetProperty(cx, from, name, &v))
7162 return JS_FALSE;
7163 if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v))
7164 return JS_FALSE;
7167 name = xml_static_props[i].name;
7168 if (!JS_GetProperty(cx, from, name, &v))
7169 return JS_FALSE;
7170 if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v))
7171 return JS_FALSE;
7172 return JS_TRUE;
7175 static JSBool
7176 SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
7178 int i;
7179 jsval v;
7181 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
7182 v = JSVAL_TRUE;
7183 if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
7184 return JS_FALSE;
7186 v = INT_TO_JSVAL(2);
7187 return JS_SetProperty(cx, obj, xml_static_props[i].name, &v);
7190 static JSBool
7191 xml_settings(JSContext *cx, uintN argc, jsval *vp)
7193 JSObject *settings;
7194 JSObject *obj;
7196 settings = JS_NewObject(cx, NULL, NULL, NULL);
7197 if (!settings)
7198 return JS_FALSE;
7199 *vp = OBJECT_TO_JSVAL(settings);
7200 obj = JS_THIS_OBJECT(cx, vp);
7201 return obj && CopyXMLSettings(cx, obj, settings);
7204 static JSBool
7205 xml_setSettings(JSContext *cx, uintN argc, jsval *vp)
7207 JSObject *obj, *settings;
7208 jsval v;
7209 JSBool ok;
7211 obj = JS_THIS_OBJECT(cx, vp);
7212 if (!obj)
7213 return JS_FALSE;
7214 v = (argc == 0) ? JSVAL_VOID : vp[2];
7215 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
7216 cx->xmlSettingFlags = 0;
7217 ok = SetDefaultXMLSettings(cx, obj);
7218 } else {
7219 if (JSVAL_IS_PRIMITIVE(v))
7220 return JS_TRUE;
7221 settings = JSVAL_TO_OBJECT(v);
7222 cx->xmlSettingFlags = 0;
7223 ok = CopyXMLSettings(cx, settings, obj);
7225 if (ok)
7226 cx->xmlSettingFlags |= XSF_CACHE_VALID;
7227 return ok;
7230 static JSBool
7231 xml_defaultSettings(JSContext *cx, uintN argc, jsval *vp)
7233 JSObject *settings;
7235 settings = JS_NewObject(cx, NULL, NULL, NULL);
7236 if (!settings)
7237 return JS_FALSE;
7238 *vp = OBJECT_TO_JSVAL(settings);
7239 return SetDefaultXMLSettings(cx, settings);
7242 static JSFunctionSpec xml_static_methods[] = {
7243 JS_FN("settings", xml_settings, 0,0),
7244 JS_FN("setSettings", xml_setSettings, 1,0),
7245 JS_FN("defaultSettings", xml_defaultSettings, 0,0),
7246 JS_FS_END
7249 static JSBool
7250 XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7252 jsval v;
7253 JSXML *xml, *copy;
7254 JSObject *xobj, *vobj;
7255 JSClass *clasp;
7257 v = argv[0];
7258 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7259 v = STRING_TO_JSVAL(cx->runtime->emptyString);
7261 xobj = ToXML(cx, v);
7262 if (!xobj)
7263 return JS_FALSE;
7264 *rval = OBJECT_TO_JSVAL(xobj);
7265 xml = (JSXML *) JS_GetPrivate(cx, xobj);
7267 if (JS_IsConstructing(cx) && !JSVAL_IS_PRIMITIVE(v)) {
7268 vobj = JSVAL_TO_OBJECT(v);
7269 clasp = OBJ_GET_CLASS(cx, vobj);
7270 if (clasp == &js_XMLClass ||
7271 (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
7272 /* No need to lock obj, it's newly constructed and thread local. */
7273 copy = DeepCopy(cx, xml, obj, 0);
7274 if (!copy)
7275 return JS_FALSE;
7276 JS_ASSERT(copy->object == obj);
7277 *rval = OBJECT_TO_JSVAL(obj);
7278 return JS_TRUE;
7281 return JS_TRUE;
7284 static JSBool
7285 XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7287 jsval v;
7288 JSObject *vobj, *listobj;
7289 JSXML *xml, *list;
7291 v = argv[0];
7292 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7293 v = STRING_TO_JSVAL(cx->runtime->emptyString);
7295 if (JS_IsConstructing(cx) && !JSVAL_IS_PRIMITIVE(v)) {
7296 vobj = JSVAL_TO_OBJECT(v);
7297 if (OBJECT_IS_XML(cx, vobj)) {
7298 xml = (JSXML *) JS_GetPrivate(cx, vobj);
7299 if (xml->xml_class == JSXML_CLASS_LIST) {
7300 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7301 if (!listobj)
7302 return JS_FALSE;
7303 *rval = OBJECT_TO_JSVAL(listobj);
7305 list = (JSXML *) JS_GetPrivate(cx, listobj);
7306 if (!Append(cx, list, xml))
7307 return JS_FALSE;
7308 return JS_TRUE;
7313 /* Toggle on XML support since the script has explicitly requested it. */
7314 listobj = ToXMLList(cx, v);
7315 if (!listobj)
7316 return JS_FALSE;
7318 *rval = OBJECT_TO_JSVAL(listobj);
7319 return JS_TRUE;
7322 #define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar))
7323 #define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLElemVar))
7324 #define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *))
7326 static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = {
7327 JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */
7328 JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */
7329 JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */
7330 JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */
7331 JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */
7332 JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */
7335 #ifdef DEBUG_notme
7336 JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
7337 uint32 xml_serial;
7338 #endif
7340 JSXML *
7341 js_NewXML(JSContext *cx, JSXMLClass xml_class)
7343 JSXML *xml;
7345 xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]);
7346 if (!xml)
7347 return NULL;
7349 xml->object = NULL;
7350 xml->domnode = NULL;
7351 xml->parent = NULL;
7352 xml->name = NULL;
7353 xml->xml_class = xml_class;
7354 xml->xml_flags = 0;
7355 if (JSXML_CLASS_HAS_VALUE(xml_class)) {
7356 xml->xml_value = cx->runtime->emptyString;
7357 } else {
7358 XMLArrayInit(cx, &xml->xml_kids, 0);
7359 if (xml_class == JSXML_CLASS_LIST) {
7360 xml->xml_target = NULL;
7361 xml->xml_targetprop = NULL;
7362 } else {
7363 XMLArrayInit(cx, &xml->xml_namespaces, 0);
7364 XMLArrayInit(cx, &xml->xml_attrs, 0);
7368 #ifdef DEBUG_notme
7369 JS_APPEND_LINK(&xml->links, &xml_leaks);
7370 xml->serial = xml_serial++;
7371 #endif
7372 METER(xml_stats.xml);
7373 return xml;
7376 void
7377 js_TraceXML(JSTracer *trc, JSXML *xml)
7379 if (xml->object)
7380 JS_CALL_OBJECT_TRACER(trc, xml->object, "object");
7381 if (xml->name)
7382 JS_CALL_OBJECT_TRACER(trc, xml->name, "name");
7383 if (xml->parent)
7384 JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent");
7386 if (JSXML_HAS_VALUE(xml)) {
7387 if (xml->xml_value)
7388 JS_CALL_STRING_TRACER(trc, xml->xml_value, "value");
7389 return;
7392 xml_trace_vector(trc,
7393 (JSXML **) xml->xml_kids.vector,
7394 xml->xml_kids.length);
7395 XMLArrayCursorTrace(trc, xml->xml_kids.cursors);
7396 if (IS_GC_MARKING_TRACER(trc))
7397 XMLArrayTrim(&xml->xml_kids);
7399 if (xml->xml_class == JSXML_CLASS_LIST) {
7400 if (xml->xml_target)
7401 JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target");
7402 if (xml->xml_targetprop)
7403 JS_CALL_OBJECT_TRACER(trc, xml->xml_targetprop, "targetprop");
7404 } else {
7405 TraceObjectVector(trc,
7406 (JSObject **) xml->xml_namespaces.vector,
7407 xml->xml_namespaces.length);
7408 XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
7409 if (IS_GC_MARKING_TRACER(trc))
7410 XMLArrayTrim(&xml->xml_namespaces);
7412 xml_trace_vector(trc,
7413 (JSXML **) xml->xml_attrs.vector,
7414 xml->xml_attrs.length);
7415 XMLArrayCursorTrace(trc, xml->xml_attrs.cursors);
7416 if (IS_GC_MARKING_TRACER(trc))
7417 XMLArrayTrim(&xml->xml_attrs);
7421 void
7422 js_FinalizeXML(JSContext *cx, JSXML *xml)
7424 if (JSXML_HAS_KIDS(xml)) {
7425 XMLArrayFinish(cx, &xml->xml_kids);
7426 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
7427 XMLArrayFinish(cx, &xml->xml_namespaces);
7428 XMLArrayFinish(cx, &xml->xml_attrs);
7432 #ifdef DEBUG_notme
7433 JS_REMOVE_LINK(&xml->links);
7434 #endif
7437 JSObject *
7438 js_ParseNodeToXMLObject(JSContext *cx, JSParseContext *pc, JSParseNode *pn)
7440 jsval nsval;
7441 JSObject *ns;
7442 JSXMLArray nsarray;
7443 JSXML *xml;
7445 if (!js_GetDefaultXMLNamespace(cx, &nsval))
7446 return NULL;
7447 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
7448 ns = JSVAL_TO_OBJECT(nsval);
7450 if (!XMLArrayInit(cx, &nsarray, 1))
7451 return NULL;
7453 XMLARRAY_APPEND(cx, &nsarray, ns);
7454 xml = ParseNodeToXML(cx, pc, pn, &nsarray, XSF_PRECOMPILED_ROOT);
7455 XMLArrayFinish(cx, &nsarray);
7456 if (!xml)
7457 return NULL;
7459 return xml->object;
7462 JSObject *
7463 js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7465 JSXML *xml;
7466 JSObject *obj;
7467 JSTempValueRooter tvr;
7469 xml = js_NewXML(cx, xml_class);
7470 if (!xml)
7471 return NULL;
7472 JS_PUSH_TEMP_ROOT_XML(cx, xml, &tvr);
7473 obj = js_GetXMLObject(cx, xml);
7474 JS_POP_TEMP_ROOT(cx, &tvr);
7475 return obj;
7478 static JSObject *
7479 NewXMLObject(JSContext *cx, JSXML *xml)
7481 JSObject *obj;
7483 obj = js_NewObject(cx, &js_XMLClass, NULL, NULL, 0);
7484 if (!obj || !JS_SetPrivate(cx, obj, xml)) {
7485 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
7486 return NULL;
7488 METER(xml_stats.xmlobj);
7489 return obj;
7492 JSObject *
7493 js_GetXMLObject(JSContext *cx, JSXML *xml)
7495 JSObject *obj;
7497 obj = xml->object;
7498 if (obj) {
7499 JS_ASSERT(JS_GetPrivate(cx, obj) == xml);
7500 return obj;
7504 * A JSXML cannot be shared among threads unless it has an object.
7505 * A JSXML cannot be given an object unless:
7506 * (a) it has no parent; or
7507 * (b) its parent has no object (therefore is thread-private); or
7508 * (c) its parent's object is locked.
7510 * Once given an object, a JSXML is immutable.
7512 JS_ASSERT(!xml->parent ||
7513 !xml->parent->object ||
7514 JS_IS_OBJ_LOCKED(cx, xml->parent->object));
7516 obj = NewXMLObject(cx, xml);
7517 if (!obj)
7518 return NULL;
7519 xml->object = obj;
7520 return obj;
7523 JSObject *
7524 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7526 return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2,
7527 namespace_props, namespace_methods, NULL, NULL);
7530 JSObject *
7531 js_InitQNameClass(JSContext *cx, JSObject *obj)
7533 return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2,
7534 qname_props, qname_methods, NULL, NULL);
7537 JSObject *
7538 js_InitAttributeNameClass(JSContext *cx, JSObject *obj)
7540 return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2,
7541 qname_props, qname_methods, NULL, NULL);
7544 JSObject *
7545 js_InitAnyNameClass(JSContext *cx, JSObject *obj)
7547 jsval v;
7549 if (!js_GetAnyName(cx, &v))
7550 return NULL;
7551 return JSVAL_TO_OBJECT(v);
7554 JSObject *
7555 js_InitXMLClass(JSContext *cx, JSObject *obj)
7557 JSObject *proto, *pobj;
7558 JSFunction *fun;
7559 JSXML *xml;
7560 JSProperty *prop;
7561 JSScopeProperty *sprop;
7562 jsval cval, vp[3];
7564 /* Define the isXMLName function. */
7565 if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7566 return NULL;
7568 /* Define the XML class constructor and prototype. */
7569 proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
7570 NULL, xml_methods,
7571 xml_static_props, xml_static_methods);
7572 if (!proto)
7573 return NULL;
7575 xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7576 if (!xml || !JS_SetPrivate(cx, proto, xml))
7577 return NULL;
7578 xml->object = proto;
7579 METER(xml_stats.xmlobj);
7582 * Prepare to set default settings on the XML constructor we just made.
7583 * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY,
7584 * which is xml_getProperty, which creates a new XMLList every time! We
7585 * must instead call js_LookupProperty directly.
7587 if (!js_LookupProperty(cx, proto,
7588 ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
7589 &pobj, &prop)) {
7590 return NULL;
7592 JS_ASSERT(prop);
7593 sprop = (JSScopeProperty *) prop;
7594 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
7595 cval = OBJ_GET_SLOT(cx, pobj, sprop->slot);
7596 OBJ_DROP_PROPERTY(cx, pobj, prop);
7597 JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
7599 /* Set default settings. */
7600 vp[0] = JSVAL_NULL;
7601 vp[1] = cval;
7602 vp[2] = JSVAL_VOID;
7603 if (!xml_setSettings(cx, 1, vp))
7604 return NULL;
7606 /* Define the XMLList function and give it the same prototype as XML. */
7607 fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0);
7608 if (!fun)
7609 return NULL;
7610 if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
7611 JSPROP_READONLY | JSPROP_PERMANENT)) {
7612 return NULL;
7614 return proto;
7617 JSObject *
7618 js_InitXMLClasses(JSContext *cx, JSObject *obj)
7620 if (!js_InitNamespaceClass(cx, obj))
7621 return NULL;
7622 if (!js_InitQNameClass(cx, obj))
7623 return NULL;
7624 if (!js_InitAttributeNameClass(cx, obj))
7625 return NULL;
7626 if (!js_InitAnyNameClass(cx, obj))
7627 return NULL;
7628 if (!js_InitXMLFilterClass(cx, obj))
7629 return NULL;
7630 return js_InitXMLClass(cx, obj);
7633 JSBool
7634 js_GetFunctionNamespace(JSContext *cx, jsval *vp)
7636 JSRuntime *rt;
7637 JSObject *obj;
7638 JSAtom *atom;
7639 JSString *prefix, *uri;
7641 /* An invalid URI, for internal use only, guaranteed not to collide. */
7642 static const char anti_uri[] = "@mozilla.org/js/function";
7644 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7645 rt = cx->runtime;
7646 obj = rt->functionNamespaceObject;
7647 if (!obj) {
7648 JS_LOCK_GC(rt);
7649 obj = rt->functionNamespaceObject;
7650 if (!obj) {
7651 JS_UNLOCK_GC(rt);
7654 * Note that any race to atomize anti_uri here is resolved by
7655 * the atom table code, such that at most one atom for anti_uri
7656 * is created. We store in rt->atomState.lazy unconditionally,
7657 * since we are guaranteed to overwrite either null or the same
7658 * atom pointer.
7660 atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED);
7661 if (!atom)
7662 return JS_FALSE;
7663 rt->atomState.lazy.functionNamespaceURIAtom = atom;
7665 prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]);
7666 uri = ATOM_TO_STRING(atom);
7667 obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE);
7668 if (!obj)
7669 return JS_FALSE;
7672 * Avoid entraining any in-scope Object.prototype. The loss of
7673 * Namespace.prototype is not detectable, as there is no way to
7674 * refer to this instance in scripts. When used to qualify method
7675 * names, its prefix and uri references are copied to the QName.
7677 OBJ_CLEAR_PROTO(cx, obj);
7678 OBJ_CLEAR_PARENT(cx, obj);
7680 JS_LOCK_GC(rt);
7681 if (!rt->functionNamespaceObject)
7682 rt->functionNamespaceObject = obj;
7683 else
7684 obj = rt->functionNamespaceObject;
7686 JS_UNLOCK_GC(rt);
7688 *vp = OBJECT_TO_JSVAL(obj);
7689 return JS_TRUE;
7693 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7694 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7695 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a
7696 * lightweight function activation). There's no requirement that fp->varobj
7697 * lie directly on fp->scopeChain, although it should be reachable using the
7698 * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h).
7700 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7701 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7702 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7703 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7704 * the default XML namespace will be set to ("", n.uri). So the uri string
7705 * is really the only usefully stored value of the default namespace.
7707 JSBool
7708 js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7710 JSStackFrame *fp;
7711 JSObject *ns, *obj, *tmp;
7712 jsval v;
7714 fp = js_GetTopStackFrame(cx);
7715 ns = fp->xmlNamespace;
7716 if (ns) {
7717 *vp = OBJECT_TO_JSVAL(ns);
7718 return JS_TRUE;
7721 obj = NULL;
7722 for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) {
7723 obj = tmp;
7724 if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v))
7725 return JS_FALSE;
7726 if (!JSVAL_IS_PRIMITIVE(v)) {
7727 fp->xmlNamespace = JSVAL_TO_OBJECT(v);
7728 *vp = v;
7729 return JS_TRUE;
7733 ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL);
7734 if (!ns)
7735 return JS_FALSE;
7736 v = OBJECT_TO_JSVAL(ns);
7737 if (obj &&
7738 !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v,
7739 JS_PropertyStub, JS_PropertyStub,
7740 JSPROP_PERMANENT, NULL)) {
7741 return JS_FALSE;
7743 fp->xmlNamespace = ns;
7744 *vp = v;
7745 return JS_TRUE;
7748 JSBool
7749 js_SetDefaultXMLNamespace(JSContext *cx, jsval v)
7751 jsval argv[2];
7752 JSObject *ns, *varobj;
7753 JSStackFrame *fp;
7755 argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString);
7756 argv[1] = v;
7757 ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, 2, argv);
7758 if (!ns)
7759 return JS_FALSE;
7760 v = OBJECT_TO_JSVAL(ns);
7762 fp = js_GetTopStackFrame(cx);
7763 varobj = fp->varobj;
7764 if (varobj) {
7765 if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v,
7766 JS_PropertyStub, JS_PropertyStub,
7767 JSPROP_PERMANENT, NULL)) {
7768 return JS_FALSE;
7770 } else {
7771 JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags));
7773 fp->xmlNamespace = JSVAL_TO_OBJECT(v);
7774 return JS_TRUE;
7777 JSBool
7778 js_ToAttributeName(JSContext *cx, jsval *vp)
7780 JSObject *qn;
7782 qn = ToAttributeName(cx, *vp);
7783 if (!qn)
7784 return JS_FALSE;
7785 *vp = OBJECT_TO_JSVAL(qn);
7786 return JS_TRUE;
7789 JSString *
7790 js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote)
7792 return EscapeAttributeValue(cx, NULL, str, quote);
7795 JSString *
7796 js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7798 size_t len, len2, newlen;
7799 jschar *chars, *chars2;
7801 JSSTRING_CHARS_AND_LENGTH(str, chars, len);
7802 if (!JSSTRING_IS_MUTABLE(str)) {
7803 str = js_NewStringCopyN(cx, chars, len);
7804 if (!str)
7805 return NULL;
7806 chars = JSFLATSTR_CHARS(str);
7807 } else {
7809 * Reallocating str (because we know it has no other references)
7810 * requires purging any deflated string cached for it.
7812 js_PurgeDeflatedStringCache(cx->runtime, str);
7815 JSSTRING_CHARS_AND_LENGTH(str2, chars2, len2);
7816 newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
7817 chars = (jschar *) JS_realloc(cx, chars, (newlen+1) * sizeof(jschar));
7818 if (!chars)
7819 return NULL;
7821 JSFLATSTR_INIT(str, chars, newlen);
7822 chars += len;
7823 if (isName) {
7824 *chars++ = ' ';
7825 js_strncpy(chars, chars2, len2);
7826 chars += len2;
7827 } else {
7828 *chars++ = '=';
7829 *chars++ = '"';
7830 js_strncpy(chars, chars2, len2);
7831 chars += len2;
7832 *chars++ = '"';
7834 *chars = 0;
7835 return str;
7838 JSString *
7839 js_EscapeElementValue(JSContext *cx, JSString *str)
7841 return EscapeElementValue(cx, NULL, str);
7844 JSString *
7845 js_ValueToXMLString(JSContext *cx, jsval v)
7847 return ToXMLString(cx, v, 0);
7850 static JSBool
7851 anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7852 jsval *rval)
7854 *rval = ATOM_KEY(cx->runtime->atomState.starAtom);
7855 return JS_TRUE;
7858 JSBool
7859 js_GetAnyName(JSContext *cx, jsval *vp)
7861 JSRuntime *rt;
7862 JSObject *obj;
7863 JSBool ok;
7865 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7866 rt = cx->runtime;
7867 obj = rt->anynameObject;
7868 if (!obj) {
7869 JS_LOCK_GC(rt);
7870 obj = rt->anynameObject;
7871 if (!obj) {
7872 JS_UNLOCK_GC(rt);
7875 * Protect multiple newborns created below, in the do-while(0)
7876 * loop used to ensure that we leave this local root scope.
7878 ok = js_EnterLocalRootScope(cx);
7879 if (!ok)
7880 return JS_FALSE;
7882 do {
7883 obj = js_NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL,
7884 NULL, 0);
7885 if (!obj) {
7886 ok = JS_FALSE;
7887 break;
7889 InitXMLQName(obj, rt->emptyString, rt->emptyString,
7890 ATOM_TO_STRING(rt->atomState.starAtom));
7891 METER(xml_stats.qname);
7894 * Avoid entraining any Object.prototype found via cx's scope
7895 * chain or global object. This loses the default toString,
7896 * but no big deal: we want to customize toString anyway for
7897 * clearer diagnostics.
7899 if (!JS_DefineFunction(cx, obj, js_toString_str,
7900 anyname_toString, 0, 0)) {
7901 ok = JS_FALSE;
7902 break;
7904 JS_ASSERT(!OBJ_GET_PROTO(cx, obj));
7905 JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
7906 } while (0);
7908 js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj));
7909 if (!ok)
7910 return JS_FALSE;
7912 JS_LOCK_GC(rt);
7913 if (!rt->anynameObject)
7914 rt->anynameObject = obj;
7915 else
7916 obj = rt->anynameObject;
7918 JS_UNLOCK_GC(rt);
7920 *vp = OBJECT_TO_JSVAL(obj);
7921 return JS_TRUE;
7924 JSBool
7925 js_FindXMLProperty(JSContext *cx, jsval nameval, JSObject **objp, jsid *idp)
7927 JSObject *nameobj;
7928 jsval v;
7929 JSObject *qn;
7930 jsid funid;
7931 JSObject *obj, *target, *proto, *pobj;
7932 JSXML *xml;
7933 JSBool found;
7934 JSProperty *prop;
7935 const char *printable;
7937 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nameval));
7938 nameobj = JSVAL_TO_OBJECT(nameval);
7939 if (OBJ_GET_CLASS(cx, nameobj) == &js_AnyNameClass) {
7940 v = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.starAtom));
7941 nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1,
7942 &v);
7943 if (!nameobj)
7944 return JS_FALSE;
7945 } else {
7946 JS_ASSERT(OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass ||
7947 OBJ_GET_CLASS(cx, nameobj) == &js_QNameClass.base);
7950 qn = nameobj;
7951 if (!IsFunctionQName(cx, qn, &funid))
7952 return JS_FALSE;
7954 obj = js_GetTopStackFrame(cx)->scopeChain;
7955 do {
7956 /* Skip any With object that can wrap XML. */
7957 target = obj;
7958 while (OBJ_GET_CLASS(cx, target) == &js_WithClass) {
7959 proto = OBJ_GET_PROTO(cx, target);
7960 if (!proto)
7961 break;
7962 target = proto;
7965 if (OBJECT_IS_XML(cx, target)) {
7966 if (funid == 0) {
7967 xml = (JSXML *) JS_GetPrivate(cx, target);
7968 found = HasNamedProperty(xml, qn);
7969 } else {
7970 if (!HasFunctionProperty(cx, target, funid, &found))
7971 return JS_FALSE;
7973 if (found) {
7974 *idp = OBJECT_TO_JSID(nameobj);
7975 *objp = target;
7976 return JS_TRUE;
7978 } else if (funid != 0) {
7979 if (!OBJ_LOOKUP_PROPERTY(cx, target, funid, &pobj, &prop))
7980 return JS_FALSE;
7981 if (prop) {
7982 OBJ_DROP_PROPERTY(cx, pobj, prop);
7983 *idp = funid;
7984 *objp = target;
7985 return JS_TRUE;
7988 } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
7990 printable = js_ValueToPrintableString(cx, OBJECT_TO_JSVAL(nameobj));
7991 if (printable) {
7992 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
7993 js_GetErrorMessage, NULL,
7994 JSMSG_UNDEFINED_XML_NAME, printable);
7996 return JS_FALSE;
7999 JSBool
8000 js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
8002 JSObject *target;
8003 JSXML *xml;
8004 JSTempValueRooter tvr;
8005 JSBool ok;
8007 JS_ASSERT(OBJECT_IS_XML(cx, obj));
8009 MUST_FLOW_THROUGH("out");
8010 JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
8013 * See comments before xml_lookupProperty about the need for the proto
8014 * chain lookup.
8016 target = obj;
8017 for (;;) {
8018 ok = js_GetProperty(cx, target, id, vp);
8019 if (!ok)
8020 goto out;
8021 if (VALUE_IS_FUNCTION(cx, *vp)) {
8022 ok = JS_TRUE;
8023 goto out;
8025 target = OBJ_GET_PROTO(cx, target);
8026 if (target == NULL)
8027 break;
8028 tvr.u.object = target;
8031 xml = (JSXML *) JS_GetPrivate(cx, obj);
8032 if (HasSimpleContent(xml)) {
8033 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
8034 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
8035 &tvr.u.object);
8036 if (!ok)
8037 goto out;
8038 JS_ASSERT(tvr.u.object);
8039 ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp);
8042 out:
8043 JS_POP_TEMP_ROOT(cx, &tvr);
8044 return ok;
8047 static JSXML *
8048 GetPrivate(JSContext *cx, JSObject *obj, const char *method)
8050 JSXML *xml;
8052 xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
8053 if (!xml) {
8054 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
8055 JSMSG_INCOMPATIBLE_METHOD,
8056 js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name);
8058 return xml;
8061 JSBool
8062 js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
8064 JSXML *xml, *list;
8066 xml = GetPrivate(cx, obj, "descendants internal method");
8067 if (!xml)
8068 return JS_FALSE;
8070 list = Descendants(cx, xml, id);
8071 if (!list)
8072 return JS_FALSE;
8073 *vp = OBJECT_TO_JSVAL(list->object);
8074 return JS_TRUE;
8077 JSBool
8078 js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
8080 JSXML *list;
8081 uint32 n;
8083 list = (JSXML *) JS_GetPrivate(cx, listobj);
8084 for (n = list->xml_kids.length; n != 0; --n)
8085 DeleteListElement(cx, list, 0);
8087 return JS_TRUE;
8090 typedef struct JSXMLFilter {
8091 JSXML *list;
8092 JSXML *result;
8093 JSXML *kid;
8094 JSXMLArrayCursor cursor;
8096 } JSXMLFilter;
8098 static void
8099 xmlfilter_trace(JSTracer *trc, JSObject *obj)
8101 JSXMLFilter *filter;
8103 filter = (JSXMLFilter *) JS_GetPrivate(trc->context, obj);
8104 if (!filter)
8105 return;
8107 JS_ASSERT(filter->list);
8108 JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list");
8109 if (filter->result)
8110 JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result");
8111 if (filter->kid)
8112 JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid");
8115 * We do not need to trace the cursor as that would be done when
8116 * tracing the filter->list.
8120 static void
8121 xmlfilter_finalize(JSContext *cx, JSObject *obj)
8123 JSXMLFilter *filter;
8125 filter = (JSXMLFilter *) JS_GetPrivate(cx, obj);
8126 if (!filter)
8127 return;
8129 XMLArrayCursorFinish(&filter->cursor);
8130 JS_free(cx, filter);
8133 JSClass js_XMLFilterClass = {
8134 "XMLFilter",
8135 JSCLASS_HAS_PRIVATE |
8136 JSCLASS_IS_ANONYMOUS |
8137 JSCLASS_MARK_IS_TRACE |
8138 JSCLASS_HAS_CACHED_PROTO(JSProto_XMLFilter),
8139 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
8140 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xmlfilter_finalize,
8141 NULL, NULL, NULL, NULL,
8142 NULL, NULL, JS_CLASS_TRACE(xmlfilter_trace), NULL
8145 JSObject *
8146 js_InitXMLFilterClass(JSContext *cx, JSObject *obj)
8148 JSObject *proto;
8150 proto = JS_InitClass(cx, obj, NULL, &js_XMLFilterClass, NULL, 0, NULL,
8151 NULL, NULL, NULL);
8152 if (!proto)
8153 return NULL;
8155 OBJ_CLEAR_PROTO(cx, proto);
8156 return proto;
8159 JSBool
8160 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
8162 jsval *sp;
8163 JSObject *obj, *filterobj, *resobj, *kidobj;
8164 JSXML *xml, *list;
8165 JSXMLFilter *filter;
8167 sp = js_GetTopStackFrame(cx)->regs->sp;
8168 if (!initialized) {
8170 * We haven't iterated yet, so initialize the filter based on the
8171 * value stored in sp[-2].
8173 if (!VALUE_IS_XML(cx, sp[-2])) {
8174 js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL);
8175 return JS_FALSE;
8177 obj = JSVAL_TO_OBJECT(sp[-2]);
8178 xml = (JSXML *) JS_GetPrivate(cx, obj);
8180 if (xml->xml_class == JSXML_CLASS_LIST) {
8181 list = xml;
8182 } else {
8183 obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
8184 if (!obj)
8185 return JS_FALSE;
8188 * Root just-created obj. sp[-2] cannot be used yet for rooting
8189 * as it may be the only root holding xml.
8191 sp[-1] = OBJECT_TO_JSVAL(obj);
8192 list = (JSXML *) JS_GetPrivate(cx, obj);
8193 if (!Append(cx, list, xml))
8194 return JS_FALSE;
8197 filterobj = js_NewObject(cx, &js_XMLFilterClass, NULL, NULL, 0);
8198 if (!filterobj)
8199 return JS_FALSE;
8201 filter = (JSXMLFilter *) JS_malloc(cx, sizeof *filter);
8202 if (!filter)
8203 return JS_FALSE;
8206 * Init all filter fields before JS_SetPrivate exposes it to
8207 * xmlfilter_trace or xmlfilter_finalize.
8209 filter->list = list;
8210 filter->result = NULL;
8211 filter->kid = NULL;
8212 XMLArrayCursorInit(&filter->cursor, &list->xml_kids);
8213 JS_SetPrivate(cx, filterobj, filter);
8215 /* Store filterobj to use in the later iterations. */
8216 sp[-2] = OBJECT_TO_JSVAL(filterobj);
8218 resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
8219 if (!resobj)
8220 return JS_FALSE;
8222 /* This also roots resobj. */
8223 filter->result = (JSXML *) JS_GetPrivate(cx, resobj);
8224 } else {
8225 /* We have iterated at least once. */
8226 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
8227 JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(sp[-2])) ==
8228 &js_XMLFilterClass);
8229 filter = (JSXMLFilter *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(sp[-2]));
8230 JS_ASSERT(filter->kid);
8232 /* Check if the filter expression wants to append the element. */
8233 if (js_ValueToBoolean(sp[-1]) &&
8234 !Append(cx, filter->result, filter->kid)) {
8235 return JS_FALSE;
8239 /* Do the iteration. */
8240 filter->kid = (JSXML *) XMLArrayCursorNext(&filter->cursor);
8241 if (!filter->kid) {
8243 * Do not defer finishing the cursor until the next GC cycle to avoid
8244 * accumulation of dead cursors associated with filter->list.
8246 XMLArrayCursorFinish(&filter->cursor);
8247 JS_ASSERT(filter->result->object);
8248 sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
8249 kidobj = NULL;
8250 } else {
8251 kidobj = js_GetXMLObject(cx, filter->kid);
8252 if (!kidobj)
8253 return JS_FALSE;
8256 /* Null as kidobj at sp[-1] signals filter termination. */
8257 sp[-1] = OBJECT_TO_JSVAL(kidobj);
8258 return JS_TRUE;
8261 JSObject *
8262 js_ValueToXMLObject(JSContext *cx, jsval v)
8264 return ToXML(cx, v);
8267 JSObject *
8268 js_ValueToXMLListObject(JSContext *cx, jsval v)
8270 return ToXMLList(cx, v);
8273 JSObject *
8274 js_CloneXMLObject(JSContext *cx, JSObject *obj)
8276 uintN flags;
8277 JSXML *xml;
8279 if (!GetXMLSettingFlags(cx, &flags))
8280 return NULL;
8281 xml = (JSXML *) JS_GetPrivate(cx, obj);
8282 if (flags & (XSF_IGNORE_COMMENTS |
8283 XSF_IGNORE_PROCESSING_INSTRUCTIONS |
8284 XSF_IGNORE_WHITESPACE)) {
8285 xml = DeepCopy(cx, xml, NULL, flags);
8286 if (!xml)
8287 return NULL;
8288 return xml->object;
8290 return NewXMLObject(cx, xml);
8293 JSObject *
8294 js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
8295 JSString *value)
8297 uintN flags;
8298 JSObject *obj;
8299 JSXML *xml;
8300 JSObject *qn;
8302 if (!GetXMLSettingFlags(cx, &flags))
8303 return NULL;
8305 if ((xml_class == JSXML_CLASS_COMMENT &&
8306 (flags & XSF_IGNORE_COMMENTS)) ||
8307 (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
8308 (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
8309 return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
8312 obj = js_NewXMLObject(cx, xml_class);
8313 if (!obj)
8314 return NULL;
8315 xml = (JSXML *) JS_GetPrivate(cx, obj);
8316 if (name) {
8317 qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, name);
8318 if (!qn)
8319 return NULL;
8320 xml->name = qn;
8322 xml->xml_value = value;
8323 return obj;
8326 JSString *
8327 js_MakeXMLCDATAString(JSContext *cx, JSString *str)
8329 return MakeXMLCDATAString(cx, NULL, str);
8332 JSString *
8333 js_MakeXMLCommentString(JSContext *cx, JSString *str)
8335 return MakeXMLCommentString(cx, NULL, str);
8338 JSString *
8339 js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
8341 return MakeXMLPIString(cx, NULL, name, str);
8344 #endif /* JS_HAS_XML_SUPPORT */