/[jscoverage]/trunk/js/jsxml.cpp
ViewVC logotype

Contents of /trunk/js/jsxml.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (10 years, 2 months ago) by siliconforks
File size: 250984 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=78:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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.
16 *
17 * The Original Code is SpiderMonkey E4X code, released August, 2004.
18 *
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.
23 *
24 * Contributor(s):
25 *
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.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 #include "jsstddef.h"
41 #include "jsversion.h"
42
43 #if JS_HAS_XML_SUPPORT
44
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"
71
72 #ifdef DEBUG
73 #include <string.h> /* for #ifdef DEBUG memset calls */
74 #endif
75
76 /*
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
80 *
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 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
86 * - JS_TypeOfValue sure could use a cleaner interface to "types"
87 */
88
89 #ifdef XML_METERING
90 static struct {
91 jsrefcount qname;
92 jsrefcount xmlnamespace;
93 jsrefcount xml;
94 jsrefcount xmlobj;
95 } xml_stats;
96
97 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
98 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
99 #else
100 #define METER(x) /* nothing */
101 #define UNMETER(x) /* nothing */
102 #endif
103
104 /*
105 * Random utilities and global functions.
106 */
107 const char js_isXMLName_str[] = "isXMLName";
108 const char js_XMLList_str[] = "XMLList";
109 const char js_localName_str[] = "localName";
110 const char js_xml_parent_str[] = "parent";
111 const char js_prefix_str[] = "prefix";
112 const char js_toXMLString_str[] = "toXMLString";
113 const char js_uri_str[] = "uri";
114
115 const char js_amp_entity_str[] = "&amp;";
116 const char js_gt_entity_str[] = "&gt;";
117 const char js_lt_entity_str[] = "&lt;";
118 const char js_quot_entity_str[] = "&quot;";
119
120 #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
121 #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
122 /* Slot indexes shared between Namespace and QName objects. */
123 const uint32 JSSLOT_PREFIX = JSSLOT_PRIVATE;
124 const uint32 JSSLOT_URI = JSSLOT_PRIVATE + 1;
125
126 /* Namespace-specific slot. */
127 const uint32 JSSLOT_DECLARED = JSSLOT_PRIVATE + 2;
128
129 /* QName-specific slot. */
130 const uint32 JSSLOT_LOCAL_NAME = JSSLOT_PRIVATE + 2;
131
132 const uint32 NAMESPACE_RESERVED_SLOTS = 3;
133 const uint32 QNAME_RESERVED_SLOTS = 3;
134
135 static JSBool
136 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
137
138 static JSBool
139 IsQNameClass(JSClass *clasp)
140 {
141 return clasp == &js_QNameClass.base ||
142 clasp == &js_AttributeNameClass ||
143 clasp == &js_AnyNameClass;
144 }
145
146 static JSString *
147 GetSlotString(const JSObject *obj, uint32 slot)
148 {
149 jsval v;
150
151 JS_ASSERT(slot == JSSLOT_PREFIX ||
152 slot == JSSLOT_URI ||
153 slot == JSSLOT_LOCAL_NAME);
154 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base ||
155 IsQNameClass(STOBJ_GET_CLASS(obj)));
156 JS_ASSERT_IF(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base,
157 slot != JSSLOT_LOCAL_NAME);
158
159 v = obj->fslots[slot];
160 if (JSVAL_IS_VOID(v))
161 return NULL;
162 JS_ASSERT(JSVAL_IS_STRING(v));
163 return JSVAL_TO_STRING(v);
164 }
165
166 static JS_INLINE JSString *
167 GetPrefix(const JSObject *obj)
168 {
169 return GetSlotString(obj, JSSLOT_PREFIX);
170 }
171
172 static JSString *
173 GetURI(const JSObject *obj)
174 {
175 return GetSlotString(obj, JSSLOT_URI);
176 }
177
178 static JSString *
179 GetLocalName(const JSObject *obj)
180 {
181 return GetSlotString(obj, JSSLOT_LOCAL_NAME);
182 }
183
184 static JSBool
185 IsDeclared(const JSObject *obj)
186 {
187 jsval v;
188
189 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base);
190 v = obj->fslots[JSSLOT_DECLARED];
191 JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
192 return v == JSVAL_TRUE;
193 }
194
195 static JSBool
196 xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
197 jsval *rval)
198 {
199 *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
200 return JS_TRUE;
201 }
202
203 /*
204 * Namespace class and library functions.
205 */
206 enum namespace_tinyid {
207 NAMESPACE_PREFIX = -1,
208 NAMESPACE_URI = -2
209 };
210
211 static JSBool
212 namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
213 {
214 if (!JSVAL_IS_INT(id))
215 return JS_TRUE;
216
217 if (STOBJ_GET_CLASS(obj) != &js_NamespaceClass.base)
218 return JS_TRUE;
219
220 switch (JSVAL_TO_INT(id)) {
221 case NAMESPACE_PREFIX:
222 *vp = obj->fslots[JSSLOT_PREFIX];
223 break;
224 case NAMESPACE_URI:
225 *vp = obj->fslots[JSSLOT_URI];
226 break;
227 }
228 return JS_TRUE;
229 }
230
231 static void
232 namespace_finalize(JSContext *cx, JSObject *obj)
233 {
234 if (cx->runtime->functionNamespaceObject == obj)
235 cx->runtime->functionNamespaceObject = NULL;
236 }
237
238 static JSBool
239 namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
240 {
241 JSObject *obj2;
242
243 JS_ASSERT(JSVAL_IS_OBJECT(v));
244 obj2 = JSVAL_TO_OBJECT(v);
245 *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base)
246 ? JS_FALSE
247 : js_EqualStrings(GetURI(obj), GetURI(obj2));
248 return JS_TRUE;
249 }
250
251 JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
252 { "Namespace",
253 JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
254 JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS) |
255 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
256 JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL,
257 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize,
258 NULL, NULL, NULL, NULL,
259 NULL, NULL, NULL, NULL },
260 namespace_equality,NULL, NULL, NULL,
261 NULL, NULL, NULL, NULL
262 };
263
264 #define NAMESPACE_ATTRS \
265 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
266
267 static JSPropertySpec namespace_props[] = {
268 {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0},
269 {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0},
270 {0,0,0,0,0}
271 };
272
273 static JSBool
274 namespace_toString(JSContext *cx, uintN argc, jsval *vp)
275 {
276 JSObject *obj;
277
278 obj = JS_THIS_OBJECT(cx, vp);
279 if (!JS_InstanceOf(cx, obj, &js_NamespaceClass.base, vp))
280 return JS_FALSE;
281 *vp = obj->fslots[JSSLOT_URI];
282 return JS_TRUE;
283 }
284
285 static JSFunctionSpec namespace_methods[] = {
286 JS_FN(js_toString_str, namespace_toString, 0,0),
287 JS_FS_END
288 };
289
290 static JSObject *
291 NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
292 {
293 JSObject *obj;
294
295 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
296 if (!obj)
297 return JS_FALSE;
298 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
299 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
300 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_DECLARED]));
301 if (prefix)
302 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
303 if (uri)
304 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
305 if (declared)
306 obj->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
307 METER(xml_stats.xmlnamespace);
308 return obj;
309 }
310
311 /*
312 * QName class and library functions.
313 */
314 enum qname_tinyid {
315 QNAME_URI = -1,
316 QNAME_LOCALNAME = -2
317 };
318
319 static JSBool
320 qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
321 {
322 if (!JSVAL_IS_INT(id))
323 return JS_TRUE;
324
325 if (STOBJ_GET_CLASS(obj) != &js_QNameClass.base)
326 return JS_TRUE;
327
328 switch (JSVAL_TO_INT(id)) {
329 case QNAME_URI:
330 *vp = obj->fslots[JSSLOT_URI];
331 if (*vp == JSVAL_VOID)
332 *vp = JSVAL_NULL;
333 break;
334 case QNAME_LOCALNAME:
335 *vp = obj->fslots[JSSLOT_LOCAL_NAME];
336 break;
337 }
338 return JS_TRUE;
339 }
340
341 static void
342 anyname_finalize(JSContext* cx, JSObject* obj)
343 {
344 /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
345 if (cx->runtime->anynameObject == obj)
346 cx->runtime->anynameObject = NULL;
347 }
348
349 static JSBool
350 qname_identity(JSObject *qna, JSObject *qnb)
351 {
352 JSString *uri1 = GetURI(qna);
353 JSString *uri2 = GetURI(qnb);
354
355 if (!uri1 ^ !uri2)
356 return JS_FALSE;
357 if (uri1 && !js_EqualStrings(uri1, uri2))
358 return JS_FALSE;
359 return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb));
360 }
361
362 static JSBool
363 qname_equality(JSContext *cx, JSObject *qn, jsval v, JSBool *bp)
364 {
365 JSObject *obj2;
366
367 JS_ASSERT(JSVAL_IS_OBJECT(v));
368 obj2 = JSVAL_TO_OBJECT(v);
369 *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base)
370 ? JS_FALSE
371 : qname_identity(qn, obj2);
372 return JS_TRUE;
373 }
374
375 JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
376 { "QName",
377 JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
378 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
379 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
380 JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL,
381 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
382 NULL, NULL, NULL, NULL,
383 NULL, NULL, NULL, NULL },
384 qname_equality, NULL, NULL, NULL,
385 NULL, NULL, NULL, NULL
386 };
387
388 /*
389 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
390 * are like QName, except that they have no property getters. They share the
391 * qname_toString method, and therefore are exposed as constructable objects
392 * in this implementation.
393 */
394 JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
395 js_AttributeName_str,
396 JSCLASS_CONSTRUCT_PROTOTYPE |
397 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
398 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
399 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
400 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
401 NULL, NULL, NULL, NULL,
402 NULL, NULL, NULL, NULL
403 };
404
405 JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
406 js_AnyName_str,
407 JSCLASS_CONSTRUCT_PROTOTYPE |
408 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
409 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
410 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
411 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize,
412 NULL, NULL, NULL, NULL,
413 NULL, NULL, NULL, NULL
414 };
415
416 #define QNAME_ATTRS \
417 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
418
419 static JSPropertySpec qname_props[] = {
420 {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0},
421 {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0},
422 {0,0,0,0,0}
423 };
424
425 static JSBool
426 qname_toString(JSContext *cx, uintN argc, jsval *vp)
427 {
428 JSObject *obj;
429 JSClass *clasp;
430 JSString *uri, *str, *qualstr;
431 size_t length;
432 jschar *chars;
433
434 obj = JS_THIS_OBJECT(cx, vp);
435 if (!obj)
436 return JS_FALSE;
437 clasp = OBJ_GET_CLASS(cx, obj);
438 if (clasp != &js_AttributeNameClass &&
439 clasp != &js_AnyNameClass &&
440 !JS_InstanceOf(cx, obj, &js_QNameClass.base, vp + 2)) {
441 return JS_FALSE;
442 }
443
444 uri = GetURI(obj);
445 if (!uri) {
446 /* No uri means wildcard qualifier. */
447 str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
448 } else if (IS_EMPTY(uri)) {
449 /* Empty string for uri means localName is in no namespace. */
450 str = cx->runtime->emptyString;
451 } else {
452 qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
453 str = js_ConcatStrings(cx, uri, qualstr);
454 if (!str)
455 return JS_FALSE;
456 }
457 str = js_ConcatStrings(cx, str, GetLocalName(obj));
458 if (!str)
459 return JS_FALSE;
460
461 if (str && clasp == &js_AttributeNameClass) {
462 length = JSSTRING_LENGTH(str);
463 chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
464 if (!chars)
465 return JS_FALSE;
466 *chars = '@';
467 js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
468 chars[++length] = 0;
469 str = js_NewString(cx, chars, length);
470 if (!str) {
471 JS_free(cx, chars);
472 return JS_FALSE;
473 }
474 }
475
476 *vp = STRING_TO_JSVAL(str);
477 return JS_TRUE;
478 }
479
480 static JSFunctionSpec qname_methods[] = {
481 JS_FN(js_toString_str, qname_toString, 0,0),
482 JS_FS_END
483 };
484
485
486 static void
487 InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix,
488 JSString *localName)
489 {
490 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
491 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
492 JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_LOCAL_NAME]));
493 if (uri)
494 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
495 if (prefix)
496 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
497 if (localName)
498 obj->fslots[JSSLOT_LOCAL_NAME] = STRING_TO_JSVAL(localName);
499 }
500
501 static JSObject *
502 NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
503 JSClass *clasp = &js_QNameClass.base)
504 {
505 JSObject *obj;
506
507 JS_ASSERT(IsQNameClass(clasp));
508 obj = js_NewObject(cx, clasp, NULL, NULL, 0);
509 if (!obj)
510 return NULL;
511 InitXMLQName(obj, uri, prefix, localName);
512 METER(xml_stats.qname);
513 return obj;
514 }
515
516 JSObject *
517 js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
518 {
519 jsval argv[2];
520
521 /*
522 * ECMA-357 11.1.2,
523 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
524 * production, step 2.
525 */
526 if (!JSVAL_IS_PRIMITIVE(nsval) &&
527 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
528 nsval = JSVAL_NULL;
529 }
530
531 argv[0] = nsval;
532 argv[1] = lnval;
533 return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
534 }
535
536 static JSBool
537 IsXMLName(const jschar *cp, size_t n)
538 {
539 JSBool rv;
540 jschar c;
541
542 rv = JS_FALSE;
543 if (n != 0 && JS_ISXMLNSSTART(*cp)) {
544 while (--n != 0) {
545 c = *++cp;
546 if (!JS_ISXMLNS(c))
547 return rv;
548 }
549 rv = JS_TRUE;
550 }
551 return rv;
552 }
553
554 JSBool
555 js_IsXMLName(JSContext *cx, jsval v)
556 {
557 JSString *name;
558 JSErrorReporter older;
559
560 /*
561 * Inline specialization of the QName constructor called with v passed as
562 * the only argument, to compute the localName for the constructed qname,
563 * without actually allocating the object or computing its uri and prefix.
564 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
565 */
566 if (!JSVAL_IS_PRIMITIVE(v) &&
567 IsQNameClass(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)))) {
568 name = GetLocalName(JSVAL_TO_OBJECT(v));
569 } else {
570 older = JS_SetErrorReporter(cx, NULL);
571 name = js_ValueToString(cx, v);
572 JS_SetErrorReporter(cx, older);
573 if (!name) {
574 JS_ClearPendingException(cx);
575 return JS_FALSE;
576 }
577 }
578
579 return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
580 }
581
582 /*
583 * When argc is -1, it indicates argv is empty but the code should behave as
584 * if argc is 1 and argv[0] is JSVAL_VOID.
585 */
586 static JSBool
587 NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
588 jsval *rval)
589 {
590 jsval urival, prefixval;
591 JSObject *uriobj;
592 JSBool isNamespace, isQName;
593 JSClass *clasp;
594 JSString *empty, *uri, *prefix;
595
596 isNamespace = isQName = JS_FALSE;
597 #ifdef __GNUC__ /* suppress bogus gcc warnings */
598 uriobj = NULL;
599 #endif
600 if (argc <= 0) {
601 urival = JSVAL_VOID;
602 } else {
603 urival = argv[argc > 1];
604 if (!JSVAL_IS_PRIMITIVE(urival)) {
605 uriobj = JSVAL_TO_OBJECT(urival);
606 clasp = OBJ_GET_CLASS(cx, uriobj);
607 isNamespace = (clasp == &js_NamespaceClass.base);
608 isQName = (clasp == &js_QNameClass.base);
609 }
610 }
611
612 if (!obj) {
613 /* Namespace called as function. */
614 if (argc == 1 && isNamespace) {
615 /* Namespace called with one Namespace argument is identity. */
616 *rval = urival;
617 return JS_TRUE;
618 }
619
620 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
621 if (!obj)
622 return JS_FALSE;
623 *rval = OBJECT_TO_JSVAL(obj);
624 }
625 METER(xml_stats.xmlnamespace);
626
627 empty = cx->runtime->emptyString;
628 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(empty);
629 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(empty);
630
631 if (argc == 1 || argc == -1) {
632 if (isNamespace) {
633 obj->fslots[JSSLOT_URI] = uriobj->fslots[JSSLOT_URI];
634 obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
635 } else if (isQName && (uri = GetURI(uriobj))) {
636 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
637 obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
638 } else {
639 uri = js_ValueToString(cx, urival);
640 if (!uri)
641 return JS_FALSE;
642 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
643 if (!IS_EMPTY(uri))
644 obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
645 }
646 } else if (argc == 2) {
647 if (!isQName || !(uri = GetURI(uriobj))) {
648 uri = js_ValueToString(cx, urival);
649 if (!uri)
650 return JS_FALSE;
651 }
652 obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
653
654 prefixval = argv[0];
655 if (IS_EMPTY(uri)) {
656 if (!JSVAL_IS_VOID(prefixval)) {
657 prefix = js_ValueToString(cx, prefixval);
658 if (!prefix)
659 return JS_FALSE;
660 if (!IS_EMPTY(prefix)) {
661 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
662 JSMSG_BAD_XML_NAMESPACE,
663 js_ValueToPrintableString(cx,
664 STRING_TO_JSVAL(prefix)));
665 return JS_FALSE;
666 }
667 }
668 } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
669 obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
670 } else {
671 prefix = js_ValueToString(cx, prefixval);
672 if (!prefix)
673 return JS_FALSE;
674 obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
675 }
676 }
677
678 return JS_TRUE;
679 }
680
681 static JSBool
682 Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
683 {
684 return NamespaceHelper(cx,
685 JS_IsConstructing(cx) ? obj : NULL,
686 argc, argv, rval);
687 }
688
689 /*
690 * When argc is -1, it indicates argv is empty but the code should behave as
691 * if argc is 1 and argv[0] is JSVAL_VOID.
692 */
693 static JSBool
694 QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
695 jsval *argv, jsval *rval)
696 {
697 jsval nameval, nsval;
698 JSBool isQName, isNamespace;
699 JSObject *qn;
700 JSString *uri, *prefix, *name;
701 JSObject *obj2;
702
703 JS_ASSERT(clasp == &js_QNameClass.base ||
704 clasp == &js_AttributeNameClass);
705 if (argc <= 0) {
706 nameval = JSVAL_VOID;
707 isQName = JS_FALSE;
708 } else {
709 nameval = argv[argc > 1];
710 isQName =
711 !JSVAL_IS_PRIMITIVE(nameval) &&
712 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
713 }
714
715 if (!obj) {
716 /* QName called as function. */
717 if (argc == 1 && isQName) {
718 /* QName called with one QName argument is identity. */
719 *rval = nameval;
720 return JS_TRUE;
721 }
722
723 /*
724 * Create and return a new QName or AttributeName object exactly as if
725 * constructed.
726 */
727 obj = js_NewObject(cx, clasp, NULL, NULL, 0);
728 if (!obj)
729 return JS_FALSE;
730 *rval = OBJECT_TO_JSVAL(obj);
731 }
732 METER(xml_stats.qname);
733
734 if (isQName) {
735 /* If namespace is not specified and name is a QName, clone it. */
736 qn = JSVAL_TO_OBJECT(nameval);
737 if (argc == 1) {
738 uri = GetURI(qn);
739 prefix = GetPrefix(qn);
740 name = GetLocalName(qn);
741 goto out;
742 }
743
744 /* Namespace and qname were passed -- use the qname's localName. */
745 nameval = qn->fslots[JSSLOT_LOCAL_NAME];
746 }
747
748 if (argc == 0) {
749 name = cx->runtime->emptyString;
750 } else if (argc < 0) {
751 name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
752 } else {
753 name = js_ValueToString(cx, nameval);
754 if (!name)
755 return JS_FALSE;
756 argv[argc > 1] = STRING_TO_JSVAL(name);
757 }
758
759 if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
760 nsval = argv[0];
761 } else if (IS_STAR(name)) {
762 nsval = JSVAL_NULL;
763 } else {
764 if (!js_GetDefaultXMLNamespace(cx, &nsval))
765 return JS_FALSE;
766 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
767 JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) ==
768 &js_NamespaceClass.base);
769 }
770
771 if (JSVAL_IS_NULL(nsval)) {
772 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
773 uri = prefix = NULL;
774 } else {
775 /*
776 * Inline specialization of the Namespace constructor called with
777 * nsval passed as the only argument, to compute the uri and prefix
778 * for the constructed namespace, without actually allocating the
779 * object or computing other members. See ECMA-357 13.3.2 6(a) and
780 * 13.2.2.
781 */
782 isNamespace = isQName = JS_FALSE;
783 if (!JSVAL_IS_PRIMITIVE(nsval)) {
784 obj2 = JSVAL_TO_OBJECT(nsval);
785 clasp = OBJ_GET_CLASS(cx, obj2);
786 isNamespace = (clasp == &js_NamespaceClass.base);
787 isQName = (clasp == &js_QNameClass.base);
788 }
789 #ifdef __GNUC__ /* suppress bogus gcc warnings */
790 else obj2 = NULL;
791 #endif
792
793 if (isNamespace) {
794 uri = GetURI(obj2);
795 prefix = GetPrefix(obj2);
796 } else if (isQName && (uri = GetURI(obj2))) {
797 JS_ASSERT(argc > 1);
798 prefix = GetPrefix(obj2);
799 } else {
800 JS_ASSERT(argc > 1);
801 uri = js_ValueToString(cx, nsval);
802 if (!uri)
803 return JS_FALSE;
804 argv[0] = STRING_TO_JSVAL(uri); /* local root */
805
806 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
807 prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
808 }
809 }
810
811 out:
812 InitXMLQName(obj, uri, prefix, name);
813 return JS_TRUE;
814 }
815
816 static JSBool
817 QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
818 {
819 return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL,
820 &js_QNameClass.base, argc, argv, rval);
821 }
822
823 static JSBool
824 AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
825 jsval *rval)
826 {
827 return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL,
828 &js_AttributeNameClass, argc, argv, rval);
829 }
830
831 /*
832 * XMLArray library functions.
833 */
834 static JSBool
835 namespace_identity(const void *a, const void *b)
836 {
837 const JSObject *nsa = (const JSObject *) a;
838 const JSObject *nsb = (const JSObject *) b;
839 JSString *prefixa = GetPrefix(nsa);
840 JSString *prefixb = GetPrefix(nsb);
841
842 if (prefixa && prefixb) {
843 if (!js_EqualStrings(prefixa, prefixb))
844 return JS_FALSE;
845 } else {
846 if (prefixa || prefixb)
847 return JS_FALSE;
848 }
849 return js_EqualStrings(GetURI(nsa), GetURI(nsb));
850 }
851
852 static JSBool
853 attr_identity(const void *a, const void *b)
854 {
855 const JSXML *xmla = (const JSXML *) a;
856 const JSXML *xmlb = (const JSXML *) b;
857
858 return qname_identity(xmla->name, xmlb->name);
859 }
860
861 static void
862 XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
863 {
864 JSXMLArrayCursor *next;
865
866 cursor->array = array;
867 cursor->index = 0;
868 next = cursor->next = array->cursors;
869 if (next)
870 next->prevp = &cursor->next;
871 cursor->prevp = &array->cursors;
872 array->cursors = cursor;
873 cursor->root = NULL;
874 }
875
876 static void
877 XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
878 {
879 JSXMLArrayCursor *next;
880
881 if (!cursor->array)
882 return;
883 next = cursor->next;
884 if (next)
885 next->prevp = cursor->prevp;
886 *cursor->prevp = next;
887 cursor->array = NULL;
888 }
889
890 static void *
891 XMLArrayCursorNext(JSXMLArrayCursor *cursor)
892 {
893 JSXMLArray *array;
894
895 array = cursor->array;
896 if (!array || cursor->index >= array->length)
897 return NULL;
898 return cursor->root = array->vector[cursor->index++];
899 }
900
901 static void *
902 XMLArrayCursorItem(JSXMLArrayCursor *cursor)
903 {
904 JSXMLArray *array;
905
906 array = cursor->array;
907 if (!array || cursor->index >= array->length)
908 return NULL;
909 return cursor->root = array->vector[cursor->index];
910 }
911
912 static void
913 XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
914 {
915 void *root;
916 #ifdef DEBUG
917 size_t index = 0;
918 #endif
919
920 for (; cursor; cursor = cursor->next) {
921 root = cursor->root;
922 JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
923 js_CallValueTracerIfGCThing(trc, (jsval)root);
924 }
925 }
926
927 /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
928 static JSBool
929 XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
930 {
931 void **vector;
932
933 if (capacity == 0) {
934 /* We could let realloc(p, 0) free this, but purify gets confused. */
935 if (array->vector)
936 free(array->vector);
937 vector = NULL;
938 } else {
939 if (
940 #if JS_BITS_PER_WORD == 32
941 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
942 #endif
943 !(vector = (void **)
944 realloc(array->vector, capacity * sizeof(void *)))) {
945 if (cx)
946 JS_ReportOutOfMemory(cx);
947 return JS_FALSE;
948 }
949 }
950 array->capacity = JSXML_PRESET_CAPACITY | capacity;
951 array->vector = vector;
952 return JS_TRUE;
953 }
954
955 static void
956 XMLArrayTrim(JSXMLArray *array)
957 {
958 if (array->capacity & JSXML_PRESET_CAPACITY)
959 return;
960 if (array->length < array->capacity)
961 XMLArraySetCapacity(NULL, array, array->length);
962 }
963
964 static JSBool
965 XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
966 {
967 array->length = array->capacity = 0;
968 array->vector = NULL;
969 array->cursors = NULL;
970 return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
971 }
972
973 static void
974 XMLArrayFinish(JSContext *cx, JSXMLArray *array)
975 {
976 JSXMLArrayCursor *cursor;
977
978 JS_free(cx, array->vector);
979
980 while ((cursor = array->cursors) != NULL)
981 XMLArrayCursorFinish(cursor);
982
983 #ifdef DEBUG
984 memset(array, 0xd5, sizeof *array);
985 #endif
986 }
987
988 #define XML_NOT_FOUND ((uint32) -1)
989
990 static uint32
991 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
992 {
993 void **vector;
994 uint32 i, n;
995
996 /* The identity op must not reallocate array->vector. */
997 vector = array->vector;
998 if (identity) {
999 for (i = 0, n = array->length; i < n; i++) {
1000 if (identity(vector[i], elt))
1001 return i;
1002 }
1003 } else {
1004 for (i = 0, n = array->length; i < n; i++) {
1005 if (vector[i] == elt)
1006 return i;
1007 }
1008 }
1009 return XML_NOT_FOUND;
1010 }
1011
1012 /*
1013 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1014 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1015 * should be greater than increment.
1016 */
1017 #define LINEAR_THRESHOLD 256
1018 #define LINEAR_INCREMENT 32
1019
1020 static JSBool
1021 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
1022 {
1023 uint32 capacity, i;
1024 int log2;
1025 void **vector;
1026
1027 if (index >= array->length) {
1028 if (index >= JSXML_CAPACITY(array)) {
1029 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1030 capacity = index + 1;
1031 if (index >= LINEAR_THRESHOLD) {
1032 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
1033 } else {
1034 JS_CEILING_LOG2(log2, capacity);
1035 capacity = JS_BIT(log2);
1036 }
1037 if (
1038 #if JS_BITS_PER_WORD == 32
1039 (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1040 #endif
1041 !(vector = (void **)
1042 realloc(array->vector, capacity * sizeof(void *)))) {
1043 JS_ReportOutOfMemory(cx);
1044 return JS_FALSE;
1045 }
1046 array->capacity = capacity;
1047 array->vector = vector;
1048 for (i = array->length; i < index; i++)
1049 vector[i] = NULL;
1050 }
1051 array->length = index + 1;
1052 }
1053
1054 array->vector[index] = elt;
1055 return JS_TRUE;
1056 }
1057
1058 static JSBool
1059 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1060 {
1061 uint32 j;
1062 JSXMLArrayCursor *cursor;
1063
1064 j = array->length;
1065 JS_ASSERT(i <= j);
1066 if (!XMLArraySetCapacity(cx, array, j + n))
1067 return JS_FALSE;
1068
1069 array->length = j + n;
1070 JS_ASSERT(n != (uint32)-1);
1071 while (j != i) {
1072 --j;
1073 array->vector[j + n] = array->vector[j];
1074 }
1075
1076 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1077 if (cursor->index > i)
1078 cursor->index += n;
1079 }
1080 return JS_TRUE;
1081 }
1082
1083 static void *
1084 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1085 {
1086 uint32 length;
1087 void **vector, *elt;
1088 JSXMLArrayCursor *cursor;
1089
1090 length = array->length;
1091 if (index >= length)
1092 return NULL;
1093
1094 vector = array->vector;
1095 elt = vector[index];
1096 if (compress) {
1097 while (++index < length)
1098 vector[index-1] = vector[index];
1099 array->length = length - 1;
1100 array->capacity = JSXML_CAPACITY(array);
1101 } else {
1102 vector[index] = NULL;
1103 }
1104
1105 for (cursor = array->cursors; cursor; cursor = cursor->next) {
1106 if (cursor->index > index)
1107 --cursor->index;
1108 }
1109 return elt;
1110 }
1111
1112 static void
1113 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1114 {
1115 void **vector;
1116
1117 JS_ASSERT(!array->cursors);
1118 if (length >= array->length)
1119 return;
1120
1121 if (length == 0) {
1122 if (array->vector)
1123 free(array->vector);
1124 vector = NULL;
1125 } else {
1126 vector = (void **) realloc(array->vector, length * sizeof(void *));
1127 if (!vector)
1128 return;
1129 }
1130
1131 if (array->length > length)
1132 array->length = length;
1133 array->capacity = length;
1134 array->vector = vector;
1135 }
1136
1137 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1138 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1139 XML_NOT_FOUND)
1140 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1141 ? (t *) (a)->vector[i] \
1142 : NULL)
1143 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1144 if ((a)->length <= (i)) \
1145 (a)->length = (i) + 1; \
1146 ((a)->vector[i] = (void *)(e)); \
1147 JS_END_MACRO
1148 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1149 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1150 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1151 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1152 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1153
1154 /*
1155 * Define XML setting property strings and constants early, so everyone can
1156 * use the same names and their magic numbers (tinyids, flags).
1157 */
1158 static const char js_ignoreComments_str[] = "ignoreComments";
1159 static const char js_ignoreProcessingInstructions_str[]
1160 = "ignoreProcessingInstructions";
1161 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1162 static const char js_prettyPrinting_str[] = "prettyPrinting";
1163 static const char js_prettyIndent_str[] = "prettyIndent";
1164
1165 /*
1166 * NB: These XML static property tinyids must
1167 * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1168 * (b) index their corresponding xml_static_props array elements.
1169 * Don't change 'em!
1170 */
1171 enum xml_static_tinyid {
1172 XML_IGNORE_COMMENTS,
1173 XML_IGNORE_PROCESSING_INSTRUCTIONS,
1174 XML_IGNORE_WHITESPACE,
1175 XML_PRETTY_PRINTING,
1176 XML_PRETTY_INDENT
1177 };
1178
1179 static JSBool
1180 xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1181 {
1182 return JS_TRUE;
1183 }
1184
1185 static JSBool
1186 xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1187 {
1188 uint8 flag;
1189
1190 JS_ASSERT(JSVAL_IS_INT(id));
1191
1192 flag = JS_BIT(JSVAL_TO_INT(id));
1193 if (js_ValueToBoolean(*vp))
1194 cx->xmlSettingFlags |= flag;
1195 else
1196 cx->xmlSettingFlags &= ~flag;
1197 return JS_TRUE;
1198 }
1199
1200 static JSPropertySpec xml_static_props[] = {
1201 {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT,
1202 xml_setting_getter, xml_setting_setter},
1203 {js_ignoreProcessingInstructions_str,
1204 XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
1205 xml_setting_getter, xml_setting_setter},
1206 {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
1207 xml_setting_getter, xml_setting_setter},
1208 {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT,
1209 xml_setting_getter, xml_setting_setter},
1210 {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT,
1211 xml_setting_getter, NULL},
1212 {0,0,0,0,0}
1213 };
1214
1215 /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1216 #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1217 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1218 JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1219 #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1220 #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1221 #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1222
1223 /*
1224 * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1225 * This flag means a couple of things:
1226 *
1227 * - The top JSXML created for a parse tree must have an object owning it.
1228 *
1229 * - That the default namespace normally inherited from the temporary
1230 * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1231 * string must, in the case of a precompiled XML object tree, inherit via
1232 * ad-hoc code in ParseNodeToXML.
1233 *
1234 * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1235 */
1236 #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1237
1238 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1239 #define IS_XML(str) \
1240 (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
1241
1242 #define IS_XMLNS(str) \
1243 (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1244
1245 #define IS_XML_CHARS(chars) \
1246 (JS_TOLOWER((chars)[0]) == 'x' && \
1247 JS_TOLOWER((chars)[1]) == 'm' && \
1248 JS_TOLOWER((chars)[2]) == 'l')
1249
1250 #define HAS_NS_AFTER_XML(chars) \
1251 (JS_TOLOWER((chars)[3]) == 'n' && \
1252 JS_TOLOWER((chars)[4]) == 's')
1253
1254 #define IS_XMLNS_CHARS(chars) \
1255 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1256
1257 #define STARTS_WITH_XML(chars,length) \
1258 (length >= 3 && IS_XML_CHARS(chars))
1259
1260 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1261 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1262
1263 static JSObject *
1264 ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn,
1265 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1266 {
1267 JSContext *cx = jsc->context;
1268 JSString *str, *uri, *prefix, *localName;
1269 size_t length, offset;
1270 const jschar *start, *limit, *colon;
1271 uint32 n;
1272 JSObject *ns;
1273 JSString *nsprefix;
1274
1275 JS_ASSERT(pn->pn_arity == PN_NULLARY);
1276 str = ATOM_TO_STRING(pn->pn_atom);
1277 JSSTRING_CHARS_AND_LENGTH(str, start, length);
1278 JS_ASSERT(length != 0 && *start != '@');
1279 JS_ASSERT(length != 1 || *start != '*');
1280
1281 uri = cx->runtime->emptyString;
1282 limit = start + length;
1283 colon = js_strchr_limit(start, ':', limit);
1284 if (colon) {
1285 offset = PTRDIFF(colon, start, jschar);
1286 prefix = js_NewDependentString(cx, str, 0, offset);
1287 if (!prefix)
1288 return NULL;
1289
1290 if (STARTS_WITH_XML(start, offset)) {
1291 if (offset == 3) {
1292 uri = JS_InternString(cx, xml_namespace_str);
1293 if (!uri)
1294 return NULL;
1295 } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1296 uri = JS_InternString(cx, xmlns_namespace_str);
1297 if (!uri)
1298 return NULL;
1299 } else {
1300 uri = NULL;
1301 }
1302 } else {
1303 uri = NULL;
1304 n = inScopeNSes->length;
1305 while (n != 0) {
1306 --n;
1307 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1308 nsprefix = GetPrefix(ns);
1309 if (nsprefix && js_EqualStrings(nsprefix, prefix)) {
1310 uri = GetURI(ns);
1311 break;
1312 }
1313 }
1314 }
1315
1316 if (!uri) {
1317 js_ReportCompileErrorNumber(jsc->context, &jsc->tokenStream, pn,
1318 JSREPORT_ERROR,
1319 JSMSG_BAD_XML_NAMESPACE,
1320 js_ValueToPrintableString(jsc->context,
1321 STRING_TO_JSVAL(prefix)));
1322 return NULL;
1323 }
1324
1325 localName = js_NewStringCopyN(jsc->context, colon + 1, length - (offset + 1));
1326 if (!localName)
1327 return NULL;
1328 } else {
1329 if (isAttributeName) {
1330 /*
1331 * An unprefixed attribute is not in any namespace, so set prefix
1332 * as well as uri to the empty string.
1333 */
1334 prefix = uri;
1335 } else {
1336 /*
1337 * Loop from back to front looking for the closest declared default
1338 * namespace.
1339 */
1340 n = inScopeNSes->length;
1341 while (n != 0) {
1342 --n;
1343 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1344 nsprefix = GetPrefix(ns);
1345 if (!nsprefix || IS_EMPTY(nsprefix)) {
1346 uri = GetURI(ns);
1347 break;
1348 }
1349 }
1350 prefix = IS_EMPTY(uri) ? jsc->context->runtime->emptyString : NULL;
1351 }
1352 localName = str;
1353 }
1354
1355 return NewXMLQName(jsc->context, uri, prefix, localName);
1356 }
1357
1358 static JSString *
1359 ChompXMLWhitespace(JSContext *cx, JSString *str)
1360 {
1361 size_t length, newlength, offset;
1362 const jschar *cp, *start, *end;
1363 jschar c;
1364
1365 JSSTRING_CHARS_AND_LENGTH(str, start, length);
1366 for (cp = start, end = cp + length; cp < end; cp++) {
1367 c = *cp;
1368 if (!JS_ISXMLSPACE(c))
1369 break;
1370 }
1371 while (end > cp) {
1372 c = end[-1];
1373 if (!JS_ISXMLSPACE(c))
1374 break;
1375 --end;
1376 }
1377 newlength = PTRDIFF(end, cp, jschar);
1378 if (newlength == length)
1379 return str;
1380 offset = PTRDIFF(cp, start, jschar);
1381 return js_NewDependentString(cx, str, offset, newlength);
1382 }
1383
1384 static JSXML *
1385 ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
1386 JSXMLArray *inScopeNSes, uintN flags)
1387 {
1388 JSContext *cx = jsc->context;
1389 JSXML *xml, *kid, *attr, *attrj;
1390 JSString *str;
1391 uint32 length, n, i, j;
1392 JSParseNode *pn2, *pn3, *head, **pnp;
1393 JSObject *ns;
1394 JSObject *qn, *attrjqn;
1395 JSXMLClass xml_class;
1396 int stackDummy;
1397
1398 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1399 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR,
1400 JSMSG_OVER_RECURSED);
1401 return NULL;
1402 }
1403
1404 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1405
1406 /*
1407 * Cases return early to avoid common code that gets an outermost xml's
1408 * object, which protects GC-things owned by xml and its descendants from
1409 * garbage collection.
1410 */
1411 xml = NULL;
1412 if (!js_EnterLocalRootScope(cx))
1413 return NULL;
1414 switch (pn->pn_type) {
1415 case TOK_XMLELEM:
1416 length = inScopeNSes->length;
1417 pn2 = pn->pn_head;
1418 xml = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
1419 if (!xml)
1420 goto fail;
1421
1422 flags &= ~XSF_PRECOMPILED_ROOT;
1423 n = pn->pn_count;
1424 JS_ASSERT(n >= 2);
1425 n -= 2;
1426 if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1427 goto fail;
1428
1429 i = 0;
1430 while ((pn2 = pn2->pn_next) != NULL) {
1431 if (!pn2->pn_next) {
1432 /* Don't append the end tag! */
1433 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1434 break;
1435 }
1436
1437 if ((flags & XSF_IGNORE_WHITESPACE) &&
1438 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1439 --n;
1440 continue;
1441 }
1442
1443 kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
1444 if (kid == PN2X_SKIP_CHILD) {
1445 --n;
1446 continue;
1447 }
1448
1449 if (!kid)
1450 goto fail;
1451
1452 /* Store kid in xml right away, to protect it from GC. */
1453 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1454 kid->parent = xml;
1455 ++i;
1456
1457 /* XXX where is this documented in an XML spec, or in E4X? */
1458 if ((flags & XSF_IGNORE_WHITESPACE) &&
1459 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1460 str = ChompXMLWhitespace(cx, kid->xml_value);
1461 if (!str)
1462 goto fail;
1463 kid->xml_value = str;
1464 }
1465 }
1466
1467 JS_ASSERT(i == n);
1468 if (n < pn->pn_count - 2)
1469 XMLArrayTrim(&xml->xml_kids);
1470 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1471 break;
1472
1473 case TOK_XMLLIST:
1474 xml = js_NewXML(cx, JSXML_CLASS_LIST);
1475 if (!xml)
1476 goto fail;
1477
1478 n = pn->pn_count;
1479 if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1480 goto fail;
1481
1482 i = 0;
1483 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1484 /*
1485 * Always ignore insignificant whitespace in lists -- we shouldn't
1486 * condition this on an XML.ignoreWhitespace setting when the list
1487 * constructor is XMLList (note XML/XMLList unification hazard).
1488 */
1489 if (pn2->pn_type == TOK_XMLSPACE) {
1490 --n;
1491 continue;
1492 }
1493
1494 kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
1495 if (kid == PN2X_SKIP_CHILD) {
1496 --n;
1497 continue;
1498 }
1499
1500 if (!kid)
1501 goto fail;
1502
1503 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1504 ++i;
1505 }
1506
1507 if (n < pn->pn_count)
1508 XMLArrayTrim(&xml->xml_kids);
1509 break;
1510
1511 case TOK_XMLSTAGO:
1512 case TOK_XMLPTAGC:
1513 length = inScopeNSes->length;
1514 pn2 = pn->pn_head;
1515 JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1516 if (pn2->pn_arity == PN_LIST)
1517 goto syntax;
1518
1519 xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1520 if (!xml)
1521 goto fail;
1522
1523 /* First pass: check syntax and process namespace declarations. */
1524 JS_ASSERT(pn->pn_count >= 1);
1525 n = pn->pn_count - 1;
1526 pnp = &pn2->pn_next;
1527 head = *pnp;
1528 while ((pn2 = *pnp) != NULL) {
1529 size_t length;
1530 const jschar *chars;
1531
1532 if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1533 goto syntax;
1534
1535 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1536 for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1537 if (pn3->pn_atom == pn2->pn_atom) {
1538 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2,
1539 JSREPORT_ERROR,
1540 JSMSG_DUPLICATE_XML_ATTR,
1541 js_ValueToPrintableString(cx,
1542 ATOM_KEY(pn2->pn_atom)));
1543 goto fail;
1544 }
1545 }
1546
1547 str = ATOM_TO_STRING(pn2->pn_atom);
1548 pn2 = pn2->pn_next;
1549 JS_ASSERT(pn2);
1550 if (pn2->pn_type != TOK_XMLATTR)
1551 goto syntax;
1552
1553 JSSTRING_CHARS_AND_LENGTH(str, chars, length);
1554 if (length >= 5 &&
1555 IS_XMLNS_CHARS(chars) &&
1556 (length == 5 || chars[5] == ':')) {
1557 JSString *uri, *prefix;
1558
1559 uri = ATOM_TO_STRING(pn2->pn_atom);
1560 if (length == 5) {
1561 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1562 prefix = cx->runtime->emptyString;
1563 } else {
1564 prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1565 if (!prefix)
1566 goto fail;
1567 }
1568
1569 /*
1570 * Once the new ns is appended to xml->xml_namespaces, it is
1571 * protected from GC by the object that owns xml -- which is
1572 * either xml->object if outermost, or the object owning xml's
1573 * oldest ancestor if !outermost.
1574 */
1575 ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1576 if (!ns)
1577 goto fail;
1578
1579 /*
1580 * Don't add a namespace that's already in scope. If someone
1581 * extracts a child property from its parent via [[Get]], then
1582 * we enforce the invariant, noted many times in ECMA-357, that
1583 * the child's namespaces form a possibly-improper superset of
1584 * its ancestors' namespaces.
1585 */
1586 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1587 if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1588 !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1589 goto fail;
1590 }
1591 }
1592
1593 JS_ASSERT(n >= 2);
1594 n -= 2;
1595 *pnp = pn2->pn_next;
1596 /* XXXbe recycle pn2 */
1597 continue;
1598 }
1599
1600 pnp = &pn2->pn_next;
1601 }
1602
1603 /*
1604 * If called from js_ParseNodeToXMLObject, emulate the effect of the
1605 * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1606 * the String Type" (ECMA-357 10.3.1).
1607 */
1608 if (flags & XSF_PRECOMPILED_ROOT) {
1609 JS_ASSERT(length >= 1);
1610 ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSObject);
1611 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
1612 namespace_identity));
1613 ns = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_FALSE);
1614 if (!ns)
1615 goto fail;
1616 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1617 goto fail;
1618 }
1619 XMLArrayTrim(&xml->xml_namespaces);
1620
1621 /* Second pass: process tag name and attributes, using namespaces. */
1622 pn2 = pn->pn_head;
1623 qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_FALSE);
1624 if (!qn)
1625 goto fail;
1626 xml->name = qn;
1627
1628 JS_ASSERT((n & 1) == 0);
1629 n >>= 1;
1630 if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
1631 goto fail;
1632
1633 for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1634 qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_TRUE);
1635 if (!qn) {
1636 xml->xml_attrs.length = i;
1637 goto fail;
1638 }
1639
1640 /*
1641 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1642 * this time checking local name and namespace URI.
1643 */
1644 for (j = 0; j < i; j++) {
1645 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1646 attrjqn = attrj->name;
1647 if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
1648 js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
1649 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2,
1650 JSREPORT_ERROR,
1651 JSMSG_DUPLICATE_XML_ATTR,
1652 js_ValueToPrintableString(cx,
1653 ATOM_KEY(pn2->pn_atom)));
1654 goto fail;
1655 }
1656 }
1657
1658 pn2 = pn2->pn_next;
1659 JS_ASSERT(pn2);
1660 JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1661
1662 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1663 if (!attr)
1664 goto fail;
1665
1666 XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1667 attr->parent = xml;
1668 attr->name = qn;
1669 attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1670 }
1671
1672 /* Point tag closes its own namespace scope. */
1673 if (pn->pn_type == TOK_XMLPTAGC)
1674 XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1675 break;
1676
1677 case TOK_XMLSPACE:
1678 case TOK_XMLTEXT:
1679 case TOK_XMLCDATA:
1680 case TOK_XMLCOMMENT:
1681 case TOK_XMLPI:
1682 str = ATOM_TO_STRING(pn->pn_atom);
1683 qn = NULL;
1684 if (pn->pn_type == TOK_XMLCOMMENT) {
1685 if (flags & XSF_IGNORE_COMMENTS)
1686 goto skip_child;
1687 xml_class = JSXML_CLASS_COMMENT;
1688 } else if (pn->pn_type == TOK_XMLPI) {
1689 if (IS_XML(str)) {
1690 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn,
1691 JSREPORT_ERROR,
1692 JSMSG_RESERVED_ID,
1693 js_ValueToPrintableString(cx,
1694 STRING_TO_JSVAL(str)));
1695 goto fail;
1696 }
1697
1698 if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1699 goto skip_child;
1700
1701 qn = ParseNodeToQName(jsc, pn, inScopeNSes, JS_FALSE);
1702 if (!qn)
1703 goto fail;
1704
1705 str = pn->pn_atom2
1706 ? ATOM_TO_STRING(pn->pn_atom2)
1707 : cx->runtime->emptyString;
1708 xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1709 } else {
1710 /* CDATA section content, or element text. */
1711 xml_class = JSXML_CLASS_TEXT;
1712 }
1713
1714 xml = js_NewXML(cx, xml_class);
1715 if (!xml)
1716 goto fail;
1717 xml->name = qn;
1718 if (pn->pn_type == TOK_XMLSPACE)
1719 xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1720 xml->xml_value = str;
1721 break;
1722
1723 default:
1724 goto syntax;
1725 }
1726
1727 js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
1728 if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
1729 return NULL;
1730 return xml;
1731
1732 skip_child:
1733 js_LeaveLocalRootScope(cx);
1734 return PN2X_SKIP_CHILD;
1735
1736 #undef PN2X_SKIP_CHILD
1737
1738 syntax:
1739 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR,
1740 JSMSG_BAD_XML_MARKUP);
1741 fail:
1742 js_LeaveLocalRootScope(cx);
1743 return NULL;
1744 }
1745
1746 /*
1747 * XML helper, object-ops, and library functions. We start with the helpers,
1748 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1749 */
1750 static JSBool
1751 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1752 {
1753 jsval v;
1754
1755 if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
1756 return JS_FALSE;
1757 if (!VALUE_IS_FUNCTION(cx, v)) {
1758 *vp = JSVAL_VOID;
1759 return JS_TRUE;
1760 }
1761 return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1762 }
1763
1764 static JSBool
1765 FillSettingsCache(JSContext *cx)
1766 {
1767 int i;
1768 const char *name;
1769 jsval v;
1770
1771 /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1772 for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
1773 name = xml_static_props[i].name;
1774 if (!GetXMLSetting(cx, name, &v))
1775 return JS_FALSE;
1776 if (js_ValueToBoolean(v))
1777 cx->xmlSettingFlags |= JS_BIT(i);
1778 else
1779 cx->xmlSettingFlags &= ~JS_BIT(i);
1780 }
1781
1782 cx->xmlSettingFlags |= XSF_CACHE_VALID;
1783 return JS_TRUE;
1784 }
1785
1786 static JSBool
1787 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1788 {
1789 int i;
1790
1791 if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
1792 return JS_FALSE;
1793
1794 for (i = 0; xml_static_props[i].name; i++) {
1795 if (!strcmp(xml_static_props[i].name, name)) {
1796 *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
1797 return JS_TRUE;
1798 }
1799 }
1800 *bp = JS_FALSE;
1801 return JS_TRUE;
1802 }
1803
1804 static JSBool
1805 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1806 {
1807 jsval v;
1808
1809 return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1810 }
1811
1812 static JSBool
1813 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1814 {
1815 JSBool flag;
1816
1817 /* Just get the first flag to validate the setting flags cache. */
1818 if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
1819 return JS_FALSE;
1820 *flagsp = cx->xmlSettingFlags;
1821 return JS_TRUE;
1822 }
1823
1824 static JSXML *
1825 ParseXMLSource(JSContext *cx, JSString *src)
1826 {
1827 jsval nsval;
1828 JSString *uri;
1829 size_t urilen, srclen, length, offset, dstlen;
1830 jschar *chars;
1831 const jschar *srcp, *endp;
1832 JSXML *xml;
1833 const char *filename;
1834 uintN lineno;
1835 JSStackFrame *fp;
1836 JSOp op;
1837 JSParseNode *pn;
1838 JSXMLArray nsarray;
1839 uintN flags;
1840
1841 static const char prefix[] = "<parent xmlns=\"";
1842 static const char middle[] = "\">";
1843 static const char suffix[] = "</parent>";
1844
1845 #define constrlen(constr) (sizeof(constr) - 1)
1846
1847 if (!js_GetDefaultXMLNamespace(cx, &nsval))
1848 return NULL;
1849 uri = GetURI(JSVAL_TO_OBJECT(nsval));
1850 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1851
1852 urilen = JSSTRING_LENGTH(uri);
1853 srclen = JSSTRING_LENGTH(src);
1854 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1855 constrlen(suffix);
1856
1857 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1858 if (!chars)
1859 return NULL;
1860
1861 dstlen = length;
1862 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1863 offset = dstlen;
1864 js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
1865 offset += urilen;
1866 dstlen = length - offset + 1;
1867 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1868 &dstlen);
1869 offset += dstlen;
1870 srcp = JSSTRING_CHARS(src);
1871 js_strncpy(chars + offset, srcp, srclen);
1872 offset += srclen;
1873 dstlen = length - offset + 1;
1874 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1875 &dstlen);
1876 chars [offset + dstlen] = 0;
1877
1878 xml = NULL;
1879 for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down)
1880 JS_ASSERT(!fp->script);
1881 filename = NULL;
1882 lineno = 1;
1883 if (fp) {
1884 op = (JSOp) *fp->regs->pc;
1885 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1886 filename = fp->script->filename;
1887 lineno = js_FramePCToLineNumber(cx, fp);
1888 for (endp = srcp + srclen; srcp < endp; srcp++) {
1889 if (*srcp == '\n')
1890 --lineno;
1891 }
1892 }
1893 }
1894
1895 {
1896 JSCompiler jsc(cx);
1897 if (jsc.init(chars, length, NULL, filename, lineno)) {
1898 pn = jsc.parseXMLText(js_GetTopStackFrame(cx)->scopeChain, false);
1899 if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1900 if (GetXMLSettingFlags(cx, &flags))
1901 xml = ParseNodeToXML(&jsc, pn, &nsarray, flags);
1902
1903 XMLArrayFinish(cx, &nsarray);
1904 }
1905 }
1906 }
1907
1908 JS_free(cx, chars);
1909 return xml;
1910
1911 #undef constrlen
1912 }
1913
1914 /*
1915 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1916 *
1917 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1918 * the constraint:
1919 *
1920 * for all x belonging to XML:
1921 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1922 *
1923 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1924 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1925 *
1926 * Same goes for 10.4.1 Step 7(a).
1927 *
1928 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1929 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1930 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1931 * undeclared namespaces associated with x not belonging to ancestorNS.
1932 */
1933 static JSXML *
1934 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1935 {
1936 JSObject *ns;
1937
1938 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1939 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1940 if (!ns || !xml)
1941 return xml;
1942 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1943 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1944 return NULL;
1945 ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1946 }
1947 xml->parent = NULL;
1948 return xml;
1949 }
1950
1951 static JSObject *
1952 ToXML(JSContext *cx, jsval v)
1953 {
1954 JSObject *obj;
1955 JSXML *xml;
1956 JSClass *clasp;
1957 JSString *str;
1958 uint32 length;
1959
1960 if (JSVAL_IS_PRIMITIVE(v)) {
1961 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1962 goto bad;
1963 } else {
1964 obj = JSVAL_TO_OBJECT(v);
1965 if (OBJECT_IS_XML(cx, obj)) {
1966 xml = (JSXML *) JS_GetPrivate(cx, obj);
1967 if (xml->xml_class == JSXML_CLASS_LIST) {
1968 if (xml->xml_kids.length != 1)
1969 goto bad;
1970 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1971 if (xml) {
1972 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1973 return js_GetXMLObject(cx, xml);
1974 }
1975 }
1976 return obj;
1977 }
1978
1979 clasp = OBJ_GET_CLASS(cx, obj);
1980 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1981 JS_ASSERT(0);
1982 }
1983
1984 if (clasp != &js_StringClass &&
1985 clasp != &js_NumberClass &&
1986 clasp != &js_BooleanClass) {
1987 goto bad;
1988 }
1989 }
1990
1991 str = js_ValueToString(cx, v);
1992 if (!str)
1993 return NULL;
1994 if (IS_EMPTY(str)) {
1995 length = 0;
1996 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1997 xml = NULL;
1998 #endif
1999 } else {
2000 xml = ParseXMLSource(cx, str);
2001 if (!xml)
2002 return NULL;
2003 length = JSXML_LENGTH(xml);
2004 }
2005
2006 if (length == 0) {
2007 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2008 if (!obj)
2009 return NULL;
2010 } else if (length == 1) {
2011 xml = OrphanXMLChild(cx, xml, 0);
2012 if (!xml)
2013 return NULL;
2014 obj = js_GetXMLObject(cx, xml);
2015 if (!obj)
2016 return NULL;
2017 } else {
2018 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2019 return NULL;
2020 }
2021 return obj;
2022
2023 bad:
2024 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2025 JSDVG_IGNORE_STACK, v, NULL);
2026 return NULL;
2027 }
2028
2029 static JSBool
2030 Append(JSContext *cx, JSXML *list, JSXML *kid);
2031
2032 static JSObject *
2033 ToXMLList(JSContext *cx, jsval v)
2034 {
2035 JSObject *obj, *listobj;
2036 JSXML *xml, *list, *kid;
2037 JSClass *clasp;
2038 JSString *str;
2039 uint32 i, length;
2040
2041 if (JSVAL_IS_PRIMITIVE(v)) {
2042 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2043 goto bad;
2044 } else {
2045 obj = JSVAL_TO_OBJECT(v);
2046 if (OBJECT_IS_XML(cx, obj)) {
2047 xml = (JSXML *) JS_GetPrivate(cx, obj);
2048 if (xml->xml_class != JSXML_CLASS_LIST) {
2049 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2050 if (!listobj)
2051 return NULL;
2052 list = (JSXML *) JS_GetPrivate(cx, listobj);
2053 if (!Append(cx, list, xml))
2054 return NULL;
2055 return listobj;
2056 }
2057 return obj;
2058 }
2059
2060 clasp = OBJ_GET_CLASS(cx, obj);
2061 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2062 JS_ASSERT(0);
2063 }
2064
2065 if (clasp != &js_StringClass &&
2066 clasp != &js_NumberClass &&
2067 clasp != &js_BooleanClass) {
2068 goto bad;
2069 }
2070 }
2071
2072 str = js_ValueToString(cx, v);
2073 if (!str)
2074 return NULL;
2075 if (IS_EMPTY(str)) {
2076 xml = NULL;
2077 length = 0;
2078 } else {
2079 if (!js_EnterLocalRootScope(cx))
2080 return NULL;
2081 xml = ParseXMLSource(cx, str);
2082 if (!xml) {
2083 js_LeaveLocalRootScope(cx);
2084 return NULL;
2085 }
2086 length = JSXML_LENGTH(xml);
2087 }
2088
2089 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2090 if (listobj) {
2091 list = (JSXML *) JS_GetPrivate(cx, listobj);
2092 for (i = 0; i < length; i++) {
2093 kid = OrphanXMLChild(cx, xml, i);
2094 if (!kid || !Append(cx, list, kid)) {
2095 listobj = NULL;
2096 break;
2097 }
2098 }
2099 }
2100
2101 if (xml)
2102 js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2103 return listobj;
2104
2105 bad:
2106 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2107 JSDVG_IGNORE_STACK, v, NULL);
2108 return NULL;
2109 }
2110
2111 /*
2112 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2113 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2114 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2115 * MakeXMLSpecialString subroutine.
2116 *
2117 * These functions take ownership of sb->base, if sb is non-null, in all cases
2118 * of success or failure.
2119 */
2120 static JSString *
2121 MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2122 JSString *str, JSString *str2,
2123 const jschar *prefix, size_t prefixlength,
2124 const jschar *suffix, size_t suffixlength)
2125 {
2126 JSStringBuffer localSB;
2127 size_t length, length2, newlength;
2128 jschar *bp, *base;
2129
2130 if (!sb) {
2131 sb = &localSB;
2132 js_InitStringBuffer(sb);
2133 }
2134
2135 length = JSSTRING_LENGTH(str);
2136 length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2137 newlength = STRING_BUFFER_OFFSET(sb) +
2138 prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2139 suffixlength;
2140 bp = base = (jschar *)
2141 JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2142 if (!bp) {
2143 js_FinishStringBuffer(sb);
2144 return NULL;
2145 }
2146
2147 bp += STRING_BUFFER_OFFSET(sb);
2148 js_strncpy(bp, prefix, prefixlength);
2149 bp += prefixlength;
2150 js_strncpy(bp, JSSTRING_CHARS(str), length);
2151 bp += length;
2152 if (length2 != 0) {
2153 *bp++ = (jschar) ' ';
2154 js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2155 bp += length2;
2156 }
2157 js_strncpy(bp, suffix, suffixlength);
2158 bp[suffixlength] = 0;
2159
2160 str = js_NewString(cx, base, newlength);
2161 if (!str)
2162 free(base);
2163 return str;
2164 }
2165
2166 static JSString *
2167 MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2168 {
2169 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2170 'C', 'D', 'A', 'T', 'A',
2171 '['};
2172 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2173
2174 return MakeXMLSpecialString(cx, sb, str, NULL,
2175 cdata_prefix_ucNstr, 9,
2176 cdata_suffix_ucNstr, 3);
2177 }
2178
2179 static JSString *
2180 MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2181 {
2182 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2183 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2184
2185 return MakeXMLSpecialString(cx, sb, str, NULL,
2186 comment_prefix_ucNstr, 4,
2187 comment_suffix_ucNstr, 3);
2188 }
2189
2190 static JSString *
2191 MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2192 JSString *value)
2193 {
2194 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2195 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2196
2197 return MakeXMLSpecialString(cx, sb, name, value,
2198 pi_prefix_ucNstr, 2,
2199 pi_suffix_ucNstr, 2);
2200 }
2201
2202 /*
2203 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2204 * equals, a double quote, an attribute value, and a closing double quote.
2205 */
2206 static void
2207 AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2208 {
2209 js_AppendChar(sb, '=');
2210 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2211 if (!valstr) {
2212 if (STRING_BUFFER_OK(sb)) {
2213 free(sb->base);
2214 sb->base = STRING_BUFFER_ERROR_BASE;
2215 }
2216 return;
2217 }
2218 js_AppendJSString(sb, valstr);
2219 }
2220
2221 /*
2222 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2223 *
2224 * This function takes ownership of sb->base, if sb is non-null, in all cases
2225 * of success or failure.
2226 */
2227 static JSString *
2228 EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2229 {
2230 size_t length, newlength;
2231 const jschar *cp, *start, *end;
2232 jschar c;
2233
2234 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2235 newlength = length;
2236 for (cp = start, end = cp + length; cp < end; cp++) {
2237 c = *cp;
2238 if (c == '<' || c == '>')
2239 newlength += 3;
2240 else if (c == '&')
2241 newlength += 4;
2242
2243 if (newlength < length) {
2244 js_ReportAllocationOverflow(cx);
2245 return NULL;
2246 }
2247 }
2248 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2249 JSStringBuffer localSB;
2250 if (!sb) {
2251 sb = &localSB;
2252 js_InitStringBuffer(sb);
2253 }
2254 if (!sb->grow(sb, newlength)) {
2255 JS_ReportOutOfMemory(cx);
2256 return NULL;
2257 }
2258 for (cp = start; cp < end; cp++) {
2259 c = *cp;
2260 if (c == '<')
2261 js_AppendCString(sb, js_lt_entity_str);
2262 else if (c == '>')
2263 js_AppendCString(sb, js_gt_entity_str);
2264 else if (c == '&')
2265 js_AppendCString(sb, js_amp_entity_str);
2266 else
2267 js_AppendChar(sb, c);
2268 }
2269 JS_ASSERT(STRING_BUFFER_OK(sb));
2270 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2271 if (!str)
2272 js_FinishStringBuffer(sb);
2273 }
2274 return str;
2275 }
2276
2277 /*
2278 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2279 * This function takes ownership of sb->base, if sb is non-null, in all cases.
2280 */
2281 static JSString *
2282 EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str,
2283 JSBool quote)
2284 {
2285 size_t length, newlength;
2286 const jschar *cp, *start, *end;
2287 jschar c;
2288
2289 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2290 newlength = length + (quote ? 2 : 0);
2291 for (cp = start, end = cp + length; cp < end; cp++) {
2292 c = *cp;
2293 if (c == '"')
2294 newlength += 5;
2295 else if (c == '<')
2296 newlength += 3;
2297 else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2298 newlength += 4;
2299
2300 if (newlength < length) {
2301 js_ReportAllocationOverflow(cx);
2302 return NULL;
2303 }
2304 }
2305 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2306 JSStringBuffer localSB;
2307 if (!sb) {
2308 sb = &localSB;
2309 js_InitStringBuffer(sb);
2310 }
2311 if (!sb->grow(sb, newlength)) {
2312 JS_ReportOutOfMemory(cx);
2313 return NULL;
2314 }
2315 if (quote)
2316 js_AppendChar(sb, '"');
2317 for (cp = start; cp < end; cp++) {
2318 c = *cp;
2319 if (c == '"')
2320 js_AppendCString(sb, js_quot_entity_str);
2321 else if (c == '<')
2322 js_AppendCString(sb, js_lt_entity_str);
2323 else if (c == '&')
2324 js_AppendCString(sb, js_amp_entity_str);
2325 else if (c == '\n')
2326 js_AppendCString(sb, "&#xA;");
2327 else if (c == '\r')
2328 js_AppendCString(sb, "&#xD;");
2329 else if (c == '\t')
2330 js_AppendCString(sb, "&#x9;");
2331 else
2332 js_AppendChar(sb, c);
2333 }
2334 if (quote)
2335 js_AppendChar(sb, '"');
2336 JS_ASSERT(STRING_BUFFER_OK(sb));
2337 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2338 if (!str)
2339 js_FinishStringBuffer(sb);
2340 }
2341 return str;
2342 }
2343
2344 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2345 static JSObject *
2346 GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2347 {
2348 JSString *uri, *prefix, *nsprefix;
2349 JSObject *match, *ns;
2350 uint32 i, n;
2351 jsval argv[2];
2352
2353 uri = GetURI(qn);
2354 prefix = GetPrefix(qn);
2355 JS_ASSERT(uri);
2356 if (!uri) {
2357 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2358 JSMSG_BAD_XML_NAMESPACE,
2359 prefix
2360 ? js_ValueToPrintableString(cx,
2361 STRING_TO_JSVAL(prefix))
2362 : js_undefined_str);
2363 return NULL;
2364 }
2365
2366 /* Look for a matching namespace in inScopeNSes, if provided. */
2367 match = NULL;
2368 if (inScopeNSes) {
2369 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2370 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2371 if (!ns)
2372 continue;
2373
2374 /*
2375 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2376 * If we preserve prefixes, we must match null prefix against
2377 * an empty prefix of ns, in order to avoid generating redundant
2378 * prefixed and default namespaces for cases such as:
2379 *
2380 * x = <t xmlns="http://foo.com"/>
2381 * print(x.toXMLString());
2382 *
2383 * Per 10.3.2.1, the namespace attribute in t has an empty string
2384 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2385 *
2386 * 1. If the [local name] property of a is "xmlns"
2387 * a. Map ns.prefix to the empty string
2388 *
2389 * But t's name has a null prefix in this implementation, meaning
2390 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2391 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2392 * saying how "no value" maps to an ECMA-357 value -- but it must
2393 * map to the *undefined* prefix value).
2394 *
2395 * Since "" != undefined (or null, in the current implementation)
2396 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2397 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2398 * This spec bug leads to ToXMLString results that duplicate the
2399 * declared namespace.
2400 */
2401 if (js_EqualStrings(GetURI(ns), uri)) {
2402 nsprefix = GetPrefix(ns);
2403 if (nsprefix == prefix ||
2404 ((nsprefix && prefix)
2405 ? js_EqualStrings(nsprefix, prefix)
2406 : IS_EMPTY(nsprefix ? nsprefix : prefix))) {
2407 match = ns;
2408 break;
2409 }
2410 }
2411 }
2412 }
2413
2414 /* If we didn't match, make a new namespace from qn. */
2415 if (!match) {
2416 argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2417 argv[1] = STRING_TO_JSVAL(uri);
2418 ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2419 2, argv);
2420 if (!ns)
2421 return NULL;
2422 match = ns;
2423 }
2424 return match;
2425 }
2426
2427 static JSString *
2428 GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2429 {
2430 const jschar *cp, *start, *end;
2431 size_t length, newlength, offset;
2432 uint32 i, n, m, serial;
2433 jschar *bp, *dp;
2434 JSBool done;
2435 JSObject *ns;
2436 JSString *nsprefix, *prefix;
2437
2438 JS_ASSERT(!IS_EMPTY(uri));
2439
2440 /*
2441 * If there are no *declared* namespaces, skip all collision detection and
2442 * return a short prefix quickly; an example of such a situation:
2443 *
2444 * var x = <f/>;
2445 * var n = new Namespace("http://example.com/");
2446 * x.@n::att = "val";
2447 * x.toXMLString();
2448 *
2449 * This is necessary for various log10 uses below to be valid.
2450 */
2451 if (decls->length == 0)
2452 return JS_NewStringCopyZ(cx, "a");
2453
2454 /*
2455 * Try peeling off the last filename suffix or pathname component till
2456 * we have a valid XML name. This heuristic will prefer "xul" given
2457 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2458 * likely URI of the form ".../xbl2/2005".
2459 */
2460 JSSTRING_CHARS_AND_END(uri, start, end);
2461 cp = end;
2462 while (--cp > start) {
2463 if (*cp == '.' || *cp == '/' || *cp == ':') {
2464 ++cp;
2465 length = PTRDIFF(end, cp, jschar);
2466 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2467 break;
2468 end = --cp;
2469 }
2470 }
2471 length = PTRDIFF(end, cp, jschar);
2472
2473 /*
2474 * If the namespace consisted only of non-XML names or names that begin
2475 * case-insensitively with "xml", arbitrarily create a prefix consisting
2476 * of 'a's of size length (allowing dp-calculating code to work with or
2477 * without this branch executing) plus the space for storing a hyphen and
2478 * the serial number (avoiding reallocation if a collision happens).
2479 */
2480 bp = (jschar *) cp;
2481 newlength = length;
2482 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2483 newlength = length + 2 + (size_t) log10((double) decls->length);
2484 bp = (jschar *)
2485 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2486 if (!bp)
2487 return NULL;
2488
2489 bp[newlength] = 0;
2490 for (i = 0; i < newlength; i++)
2491 bp[i] = 'a';
2492 }
2493
2494 /*
2495 * Now search through decls looking for a collision. If we collide with
2496 * an existing prefix, start tacking on a hyphen and a serial number.
2497 */
2498 serial = 0;
2499 do {
2500 done = JS_TRUE;
2501 for (i = 0, n = decls->length; i < n; i++) {
2502 ns = XMLARRAY_MEMBER(decls, i, JSObject);
2503 if (ns && (nsprefix = GetPrefix(ns)) &&
2504 JSSTRING_LENGTH(nsprefix) == newlength &&
2505 !memcmp(JSSTRING_CHARS(nsprefix), bp,
2506 newlength * sizeof(jschar))) {
2507 if (bp == cp) {
2508 newlength = length + 2 + (size_t) log10((double) n);
2509 bp = (jschar *)
2510 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2511 if (!bp)
2512 return NULL;
2513 js_strncpy(bp, cp, length);
2514 }
2515
2516 ++serial;
2517 JS_ASSERT(serial <= n);
2518 dp = bp + length + 2 + (size_t) log10((double) serial);
2519 *dp = 0;
2520 for (m = serial; m != 0; m /= 10)
2521 *--dp = (jschar)('0' + m % 10);
2522 *--dp = '-';
2523 JS_ASSERT(dp == bp + length);
2524
2525 done = JS_FALSE;
2526 break;
2527 }
2528 }
2529 } while (!done);
2530
2531 if (bp == cp) {
2532 offset = PTRDIFF(cp, start, jschar);
2533 prefix = js_NewDependentString(cx, uri, offset, length);
2534 } else {
2535 prefix = js_NewString(cx, bp, newlength);
2536 if (!prefix)
2537 JS_free(cx, bp);
2538 }
2539 return prefix;
2540 }
2541
2542 static JSBool
2543 namespace_match(const void *a, const void *b)
2544 {
2545 const JSObject *nsa = (const JSObject *) a;
2546 const JSObject *nsb = (const JSObject *) b;
2547 JSString *prefixa, *prefixb = GetPrefix(nsb);
2548
2549 if (prefixb) {
2550 prefixa = GetPrefix(nsa);
2551 return prefixa && js_EqualStrings(prefixa, prefixb);
2552 }
2553 return js_EqualStrings(GetURI(nsa), GetURI(nsb));
2554 }
2555
2556 /* ECMA-357 10.2.1 and 10.2.2 */
2557 #define TO_SOURCE_FLAG 0x80000000
2558
2559 static JSString *
2560 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2561 uint32 indentLevel)
2562 {
2563 JSBool pretty, indentKids;
2564 JSStringBuffer sb;
2565 JSString *str, *prefix, *kidstr, *nsuri;
2566 JSXMLArrayCursor cursor;
2567 uint32 i, n, nextIndentLevel;
2568 JSXMLArray empty, decls, ancdecls;
2569 JSObject *ns, *ns2;
2570 JSXML *attr, *kid;
2571
2572 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2573 return NULL;
2574
2575 js_InitStringBuffer(&sb);
2576 if (pretty) {
2577 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2578
2579 if (!STRING_BUFFER_OK(&sb)) {
2580 JS_ReportOutOfMemory(cx);
2581 return NULL;
2582 }
2583 }
2584 str = NULL;
2585
2586 switch (xml->xml_class) {
2587 case JSXML_CLASS_TEXT:
2588 /* Step 4. */
2589 if (pretty) {
2590 str = ChompXMLWhitespace(cx, xml->xml_value);
2591 if (!str)
2592 return NULL;
2593 } else {
2594 str = xml->xml_value;
2595 }
2596 return EscapeElementValue(cx, &sb, str);
2597
2598 case JSXML_CLASS_ATTRIBUTE:
2599 /* Step 5. */
2600 return EscapeAttributeValue(cx, &sb, xml->xml_value,
2601 (indentLevel & TO_SOURCE_FLAG) != 0);
2602
2603 case JSXML_CLASS_COMMENT:
2604 /* Step 6. */
2605 return MakeXMLCommentString(cx, &sb, xml->xml_value);
2606
2607 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2608 /* Step 7. */
2609 return MakeXMLPIString(cx, &sb, GetLocalName(xml->name),
2610 xml->xml_value);
2611
2612 case JSXML_CLASS_LIST:
2613 /* ECMA-357 10.2.2. */
2614 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2615 i = 0;
2616 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2617 if (pretty && i != 0)
2618 js_AppendChar(&sb, '\n');
2619
2620 kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2621 if (!kidstr)
2622 break;
2623
2624 js_AppendJSString(&sb, kidstr);
2625 ++i;
2626 }
2627 XMLArrayCursorFinish(&cursor);
2628 if (kid)
2629 goto list_out;
2630
2631 if (!sb.base)
2632 return cx->runtime->emptyString;
2633
2634 if (!STRING_BUFFER_OK(&sb)) {
2635 JS_ReportOutOfMemory(cx);
2636 return NULL;
2637 }
2638
2639 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2640 list_out:
2641 if (!str && STRING_BUFFER_OK(&sb))
2642 js_FinishStringBuffer(&sb);
2643 return str;
2644
2645 default:;
2646 }
2647
2648 /* After this point, control must flow through label out: to exit. */
2649 if (!js_EnterLocalRootScope(cx))
2650 return NULL;
2651
2652 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2653 if (!ancestorNSes) {
2654 XMLArrayInit(cx, &empty, 0);
2655 ancestorNSes = &empty;
2656 }
2657 XMLArrayInit(cx, &decls, 0);
2658 ancdecls.capacity = 0;
2659
2660 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2661 XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
2662 while ((ns = (JSObject *) XMLArrayCursorNext(&cursor)) != NULL) {
2663 if (!IsDeclared(ns))
2664 continue;
2665 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2666 /* NOTE: may want to exclude unused namespaces here. */
2667 ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_TRUE);
2668 if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2))
2669 break;
2670 }
2671 }
2672 XMLArrayCursorFinish(&cursor);
2673 if (ns)
2674 goto out;
2675
2676 /*
2677 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2678 * not own its member references. In the spec, ancdecls has no name, but
2679 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2680 */
2681 if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
2682 goto out;
2683 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2684 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2685 if (!ns2)
2686 continue;
2687 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
2688 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2689 goto out;
2690 }
2691 for (i = 0, n = decls.length; i < n; i++) {
2692 ns2 = XMLARRAY_MEMBER(&decls, i, JSObject);
2693 if (!ns2)
2694 continue;
2695 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
2696 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2697 goto out;
2698 }
2699
2700 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2701 ns = GetNamespace(cx, xml->name, &ancdecls);
2702 if (!ns)
2703 goto out;
2704
2705 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2706 prefix = GetPrefix(ns);
2707 if (!prefix) {
2708 /*
2709 * Create a namespace prefix that isn't used by any member of decls.
2710 * Assign the new prefix to a copy of ns. Flag this namespace as if
2711 * it were declared, for assertion-testing's sake later below.
2712 *
2713 * Erratum: if prefix and xml->name are both null (*undefined* in
2714 * ECMA-357), we know that xml was named using the default namespace
2715 * (proof: see GetNamespace and the Namespace constructor called with
2716 * two arguments). So we ought not generate a new prefix here, when
2717 * we can declare ns as the default namespace for xml.
2718 *
2719 * This helps descendants inherit the namespace instead of redundantly
2720 * redeclaring it with generated prefixes in each descendant.
2721 */
2722 nsuri = GetURI(ns);
2723 if (!GetPrefix(xml->name)) {
2724 prefix = cx->runtime->emptyString;
2725 } else {
2726 prefix = GeneratePrefix(cx, nsuri, &ancdecls);
2727 if (!prefix)
2728 goto out;
2729 }
2730 ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2731 if (!ns)
2732 goto out;
2733
2734 /*
2735 * If the xml->name was unprefixed, we must remove any declared default
2736 * namespace from decls before appending ns. How can you get a default
2737 * namespace in decls that doesn't match the one from name? Apparently
2738 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2739 * to fix this is to update x's in-scope namespaces when setNamespace
2740 * is called, but that's not specified by ECMA-357.
2741 *
2742 * Likely Erratum here, depending on whether the lack of update to x's
2743 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2744 * erratum or not. Note that changing setNamespace to update the list
2745 * of in-scope namespaces will change x.namespaceDeclarations().
2746 */
2747 if (IS_EMPTY(prefix)) {
2748 i = XMLArrayFindMember(&decls, ns, namespace_match);
2749 if (i != XML_NOT_FOUND)
2750 XMLArrayDelete(cx, &decls, i, JS_TRUE);
2751 }
2752
2753 /*
2754 * In the spec, ancdecls has no name, but is always written out as
2755 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2756 * that union in ancdecls, any time we append a namespace strong
2757 * ref to decls, we must also append a weak ref to ancdecls. Order
2758 * matters here: code at label out: releases strong refs in decls.
2759 */
2760 if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
2761 !XMLARRAY_APPEND(cx, &decls, ns)) {
2762 goto out;
2763 }
2764 }
2765
2766 /* Format the element or point-tag into sb. */
2767 js_AppendChar(&sb, '<');
2768
2769 if (prefix && !IS_EMPTY(prefix)) {
2770 js_AppendJSString(&sb, prefix);
2771 js_AppendChar(&sb, ':');
2772 }
2773 js_AppendJSString(&sb, GetLocalName(xml->name));
2774
2775 /*
2776 * Step 16 makes a union to avoid writing two loops in step 17, to share
2777 * common attribute value appending spec-code. We prefer two loops for
2778 * faster code and less data overhead.
2779 */
2780
2781 /* Step 17(b): append attributes. */
2782 XMLArrayCursorInit(&cursor, &xml->xml_attrs);
2783 while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2784 js_AppendChar(&sb, ' ');
2785 ns2 = GetNamespace(cx, attr->name, &ancdecls);
2786 if (!ns2)
2787 break;
2788
2789 /* 17(b)(ii): NULL means *undefined* here. */
2790 prefix = GetPrefix(ns2);
2791 if (!prefix) {
2792 prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls);
2793 if (!prefix)
2794 break;
2795
2796 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2797 ns2 = NewXMLNamespace(cx, prefix, GetURI(ns2), JS_TRUE);
2798 if (!ns2)
2799 break;
2800
2801 /*
2802 * In the spec, ancdecls has no name, but is always written out as
2803 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2804 * that union in ancdecls, any time we append a namespace strong
2805 * ref to decls, we must also append a weak ref to ancdecls. Order
2806 * matters here: code at label out: releases strong refs in decls.
2807 */
2808 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
2809 !XMLARRAY_APPEND(cx, &decls, ns2)) {
2810 break;
2811 }
2812 }
2813
2814 /* 17(b)(iii). */
2815 if (!IS_EMPTY(prefix)) {
2816 js_AppendJSString(&sb, prefix);
2817 js_AppendChar(&sb, ':');
2818 }
2819
2820 /* 17(b)(iv). */
2821 js_AppendJSString(&sb, GetLocalName(attr->name));
2822
2823 /* 17(d-g). */
2824 AppendAttributeValue(cx, &sb, attr->xml_value);
2825 }
2826 XMLArrayCursorFinish(&cursor);
2827 if (attr)
2828 goto out;
2829
2830 /* Step 17(c): append XML namespace declarations. */
2831 XMLArrayCursorInit(&cursor, &decls);
2832 while ((ns2 = (JSObject *) XMLArrayCursorNext(&cursor)) != NULL) {
2833 JS_ASSERT(IsDeclared(ns2));
2834
2835 js_AppendCString(&sb, " xmlns");
2836
2837 /* 17(c)(ii): NULL means *undefined* here. */
2838 prefix = GetPrefix(ns2);
2839 if (!prefix) {
2840 prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls);
2841 if (!prefix)
2842 break;
2843 ns2->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
2844 }
2845
2846 /* 17(c)(iii). */
2847 if (!IS_EMPTY(prefix)) {
2848 js_AppendChar(&sb, ':');
2849 js_AppendJSString(&sb, prefix);
2850 }
2851
2852 /* 17(d-g). */
2853 AppendAttributeValue(cx, &sb, GetURI(ns2));
2854 }
2855 XMLArrayCursorFinish(&cursor);
2856 if (ns2)
2857 goto out;
2858
2859 /* Step 18: handle point tags. */
2860 n = xml->xml_kids.length;
2861 if (n == 0) {
2862 js_AppendCString(&sb, "/>");
2863 } else {
2864 /* Steps 19 through 25: handle element content, and open the end-tag. */
2865 js_AppendChar(&sb, '>');
2866 indentKids = n > 1 ||
2867 (n == 1 &&
2868 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2869 kid->xml_class != JSXML_CLASS_TEXT);
2870
2871 if (pretty && indentKids) {
2872 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2873 goto out;
2874 nextIndentLevel = indentLevel + i;
2875 } else {
2876 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2877 }
2878
2879 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2880 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2881 if (pretty && indentKids)
2882 js_AppendChar(&sb, '\n');
2883
2884 kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
2885 if (!kidstr)
2886 break;
2887
2888 js_AppendJSString(&sb, kidstr);
2889 }
2890 XMLArrayCursorFinish(&cursor);
2891 if (kid)
2892 goto out;
2893
2894 if (pretty && indentKids) {
2895 js_AppendChar(&sb, '\n');
2896 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2897 }
2898 js_AppendCString(&sb, "</");
2899
2900 /* Step 26. */
2901 prefix = GetPrefix(ns);
2902 if (prefix && !IS_EMPTY(prefix)) {
2903 js_AppendJSString(&sb, prefix);
2904 js_AppendChar(&sb, ':');
2905 }
2906
2907 /* Step 27. */
2908 js_AppendJSString(&sb, GetLocalName(xml->name));
2909 js_AppendChar(&sb, '>');
2910 }
2911
2912 if (!STRING_BUFFER_OK(&sb)) {
2913 JS_ReportOutOfMemory(cx);
2914 goto out;
2915 }
2916
2917 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2918 out:
2919 js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
2920 if (!str && STRING_BUFFER_OK(&sb))
2921 js_FinishStringBuffer(&sb);
2922 XMLArrayFinish(cx, &decls);
2923 if (ancdecls.capacity != 0)
2924 XMLArrayFinish(cx, &ancdecls);
2925 return str;
2926 }
2927
2928 /* ECMA-357 10.2 */
2929 static JSString *
2930 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
2931 {
2932 JSObject *obj;
2933 JSString *str;
2934 JSXML *xml;
2935
2936 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2937 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2938 JSMSG_BAD_XML_CONVERSION,
2939 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2940 return NULL;
2941 }
2942
2943 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2944 return js_ValueToString(cx, v);
2945
2946 if (JSVAL_IS_STRING(v))
2947 return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
2948
2949 obj = JSVAL_TO_OBJECT(v);
2950 if (!OBJECT_IS_XML(cx, obj)) {
2951 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
2952 return NULL;
2953 str = js_ValueToString(cx, v);
2954 if (!str)
2955 return NULL;
2956 return EscapeElementValue(cx, NULL, str);
2957 }
2958
2959 /* Handle non-element cases in this switch, returning from each case. */
2960 xml = (JSXML *) JS_GetPrivate(cx, obj);
2961 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
2962 }
2963
2964 static JSObject *
2965 ToAttributeName(JSContext *cx, jsval v)
2966 {
2967 JSString *name, *uri, *prefix;
2968 JSObject *obj;
2969 JSClass *clasp;
2970 JSObject *qn;
2971
2972 if (JSVAL_IS_STRING(v)) {
2973 name = JSVAL_TO_STRING(v);
2974 uri = prefix = cx->runtime->emptyString;
2975 } else {
2976 if (JSVAL_IS_PRIMITIVE(v)) {
2977 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2978 JSDVG_IGNORE_STACK, v, NULL);
2979 return NULL;
2980 }
2981
2982 obj = JSVAL_TO_OBJECT(v);
2983 clasp = OBJ_GET_CLASS(cx, obj);
2984 if (clasp == &js_AttributeNameClass)
2985 return obj;
2986
2987 if (clasp == &js_QNameClass.base) {
2988 qn = obj;
2989 uri = GetURI(qn);
2990 prefix = GetPrefix(qn);
2991 name = GetLocalName(qn);
2992 } else {
2993 if (clasp == &js_AnyNameClass) {
2994 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2995 } else {
2996 name = js_ValueToString(cx, v);
2997 if (!name)
2998 return NULL;
2999 }
3000 uri = prefix = cx->runtime->emptyString;
3001 }
3002 }
3003
3004 qn = NewXMLQName(cx, uri, prefix, name, &js_AttributeNameClass);
3005 if (!qn)
3006 return NULL;
3007 return qn;
3008 }
3009
3010 static void
3011 ReportBadXMLName(JSContext *cx, jsval id)
3012 {
3013 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, id, NULL);
3014 }
3015
3016 static JSBool
3017 IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
3018 {
3019 JSAtom *atom;
3020 JSString *uri;
3021
3022 atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom;
3023 uri = GetURI(qn);
3024 if (uri && atom &&
3025 (uri == ATOM_TO_STRING(atom) ||
3026 js_EqualStrings(uri, ATOM_TO_STRING(atom)))) {
3027 return JS_ValueToId(cx, STRING_TO_JSVAL(GetLocalName(qn)), funidp);
3028 }
3029 *funidp = 0;
3030 return JS_TRUE;
3031 }
3032
3033 JSBool
3034 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
3035 {
3036 if (OBJ_GET_CLASS(cx, obj) == &js_QNameClass.base)
3037 return IsFunctionQName(cx, obj, funidp);
3038 *funidp = 0;
3039 return JS_TRUE;
3040 }
3041
3042 static JSObject *
3043 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
3044 {
3045 JSString *name;
3046 JSObject *obj;
3047 JSClass *clasp;
3048 uint32 index;
3049
3050 if (JSVAL_IS_STRING(v)) {
3051 name = JSVAL_TO_STRING(v);
3052 } else {
3053 if (JSVAL_IS_PRIMITIVE(v)) {
3054 ReportBadXMLName(cx, v);
3055 return NULL;
3056 }
3057
3058 obj = JSVAL_TO_OBJECT(v);
3059 clasp = OBJ_GET_CLASS(cx, obj);
3060 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
3061 goto out;
3062 if (clasp == &js_AnyNameClass) {
3063 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3064 goto construct;
3065 }
3066 name = js_ValueToString(cx, v);
3067 if (!name)
3068 return NULL;
3069 }
3070
3071 /*
3072 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3073 *
3074 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3075 *
3076 * First, _P_ should be _s_, to refer to the given string.
3077 *
3078 * Second, why does ToXMLName applied to the string type throw TypeError
3079 * only for numeric literals without any leading or trailing whitespace?
3080 *
3081 * If the idea is to reject uint32 property names, then the check needs to
3082 * be stricter, to exclude hexadecimal and floating point literals.
3083 */
3084 if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
3085 goto bad;
3086
3087 if (*JSSTRING_CHARS(name) == '@') {
3088 name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1);
3089 if (!name)
3090 return NULL;
3091 *funidp = 0;
3092 return ToAttributeName(cx, STRING_TO_JSVAL(name));
3093 }
3094
3095 construct:
3096 v = STRING_TO_JSVAL(name);
3097 obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
3098 if (!obj)
3099 return NULL;
3100
3101 out:
3102 if (!IsFunctionQName(cx, obj, funidp))
3103 return NULL;
3104 return obj;
3105
3106 bad:
3107 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3108 JSMSG_BAD_XML_NAME,
3109 js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
3110 return NULL;
3111 }
3112
3113 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3114 static JSBool
3115 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
3116 {
3117 JSString *prefix, *prefix2;
3118 JSObject *match, *ns2;
3119 uint32 i, n, m;
3120
3121 if (xml->xml_class != JSXML_CLASS_ELEMENT)
3122 return JS_TRUE;
3123
3124 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
3125 prefix = GetPrefix(ns);
3126 if (!prefix) {
3127 match = NULL;
3128 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3129 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3130 if (ns2 && js_EqualStrings(GetURI(ns2), GetURI(ns))) {
3131 match = ns2;
3132 break;
3133 }
3134 }
3135 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
3136 return JS_FALSE;
3137 } else {
3138 if (IS_EMPTY(prefix) && IS_EMPTY(GetURI(xml->name)))
3139 return JS_TRUE;
3140 match = NULL;
3141 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3142 m = XML_NOT_FOUND;
3143 #endif
3144 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3145 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3146 if (ns2 && (prefix2 = GetPrefix(ns2)) &&
3147 js_EqualStrings(prefix2, prefix)) {
3148 match = ns2;
3149 m = i;
3150 break;
3151 }
3152 }
3153 if (match && !js_EqualStrings(GetURI(match), GetURI(ns))) {
3154 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
3155 JSObject);
3156 JS_ASSERT(ns2 == match);
3157 match->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
3158 if (!AddInScopeNamespace(cx, xml, match))
3159 return JS_FALSE;
3160 }
3161 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
3162 return JS_FALSE;
3163 }
3164
3165 /* OPTION: enforce that descendants have superset namespaces. */
3166 return JS_TRUE;
3167 }
3168
3169 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3170 static JSBool
3171 Append(JSContext *cx, JSXML *list, JSXML *xml)
3172 {
3173 uint32 i, j, k, n;
3174 JSXML *kid;
3175
3176 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
3177 i = list->xml_kids.length;
3178 n = 1;
3179 if (xml->xml_class == JSXML_CLASS_LIST) {
3180 list->xml_target = xml->xml_target;
3181 list->xml_targetprop = xml->xml_targetprop;
3182 n = JSXML_LENGTH(xml);
3183 k = i + n;
3184 if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
3185 return JS_FALSE;
3186 for (j = 0; j < n; j++) {
3187 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
3188 if (kid)
3189 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3190 }
3191 return JS_TRUE;
3192 }
3193
3194 list->xml_target = xml->parent;
3195 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3196 list->xml_targetprop = NULL;
3197 else
3198 list->xml_targetprop = xml->name;
3199 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3200 return JS_FALSE;
3201 return JS_TRUE;
3202 }
3203
3204 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3205 static JSXML *
3206 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3207
3208 static JSXML *
3209 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3210 {
3211 JSXML *copy;
3212 JSBool ok;
3213
3214 /* Our caller may not be protecting newborns with a local root scope. */
3215 if (!js_EnterLocalRootScope(cx))
3216 return NULL;
3217 copy = DeepCopyInLRS(cx, xml, flags);
3218 if (copy) {
3219 if (obj) {
3220 /* Caller provided the object for this copy, hook 'em up. */
3221 ok = JS_SetPrivate(cx, obj, copy);
3222 if (ok)
3223 copy->object = obj;
3224 } else {
3225 ok = js_GetXMLObject(cx, copy) != NULL;
3226 }
3227 if (!ok)
3228 copy = NULL;
3229 }
3230 js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
3231 return copy;
3232 }
3233
3234 /*
3235 * (i) We must be in a local root scope (InLRS).
3236 * (ii) parent must have a rooted object.
3237 * (iii) from's owning object must be locked if not thread-local.
3238 */
3239 static JSBool
3240 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3241 uintN flags)
3242 {
3243 uint32 j, n;
3244 JSXMLArrayCursor cursor;
3245 JSBool ok;
3246 JSXML *kid, *kid2;
3247 JSString *str;
3248
3249 JS_ASSERT(cx->localRootStack);
3250
3251 n = from->length;
3252 if (!XMLArraySetCapacity(cx, to, n))
3253 return JS_FALSE;
3254
3255 XMLArrayCursorInit(&cursor, from);
3256 j = 0;
3257 ok = JS_TRUE;
3258 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3259 if ((