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

Contents of /trunk/js/jsxml.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (show annotations)
Tue Dec 9 03:37:47 2008 UTC (11 years, 9 months ago) by siliconforks
File size: 251230 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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