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

Annotation of /trunk/js/jsxml.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (hide annotations)
Sun Jan 10 07:23:34 2010 UTC (10 years, 8 months ago) by siliconforks
File size: 244916 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

</
1 siliconforks 507 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 siliconforks 332 * 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 "jsversion.h"
41    
42     #if JS_HAS_XML_SUPPORT
43    
44     #include <math.h>
45     #include <stdlib.h>
46     #include <string.h>
47     #include "jstypes.h"
48 siliconforks 507 #include "jsstdint.h"
49 siliconforks 332 #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 siliconforks 507 #include "jsvector.h"
72 siliconforks 332
73     #ifdef DEBUG
74     #include <string.h> /* for #ifdef DEBUG memset calls */
75     #endif
76    
77     /*
78     * NOTES
79     * - in the js shell, you must use the -x command line option, or call
80     * options('xml') before compiling anything that uses XML literals
81     *
82     * TODO
83     * - XXXbe patrol
84     * - Fuse objects and their JSXML* private data into single GC-things
85     * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
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 siliconforks 507 #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*')
122    
123 siliconforks 332 /* 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 siliconforks 460 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
138    
139     static JSBool
140 siliconforks 332 IsQNameClass(JSClass *clasp)
141     {
142     return clasp == &js_QNameClass.base ||
143     clasp == &js_AttributeNameClass ||
144     clasp == &js_AnyNameClass;
145     }
146    
147     static JSString *
148     GetSlotString(const JSObject *obj, uint32 slot)
149     {
150     jsval v;
151    
152     JS_ASSERT(slot == JSSLOT_PREFIX ||
153     slot == JSSLOT_URI ||
154     slot == JSSLOT_LOCAL_NAME);
155     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base ||
156     IsQNameClass(STOBJ_GET_CLASS(obj)));
157     JS_ASSERT_IF(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base,
158     slot != JSSLOT_LOCAL_NAME);
159    
160     v = obj->fslots[slot];
161     if (JSVAL_IS_VOID(v))
162     return NULL;
163     JS_ASSERT(JSVAL_IS_STRING(v));
164     return JSVAL_TO_STRING(v);
165     }
166    
167     static JS_INLINE JSString *
168     GetPrefix(const JSObject *obj)
169     {
170     return GetSlotString(obj, JSSLOT_PREFIX);
171     }
172    
173     static JSString *
174     GetURI(const JSObject *obj)
175     {
176     return GetSlotString(obj, JSSLOT_URI);
177     }
178    
179     static JSString *
180     GetLocalName(const JSObject *obj)
181     {
182     return GetSlotString(obj, JSSLOT_LOCAL_NAME);
183     }
184    
185     static JSBool
186     IsDeclared(const JSObject *obj)
187     {
188     jsval v;
189    
190     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base);
191     v = obj->fslots[JSSLOT_DECLARED];
192     JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
193     return v == JSVAL_TRUE;
194     }
195    
196     static JSBool
197     xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
198     jsval *rval)
199     {
200     *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
201     return JS_TRUE;
202     }
203    
204 siliconforks 507 static inline bool
205     AppendString(JSCharBuffer &cb, JSString *str)
206     {
207     const jschar *chars, *end;
208     str->getCharsAndEnd(chars, end);
209     return cb.append(chars, end);
210     }
211    
212 siliconforks 332 /*
213     * Namespace class and library functions.
214     */
215     enum namespace_tinyid {
216     NAMESPACE_PREFIX = -1,
217     NAMESPACE_URI = -2
218     };
219    
220     static JSBool
221     namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
222     {
223     if (!JSVAL_IS_INT(id))
224     return JS_TRUE;
225    
226     if (STOBJ_GET_CLASS(obj) != &js_NamespaceClass.base)
227     return JS_TRUE;
228    
229     switch (JSVAL_TO_INT(id)) {
230     case NAMESPACE_PREFIX:
231     *vp = obj->fslots[JSSLOT_PREFIX];
232     break;
233     case NAMESPACE_URI:
234     *vp = obj->fslots[JSSLOT_URI];
235     break;
236     }
237     return JS_TRUE;
238     }
239    
240     static void
241     namespace_finalize(JSContext *cx, JSObject *obj)
242     {
243     if (cx->runtime->functionNamespaceObject == obj)
244     cx->runtime->functionNamespaceObject = NULL;
245     }
246    
247     static JSBool
248     namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
249     {
250     JSObject *obj2;
251    
252     JS_ASSERT(JSVAL_IS_OBJECT(v));
253     obj2 = JSVAL_TO_OBJECT(v);
254     *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base)
255     ? JS_FALSE
256     : js_EqualStrings(GetURI(obj), GetURI(obj2));
257     return JS_TRUE;
258     }
259    
260     JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
261     { "Namespace",
262     JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
263     JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS) |
264     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
265     JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL,
266     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize,
267     NULL, NULL, NULL, NULL,
268     NULL, NULL, NULL, NULL },
269     namespace_equality,NULL, NULL, NULL,
270     NULL, NULL, NULL, NULL
271     };
272    
273     #define NAMESPACE_ATTRS \
274     (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
275    
276     static JSPropertySpec namespace_props[] = {
277     {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0},
278     {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0},
279     {0,0,0,0,0}
280     };
281    
282     static JSBool
283     namespace_toString(JSContext *cx, uintN argc, jsval *vp)
284     {
285     JSObject *obj;
286    
287     obj = JS_THIS_OBJECT(cx, vp);
288     if (!JS_InstanceOf(cx, obj, &js_NamespaceClass.base, vp))
289     return JS_FALSE;
290     *vp = obj->fslots[JSSLOT_URI];
291     return JS_TRUE;
292     }
293    
294     static JSFunctionSpec namespace_methods[] = {
295     JS_FN(js_toString_str, namespace_toString, 0,0),
296     JS_FS_END
297     };
298    
299     static JSObject *
300     NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
301     {
302     JSObject *obj;
303    
304 siliconforks 507 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
305 siliconforks 332 if (!obj)
306     return JS_FALSE;
307     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
308     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
309     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_DECLARED]));
310     if (prefix)
311     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
312     if (uri)
313     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
314     if (declared)
315     obj->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
316     METER(xml_stats.xmlnamespace);
317     return obj;
318     }
319    
320     /*
321     * QName class and library functions.
322     */
323     enum qname_tinyid {
324     QNAME_URI = -1,
325     QNAME_LOCALNAME = -2
326     };
327    
328     static JSBool
329     qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
330     {
331     if (!JSVAL_IS_INT(id))
332     return JS_TRUE;
333    
334     if (STOBJ_GET_CLASS(obj) != &js_QNameClass.base)
335     return JS_TRUE;
336    
337     switch (JSVAL_TO_INT(id)) {
338     case QNAME_URI:
339     *vp = obj->fslots[JSSLOT_URI];
340     if (*vp == JSVAL_VOID)
341     *vp = JSVAL_NULL;
342     break;
343     case QNAME_LOCALNAME:
344     *vp = obj->fslots[JSSLOT_LOCAL_NAME];
345     break;
346     }
347     return JS_TRUE;
348     }
349    
350     static void
351     anyname_finalize(JSContext* cx, JSObject* obj)
352     {
353     /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
354     if (cx->runtime->anynameObject == obj)
355     cx->runtime->anynameObject = NULL;
356     }
357    
358     static JSBool
359     qname_identity(JSObject *qna, JSObject *qnb)
360     {
361     JSString *uri1 = GetURI(qna);
362     JSString *uri2 = GetURI(qnb);
363    
364     if (!uri1 ^ !uri2)
365     return JS_FALSE;
366     if (uri1 && !js_EqualStrings(uri1, uri2))
367     return JS_FALSE;
368     return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb));
369     }
370    
371     static JSBool
372     qname_equality(JSContext *cx, JSObject *qn, jsval v, JSBool *bp)
373     {
374     JSObject *obj2;
375    
376     JS_ASSERT(JSVAL_IS_OBJECT(v));
377     obj2 = JSVAL_TO_OBJECT(v);
378     *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base)
379     ? JS_FALSE
380     : qname_identity(qn, obj2);
381     return JS_TRUE;
382     }
383    
384     JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
385     { "QName",
386     JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
387     JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
388     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
389     JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL,
390 siliconforks 507 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
391 siliconforks 332 NULL, NULL, NULL, NULL,
392     NULL, NULL, NULL, NULL },
393     qname_equality, NULL, NULL, NULL,
394     NULL, NULL, NULL, NULL
395     };
396    
397     /*
398     * Classes for the ECMA-357-internal types AttributeName and AnyName, which
399     * are like QName, except that they have no property getters. They share the
400     * qname_toString method, and therefore are exposed as constructable objects
401     * in this implementation.
402     */
403     JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
404     js_AttributeName_str,
405     JSCLASS_CONSTRUCT_PROTOTYPE |
406     JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
407     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
408     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
409 siliconforks 507 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
410 siliconforks 332 NULL, NULL, NULL, NULL,
411     NULL, NULL, NULL, NULL
412     };
413    
414     JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
415     js_AnyName_str,
416     JSCLASS_CONSTRUCT_PROTOTYPE |
417     JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
418     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
419     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
420     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize,
421     NULL, NULL, NULL, NULL,
422     NULL, NULL, NULL, NULL
423     };
424    
425     #define QNAME_ATTRS \
426     (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
427    
428     static JSPropertySpec qname_props[] = {
429     {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0},
430     {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0},
431     {0,0,0,0,0}
432     };
433    
434     static JSBool
435     qname_toString(JSContext *cx, uintN argc, jsval *vp)
436     {
437     JSObject *obj;
438     JSClass *clasp;
439     JSString *uri, *str, *qualstr;
440     size_t length;
441     jschar *chars;
442    
443     obj = JS_THIS_OBJECT(cx, vp);
444     if (!obj)
445     return JS_FALSE;
446     clasp = OBJ_GET_CLASS(cx, obj);
447     if (clasp != &js_AttributeNameClass &&
448     clasp != &js_AnyNameClass &&
449     !JS_InstanceOf(cx, obj, &js_QNameClass.base, vp + 2)) {
450     return JS_FALSE;
451     }
452    
453     uri = GetURI(obj);
454     if (!uri) {
455     /* No uri means wildcard qualifier. */
456     str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
457 siliconforks 507 } else if (uri->empty()) {
458 siliconforks 332 /* Empty string for uri means localName is in no namespace. */
459     str = cx->runtime->emptyString;
460     } else {
461     qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
462     str = js_ConcatStrings(cx, uri, qualstr);
463     if (!str)
464     return JS_FALSE;
465     }
466     str = js_ConcatStrings(cx, str, GetLocalName(obj));
467     if (!str)
468     return JS_FALSE;
469    
470     if (str && clasp == &js_AttributeNameClass) {
471 siliconforks 507 length = str->length();
472     chars = (jschar *) cx->malloc((length + 2) * sizeof(jschar));
473 siliconforks 332 if (!chars)
474     return JS_FALSE;
475     *chars = '@';
476 siliconforks 507 js_strncpy(chars + 1, str->chars(), length);
477 siliconforks 332 chars[++length] = 0;
478     str = js_NewString(cx, chars, length);
479     if (!str) {
480 siliconforks 507 cx->free(chars);
481 siliconforks 332 return JS_FALSE;
482     }
483     }
484    
485     *vp = STRING_TO_JSVAL(str);
486     return JS_TRUE;
487     }
488    
489     static JSFunctionSpec qname_methods[] = {
490     JS_FN(js_toString_str, qname_toString, 0,0),
491     JS_FS_END
492     };
493    
494    
495     static void
496     InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix,
497     JSString *localName)
498     {
499     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
500     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
501     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_LOCAL_NAME]));
502     if (uri)
503     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
504     if (prefix)
505     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
506     if (localName)
507     obj->fslots[JSSLOT_LOCAL_NAME] = STRING_TO_JSVAL(localName);
508     }
509    
510     static JSObject *
511     NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
512     JSClass *clasp = &js_QNameClass.base)
513     {
514     JSObject *obj;
515    
516     JS_ASSERT(IsQNameClass(clasp));
517 siliconforks 507 obj = js_NewObject(cx, clasp, NULL, NULL);
518 siliconforks 332 if (!obj)
519     return NULL;
520     InitXMLQName(obj, uri, prefix, localName);
521     METER(xml_stats.qname);
522     return obj;
523     }
524    
525     JSObject *
526     js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
527     {
528     jsval argv[2];
529    
530     /*
531     * ECMA-357 11.1.2,
532     * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
533     * production, step 2.
534     */
535     if (!JSVAL_IS_PRIMITIVE(nsval) &&
536     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
537     nsval = JSVAL_NULL;
538     }
539    
540     argv[0] = nsval;
541     argv[1] = lnval;
542     return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
543     }
544    
545     static JSBool
546     IsXMLName(const jschar *cp, size_t n)
547     {
548     JSBool rv;
549     jschar c;
550    
551     rv = JS_FALSE;
552     if (n != 0 && JS_ISXMLNSSTART(*cp)) {
553     while (--n != 0) {
554     c = *++cp;
555     if (!JS_ISXMLNS(c))
556     return rv;
557     }
558     rv = JS_TRUE;
559     }
560     return rv;
561     }
562    
563     JSBool
564     js_IsXMLName(JSContext *cx, jsval v)
565     {
566     JSString *name;
567     JSErrorReporter older;
568    
569     /*
570     * Inline specialization of the QName constructor called with v passed as
571     * the only argument, to compute the localName for the constructed qname,
572     * without actually allocating the object or computing its uri and prefix.
573     * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
574     */
575     if (!JSVAL_IS_PRIMITIVE(v) &&
576     IsQNameClass(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)))) {
577     name = GetLocalName(JSVAL_TO_OBJECT(v));
578     } else {
579     older = JS_SetErrorReporter(cx, NULL);
580     name = js_ValueToString(cx, v);
581     JS_SetErrorReporter(cx, older);
582     if (!name) {
583     JS_ClearPendingException(cx);
584     return JS_FALSE;
585     }
586     }
587    
588 siliconforks 507 return IsXMLName(name->chars(), name->length());
589 siliconforks 332 }
590    
591     /*
592     * When argc is -1, it indicates argv is empty but the code should behave as
593     * if argc is 1 and argv[0] is JSVAL_VOID.
594     */
595     static JSBool
596     NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
597     jsval *rval)
598     {
599     jsval urival, prefixval;
600     JSObject *uriobj;
601     JSBool isNamespace, isQName;
602     JSClass *clasp;
603     JSString *empty, *uri, *prefix;
604    
605     isNamespace = isQName = JS_FALSE;
606     #ifdef __GNUC__ /* suppress bogus gcc warnings */
607     uriobj = NULL;
608     #endif
609     if (argc <= 0) {
610     urival = JSVAL_VOID;
611     } else {
612     urival = argv[argc > 1];
613     if (!JSVAL_IS_PRIMITIVE(urival)) {
614     uriobj = JSVAL_TO_OBJECT(urival);
615     clasp = OBJ_GET_CLASS(cx, uriobj);
616     isNamespace = (clasp == &js_NamespaceClass.base);
617     isQName = (clasp == &js_QNameClass.base);
618     }
619     }
620    
621     if (!obj) {
622     /* Namespace called as function. */
623     if (argc == 1 && isNamespace) {
624     /* Namespace called with one Namespace argument is identity. */
625     *rval = urival;
626     return JS_TRUE;
627     }
628    
629 siliconforks 507 obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
630 siliconforks 332 if (!obj)
631     return JS_FALSE;
632     *rval = OBJECT_TO_JSVAL(obj);
633     }
634     METER(xml_stats.xmlnamespace);
635    
636     empty = cx->runtime->emptyString;
637     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(empty);
638     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(empty);
639    
640     if (argc == 1 || argc == -1) {
641     if (isNamespace) {
642     obj->fslots[JSSLOT_URI] = uriobj->fslots[JSSLOT_URI];
643     obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
644     } else if (isQName && (uri = GetURI(uriobj))) {
645     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
646     obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
647     } else {
648     uri = js_ValueToString(cx, urival);
649     if (!uri)
650     return JS_FALSE;
651     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
652 siliconforks 507 if (!uri->empty())
653 siliconforks 332 obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
654     }
655     } else if (argc == 2) {
656     if (!isQName || !(uri = GetURI(uriobj))) {
657     uri = js_ValueToString(cx, urival);
658     if (!uri)
659     return JS_FALSE;
660     }
661     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
662    
663     prefixval = argv[0];
664 siliconforks 507 if (uri->empty()) {
665 siliconforks 332 if (!JSVAL_IS_VOID(prefixval)) {
666     prefix = js_ValueToString(cx, prefixval);
667     if (!prefix)
668     return JS_FALSE;
669 siliconforks 507 if (!prefix->empty()) {
670 siliconforks 332 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
671     JSMSG_BAD_XML_NAMESPACE,
672     js_ValueToPrintableString(cx,
673     STRING_TO_JSVAL(prefix)));
674     return JS_FALSE;
675     }
676     }
677     } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
678     obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
679     } else {
680     prefix = js_ValueToString(cx, prefixval);
681     if (!prefix)
682     return JS_FALSE;
683     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
684     }
685     }
686    
687     return JS_TRUE;
688     }
689    
690     static JSBool
691     Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
692     {
693     return NamespaceHelper(cx,
694 siliconforks 460 JS_IsConstructing(cx) ? obj : NULL,
695 siliconforks 332 argc, argv, rval);
696     }
697    
698     /*
699     * When argc is -1, it indicates argv is empty but the code should behave as
700     * if argc is 1 and argv[0] is JSVAL_VOID.
701     */
702     static JSBool
703     QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
704     jsval *argv, jsval *rval)
705     {
706     jsval nameval, nsval;
707     JSBool isQName, isNamespace;
708     JSObject *qn;
709     JSString *uri, *prefix, *name;
710     JSObject *obj2;
711    
712     JS_ASSERT(clasp == &js_QNameClass.base ||
713     clasp == &js_AttributeNameClass);
714     if (argc <= 0) {
715     nameval = JSVAL_VOID;
716     isQName = JS_FALSE;
717     } else {
718     nameval = argv[argc > 1];
719     isQName =
720     !JSVAL_IS_PRIMITIVE(nameval) &&
721     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
722     }
723    
724     if (!obj) {
725     /* QName called as function. */
726     if (argc == 1 && isQName) {
727     /* QName called with one QName argument is identity. */
728     *rval = nameval;
729     return JS_TRUE;
730     }
731    
732     /*
733     * Create and return a new QName or AttributeName object exactly as if
734     * constructed.
735     */
736 siliconforks 507 obj = js_NewObject(cx, clasp, NULL, NULL);
737 siliconforks 332 if (!obj)
738     return JS_FALSE;
739     *rval = OBJECT_TO_JSVAL(obj);
740     }
741     METER(xml_stats.qname);
742    
743     if (isQName) {
744     /* If namespace is not specified and name is a QName, clone it. */
745     qn = JSVAL_TO_OBJECT(nameval);
746     if (argc == 1) {
747     uri = GetURI(qn);
748     prefix = GetPrefix(qn);
749     name = GetLocalName(qn);
750     goto out;
751     }
752    
753     /* Namespace and qname were passed -- use the qname's localName. */
754     nameval = qn->fslots[JSSLOT_LOCAL_NAME];
755     }
756    
757     if (argc == 0) {
758     name = cx->runtime->emptyString;
759     } else if (argc < 0) {
760     name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
761     } else {
762     name = js_ValueToString(cx, nameval);
763     if (!name)
764     return JS_FALSE;
765     argv[argc > 1] = STRING_TO_JSVAL(name);
766     }
767    
768     if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
769     nsval = argv[0];
770     } else if (IS_STAR(name)) {
771     nsval = JSVAL_NULL;
772     } else {
773     if (!js_GetDefaultXMLNamespace(cx, &nsval))
774     return JS_FALSE;
775     JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
776     JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) ==
777     &js_NamespaceClass.base);
778     }
779    
780     if (JSVAL_IS_NULL(nsval)) {
781     /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
782     uri = prefix = NULL;
783     } else {
784     /*
785     * Inline specialization of the Namespace constructor called with
786     * nsval passed as the only argument, to compute the uri and prefix
787     * for the constructed namespace, without actually allocating the
788     * object or computing other members. See ECMA-357 13.3.2 6(a) and
789     * 13.2.2.
790     */
791     isNamespace = isQName = JS_FALSE;
792     if (!JSVAL_IS_PRIMITIVE(nsval)) {
793     obj2 = JSVAL_TO_OBJECT(nsval);
794     clasp = OBJ_GET_CLASS(cx, obj2);
795     isNamespace = (clasp == &js_NamespaceClass.base);
796     isQName = (clasp == &js_QNameClass.base);
797     }
798     #ifdef __GNUC__ /* suppress bogus gcc warnings */
799     else obj2 = NULL;
800     #endif
801    
802     if (isNamespace) {
803     uri = GetURI(obj2);
804     prefix = GetPrefix(obj2);
805     } else if (isQName && (uri = GetURI(obj2))) {
806     JS_ASSERT(argc > 1);
807     prefix = GetPrefix(obj2);
808     } else {
809     JS_ASSERT(argc > 1);
810     uri = js_ValueToString(cx, nsval);
811     if (!uri)
812     return JS_FALSE;
813     argv[0] = STRING_TO_JSVAL(uri); /* local root */
814    
815     /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
816 siliconforks 507 prefix = uri->empty() ? cx->runtime->emptyString : NULL;
817 siliconforks 332 }
818     }
819    
820     out:
821     InitXMLQName(obj, uri, prefix, name);
822     return JS_TRUE;
823     }
824    
825     static JSBool
826     QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
827     {
828 siliconforks 460 return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL,
829 siliconforks 332 &js_QNameClass.base, argc, argv, rval);
830     }
831    
832     static JSBool
833     AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
834     jsval *rval)
835     {
836 siliconforks 460 return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL,
837 siliconforks 332 &js_AttributeNameClass, argc, argv, rval);
838     }
839    
840     /*
841     * XMLArray library functions.
842     */
843     static JSBool
844     namespace_identity(const void *a, const void *b)
845     {
846     const JSObject *nsa = (const JSObject *) a;
847     const JSObject *nsb = (const JSObject *) b;
848     JSString *prefixa = GetPrefix(nsa);
849     JSString *prefixb = GetPrefix(nsb);
850    
851     if (prefixa && prefixb) {
852     if (!js_EqualStrings(prefixa, prefixb))
853     return JS_FALSE;
854     } else {
855     if (prefixa || prefixb)
856     return JS_FALSE;
857     }
858     return js_EqualStrings(GetURI(nsa), GetURI(nsb));
859     }
860    
861     static JSBool
862     attr_identity(const void *a, const void *b)
863     {
864     const JSXML *xmla = (const JSXML *) a;
865     const JSXML *xmlb = (const JSXML *) b;
866    
867     return qname_identity(xmla->name, xmlb->name);
868     }
869    
870 siliconforks 507 struct JSXMLArrayCursor
871 siliconforks 332 {
872 siliconforks 507 JSXMLArray *array;
873     uint32 index;
874 siliconforks 332 JSXMLArrayCursor *next;
875 siliconforks 507 JSXMLArrayCursor **prevp;
876     void *root;
877 siliconforks 332
878 siliconforks 507 JSXMLArrayCursor(JSXMLArray *array)
879     : array(array), index(0), next(array->cursors), prevp(&array->cursors),
880     root(NULL)
881     {
882     if (next)
883     next->prevp = &next;
884     array->cursors = this;
885     }
886 siliconforks 332
887 siliconforks 507 ~JSXMLArrayCursor() { disconnect(); }
888 siliconforks 332
889 siliconforks 507 void disconnect() {
890     if (!array)
891     return;
892     if (next)
893     next->prevp = prevp;
894     *prevp = next;
895     array = NULL;
896     }
897 siliconforks 332
898 siliconforks 507 void *getNext() {
899     if (!array || index >= array->length)
900     return NULL;
901     return root = array->vector[index++];
902     }
903 siliconforks 332
904 siliconforks 507 void *getCurrent() {
905     if (!array || index >= array->length)
906     return NULL;
907     return root = array->vector[index];
908     }
909     };
910 siliconforks 332
911     static void
912     XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
913     {
914     void *root;
915     #ifdef DEBUG
916     size_t index = 0;
917     #endif
918    
919     for (; cursor; cursor = cursor->next) {
920     root = cursor->root;
921     JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
922     js_CallValueTracerIfGCThing(trc, (jsval)root);
923     }
924     }
925    
926     /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
927     static JSBool
928     XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
929     {
930     void **vector;
931    
932     if (capacity == 0) {
933     /* We could let realloc(p, 0) free this, but purify gets confused. */
934 siliconforks 507 if (array->vector) {
935     if (cx)
936     cx->free(array->vector);
937     else
938     js_free(array->vector);
939     }
940 siliconforks 332 vector = NULL;
941     } else {
942     if (
943     #if JS_BITS_PER_WORD == 32
944     (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
945     #endif
946     !(vector = (void **)
947 siliconforks 507 js_realloc(array->vector, capacity * sizeof(void *)))) {
948 siliconforks 332 if (cx)
949     JS_ReportOutOfMemory(cx);
950     return JS_FALSE;
951     }
952     }
953     array->capacity = JSXML_PRESET_CAPACITY | capacity;
954     array->vector = vector;
955     return JS_TRUE;
956     }
957    
958     static void
959     XMLArrayTrim(JSXMLArray *array)
960     {
961     if (array->capacity & JSXML_PRESET_CAPACITY)
962     return;
963     if (array->length < array->capacity)
964     XMLArraySetCapacity(NULL, array, array->length);
965     }
966    
967     static JSBool
968     XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
969     {
970     array->length = array->capacity = 0;
971     array->vector = NULL;
972     array->cursors = NULL;
973     return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
974     }
975    
976     static void
977     XMLArrayFinish(JSContext *cx, JSXMLArray *array)
978     {
979 siliconforks 507 cx->free(array->vector);
980 siliconforks 332
981 siliconforks 507 while (JSXMLArrayCursor *cursor = array->cursors)
982     cursor->disconnect();
983 siliconforks 332
984     #ifdef DEBUG
985     memset(array, 0xd5, sizeof *array);
986     #endif
987     }
988    
989     #define XML_NOT_FOUND ((uint32) -1)
990    
991     static uint32
992     XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
993     {
994     void **vector;
995     uint32 i, n;
996    
997     /* The identity op must not reallocate array->vector. */
998     vector = array->vector;
999     if (identity) {
1000     for (i = 0, n = array->length; i < n; i++) {
1001     if (identity(vector[i], elt))
1002     return i;
1003     }
1004     } else {
1005     for (i = 0, n = array->length; i < n; i++) {
1006     if (vector[i] == elt)
1007     return i;
1008     }
1009     }
1010     return XML_NOT_FOUND;
1011     }
1012    
1013     /*
1014     * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1015     * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1016     * should be greater than increment.
1017     */
1018     #define LINEAR_THRESHOLD 256
1019     #define LINEAR_INCREMENT 32
1020    
1021     static JSBool
1022     XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
1023     {
1024     uint32 capacity, i;
1025     int log2;
1026     void **vector;
1027    
1028     if (index >= array->length) {
1029     if (index >= JSXML_CAPACITY(array)) {
1030     /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1031     capacity = index + 1;
1032     if (index >= LINEAR_THRESHOLD) {
1033     capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
1034     } else {
1035     JS_CEILING_LOG2(log2, capacity);
1036     capacity = JS_BIT(log2);
1037     }
1038     if (
1039     #if JS_BITS_PER_WORD == 32
1040     (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1041     #endif
1042     !(vector = (void **)
1043 siliconforks 507 js_realloc(array->vector, capacity * sizeof(void *)))) {
1044 siliconforks 332 JS_ReportOutOfMemory(cx);
1045     return JS_FALSE;
1046     }
1047     array->capacity = capacity;
1048     array->vector = vector;
1049     for (i = array->length; i < index; i++)
1050     vector[i] = NULL;
1051     }
1052     array->length = index + 1;
1053     }
1054    
1055     array->vector[index] = elt;
1056     return JS_TRUE;
1057     }
1058    
1059     static JSBool
1060     XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1061     {
1062     uint32 j;
1063     JSXMLArrayCursor *cursor;
1064    
1065     j = array->length;
1066     JS_ASSERT(i <= j);
1067     if (!XMLArraySetCapacity(cx, array, j + n))
1068     return JS_FALSE;
1069    
1070     array->length = j + n;
1071     JS_ASSERT(n != (uint32)-1);
1072     while (j != i) {
1073     --j;
1074     array->vector[j + n] = array->vector[j];
1075     }
1076    
1077     for (cursor = array->cursors; cursor; cursor = cursor->next) {
1078     if (cursor->index > i)
1079     cursor->index += n;
1080     }
1081     return JS_TRUE;
1082     }
1083    
1084     static void *
1085     XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1086     {
1087     uint32 length;
1088     void **vector, *elt;
1089     JSXMLArrayCursor *cursor;
1090    
1091     length = array->length;
1092     if (index >= length)
1093     return NULL;
1094    
1095     vector = array->vector;
1096     elt = vector[index];
1097     if (compress) {
1098     while (++index < length)
1099     vector[index-1] = vector[index];
1100     array->length = length - 1;
1101     array->capacity = JSXML_CAPACITY(array);
1102     } else {
1103     vector[index] = NULL;
1104     }
1105    
1106     for (cursor = array->cursors; cursor; cursor = cursor->next) {
1107     if (cursor->index > index)
1108     --cursor->index;
1109     }
1110     return elt;
1111     }
1112    
1113     static void
1114     XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1115     {
1116     void **vector;
1117    
1118     JS_ASSERT(!array->cursors);
1119     if (length >= array->length)
1120     return;
1121    
1122     if (length == 0) {
1123     if (array->vector)
1124 siliconforks 507 cx->free(array->vector);
1125 siliconforks 332 vector = NULL;
1126     } else {
1127 siliconforks 507 vector = (void **) js_realloc(array->vector, length * sizeof(void *));
1128 siliconforks 332 if (!vector)
1129     return;
1130     }
1131    
1132     if (array->length > length)
1133     array->length = length;
1134     array->capacity = length;
1135     array->vector = vector;
1136     }
1137    
1138     #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1139     #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1140     XML_NOT_FOUND)
1141     #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1142     ? (t *) (a)->vector[i] \
1143     : NULL)
1144     #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1145     if ((a)->length <= (i)) \
1146     (a)->length = (i) + 1; \
1147     ((a)->vector[i] = (void *)(e)); \
1148     JS_END_MACRO
1149     #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1150     #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1151     #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1152     #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1153     #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1154    
1155     /*
1156     * Define XML setting property strings and constants early, so everyone can
1157     * use the same names and their magic numbers (tinyids, flags).
1158     */
1159     static const char js_ignoreComments_str[] = "ignoreComments";
1160     static const char js_ignoreProcessingInstructions_str[]
1161     = "ignoreProcessingInstructions";
1162     static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1163     static const char js_prettyPrinting_str[] = "prettyPrinting";
1164     static const char js_prettyIndent_str[] = "prettyIndent";
1165    
1166     /*
1167     * NB: These XML static property tinyids must
1168     * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1169     * (b) index their corresponding xml_static_props array elements.
1170     * Don't change 'em!
1171     */
1172     enum xml_static_tinyid {
1173     XML_IGNORE_COMMENTS,
1174     XML_IGNORE_PROCESSING_INSTRUCTIONS,
1175     XML_IGNORE_WHITESPACE,
1176     XML_PRETTY_PRINTING,
1177     XML_PRETTY_INDENT
1178     };
1179    
1180     static JSBool
1181     xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1182     {
1183     return JS_TRUE;
1184     }
1185    
1186     static JSBool
1187     xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1188     {
1189     uint8 flag;
1190    
1191     JS_ASSERT(JSVAL_IS_INT(id));
1192    
1193     flag = JS_BIT(JSVAL_TO_INT(id));
1194     if (js_ValueToBoolean(*vp))
1195     cx->xmlSettingFlags |= flag;
1196     else
1197     cx->xmlSettingFlags &= ~flag;
1198     return JS_TRUE;
1199     }
1200    
1201     static JSPropertySpec xml_static_props[] = {
1202     {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT,
1203     xml_setting_getter, xml_setting_setter},
1204     {js_ignoreProcessingInstructions_str,
1205     XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
1206     xml_setting_getter, xml_setting_setter},
1207     {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
1208     xml_setting_getter, xml_setting_setter},
1209     {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT,
1210     xml_setting_getter, xml_setting_setter},
1211     {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT,
1212     xml_setting_getter, NULL},
1213     {0,0,0,0,0}
1214     };
1215    
1216     /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1217     #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1218     #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1219     JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1220     #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1221     #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1222     #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1223    
1224     /*
1225     * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1226     * This flag means a couple of things:
1227     *
1228     * - The top JSXML created for a parse tree must have an object owning it.
1229     *
1230     * - That the default namespace normally inherited from the temporary
1231     * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1232     * string must, in the case of a precompiled XML object tree, inherit via
1233     * ad-hoc code in ParseNodeToXML.
1234     *
1235     * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1236     */
1237     #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1238    
1239     /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1240     #define IS_XML(str) \
1241 siliconforks 507 (str->length() == 3 && IS_XML_CHARS(str->chars()))
1242 siliconforks 332
1243     #define IS_XMLNS(str) \
1244 siliconforks 507 (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1245 siliconforks 332
1246     #define IS_XML_CHARS(chars) \
1247     (JS_TOLOWER((chars)[0]) == 'x' && \
1248     JS_TOLOWER((chars)[1]) == 'm' && \
1249     JS_TOLOWER((chars)[2]) == 'l')
1250    
1251     #define HAS_NS_AFTER_XML(chars) \
1252     (JS_TOLOWER((chars)[3]) == 'n' && \
1253     JS_TOLOWER((chars)[4]) == 's')
1254    
1255     #define IS_XMLNS_CHARS(chars) \
1256     (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1257    
1258     #define STARTS_WITH_XML(chars,length) \
1259     (length >= 3 && IS_XML_CHARS(chars))
1260    
1261     static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1262     static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1263    
1264     static JSObject *
1265 siliconforks 460 ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn,
1266 siliconforks 332 JSXMLArray *inScopeNSes, JSBool isAttributeName)
1267     {
1268 siliconforks 460 JSContext *cx = jsc->context;
1269 siliconforks 332 JSString *str, *uri, *prefix, *localName;
1270     size_t length, offset;
1271     const jschar *start, *limit, *colon;
1272     uint32 n;
1273     JSObject *ns;
1274     JSString *nsprefix;
1275    
1276     JS_ASSERT(pn->pn_arity == PN_NULLARY);
1277     str = ATOM_TO_STRING(pn->pn_atom);
1278 siliconforks 507 str->getCharsAndLength(start, length);
1279 siliconforks 332 JS_ASSERT(length != 0 && *start != '@');
1280     JS_ASSERT(length != 1 || *start != '*');
1281    
1282     uri = cx->runtime->emptyString;
1283     limit = start + length;
1284     colon = js_strchr_limit(start, ':', limit);
1285     if (colon) {
1286 siliconforks 507 offset = colon - start;
1287 siliconforks 332 prefix = js_NewDependentString(cx, str, 0, offset);
1288     if (!prefix)
1289     return NULL;
1290    
1291     if (STARTS_WITH_XML(start, offset)) {
1292     if (offset == 3) {
1293     uri = JS_InternString(cx, xml_namespace_str);
1294     if (!uri)
1295     return NULL;
1296     } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1297     uri = JS_InternString(cx, xmlns_namespace_str);
1298     if (!uri)
1299     return NULL;
1300     } else {
1301     uri = NULL;
1302     }
1303     } else {
1304     uri = NULL;
1305     n = inScopeNSes->length;
1306     while (n != 0) {
1307     --n;
1308     ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1309     nsprefix = GetPrefix(ns);
1310     if (nsprefix && js_EqualStrings(nsprefix, prefix)) {
1311     uri = GetURI(ns);
1312     break;
1313     }
1314     }
1315     }
1316    
1317     if (!uri) {
1318 siliconforks 460 js_ReportCompileErrorNumber(jsc->context, &jsc->tokenStream, pn,
1319 siliconforks 332 JSREPORT_ERROR,
1320     JSMSG_BAD_XML_NAMESPACE,
1321 siliconforks 460 js_ValueToPrintableString(jsc->context,
1322 siliconforks 332 STRING_TO_JSVAL(prefix)));
1323     return NULL;
1324     }
1325    
1326 siliconforks 460 localName = js_NewStringCopyN(jsc->context, colon + 1, length - (offset + 1));
1327 siliconforks 332 if (!localName)
1328     return NULL;
1329     } else {
1330     if (isAttributeName) {
1331     /*
1332     * An unprefixed attribute is not in any namespace, so set prefix
1333     * as well as uri to the empty string.
1334     */
1335     prefix = uri;
1336     } else {
1337     /*
1338     * Loop from back to front looking for the closest declared default
1339     * namespace.
1340     */
1341     n = inScopeNSes->length;
1342     while (n != 0) {
1343     --n;
1344     ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1345     nsprefix = GetPrefix(ns);
1346 siliconforks 507 if (!nsprefix || nsprefix->empty()) {
1347 siliconforks 332 uri = GetURI(ns);
1348     break;
1349     }
1350     }
1351 siliconforks 507 prefix = uri->empty() ? jsc->context->runtime->emptyString : NULL;
1352 siliconforks 332 }
1353     localName = str;
1354     }
1355    
1356 siliconforks 460 return NewXMLQName(jsc->context, uri, prefix, localName);
1357 siliconforks 332 }
1358    
1359     static JSString *
1360     ChompXMLWhitespace(JSContext *cx, JSString *str)
1361     {
1362     size_t length, newlength, offset;
1363     const jschar *cp, *start, *end;
1364     jschar c;
1365    
1366 siliconforks 507 str->getCharsAndLength(start, length);
1367 siliconforks 332 for (cp = start, end = cp + length; cp < end; cp++) {
1368     c = *cp;
1369     if (!JS_ISXMLSPACE(c))
1370     break;
1371     }
1372     while (end > cp) {
1373     c = end[-1];
1374     if (!JS_ISXMLSPACE(c))
1375     break;
1376     --end;
1377     }
1378 siliconforks 507 newlength = end - cp;
1379 siliconforks 332 if (newlength == length)
1380     return str;
1381 siliconforks 507 offset = cp - start;
1382 siliconforks 332 return js_NewDependentString(cx, str, offset, newlength);
1383     }
1384    
1385     static JSXML *
1386 siliconforks 460 ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
1387 siliconforks 332 JSXMLArray *inScopeNSes, uintN flags)
1388     {
1389 siliconforks 460 JSContext *cx = jsc->context;
1390 siliconforks 332 JSXML *xml, *kid, *attr, *attrj;
1391     JSString *str;
1392     uint32 length, n, i, j;
1393     JSParseNode *pn2, *pn3, *head, **pnp;
1394     JSObject *ns;
1395     JSObject *qn, *attrjqn;
1396     JSXMLClass xml_class;
1397     int stackDummy;
1398    
1399     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1400 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR,
1401 siliconforks 332 JSMSG_OVER_RECURSED);
1402     return NULL;
1403     }
1404    
1405     #define PN2X_SKIP_CHILD ((JSXML *) 1)
1406    
1407     /*
1408     * Cases return early to avoid common code that gets an outermost xml's
1409     * object, which protects GC-things owned by xml and its descendants from
1410     * garbage collection.
1411     */
1412     xml = NULL;
1413     if (!js_EnterLocalRootScope(cx))
1414     return NULL;
1415     switch (pn->pn_type) {
1416     case TOK_XMLELEM:
1417     length = inScopeNSes->length;
1418     pn2 = pn->pn_head;
1419 siliconforks 460 xml = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
1420 siliconforks 332 if (!xml)
1421     goto fail;
1422    
1423     flags &= ~XSF_PRECOMPILED_ROOT;
1424     n = pn->pn_count;
1425     JS_ASSERT(n >= 2);
1426     n -= 2;
1427     if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1428     goto fail;
1429    
1430     i = 0;
1431     while ((pn2 = pn2->pn_next) != NULL) {
1432     if (!pn2->pn_next) {
1433     /* Don't append the end tag! */
1434     JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1435     break;
1436     }
1437    
1438     if ((flags & XSF_IGNORE_WHITESPACE) &&
1439     n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1440     --n;
1441     continue;
1442     }
1443    
1444 siliconforks 460 kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
1445 siliconforks 332 if (kid == PN2X_SKIP_CHILD) {
1446     --n;
1447     continue;
1448     }
1449    
1450     if (!kid)
1451     goto fail;
1452    
1453     /* Store kid in xml right away, to protect it from GC. */
1454     XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1455     kid->parent = xml;
1456     ++i;
1457    
1458     /* XXX where is this documented in an XML spec, or in E4X? */
1459     if ((flags & XSF_IGNORE_WHITESPACE) &&
1460     n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1461     str = ChompXMLWhitespace(cx, kid->xml_value);
1462     if (!str)
1463     goto fail;
1464     kid->xml_value = str;
1465     }
1466     }
1467    
1468     JS_ASSERT(i == n);
1469     if (n < pn->pn_count - 2)
1470     XMLArrayTrim(&xml->xml_kids);
1471     XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1472     break;
1473    
1474     case TOK_XMLLIST:
1475     xml = js_NewXML(cx, JSXML_CLASS_LIST);
1476     if (!xml)
1477     goto fail;
1478    
1479     n = pn->pn_count;
1480     if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1481     goto fail;
1482    
1483     i = 0;
1484     for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1485     /*
1486     * Always ignore insignificant whitespace in lists -- we shouldn't
1487     * condition this on an XML.ignoreWhitespace setting when the list
1488     * constructor is XMLList (note XML/XMLList unification hazard).
1489     */
1490     if (pn2->pn_type == TOK_XMLSPACE) {
1491     --n;
1492     continue;
1493     }
1494    
1495 siliconforks 460 kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
1496 siliconforks 332 if (kid == PN2X_SKIP_CHILD) {
1497     --n;
1498     continue;
1499     }
1500    
1501     if (!kid)
1502     goto fail;
1503    
1504     XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1505     ++i;
1506     }
1507    
1508     if (n < pn->pn_count)
1509     XMLArrayTrim(&xml->xml_kids);
1510     break;
1511    
1512     case TOK_XMLSTAGO:
1513     case TOK_XMLPTAGC:
1514     length = inScopeNSes->length;
1515     pn2 = pn->pn_head;
1516     JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1517     if (pn2->pn_arity == PN_LIST)
1518     goto syntax;
1519    
1520     xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1521     if (!xml)
1522     goto fail;
1523    
1524     /* First pass: check syntax and process namespace declarations. */
1525     JS_ASSERT(pn->pn_count >= 1);
1526     n = pn->pn_count - 1;
1527     pnp = &pn2->pn_next;
1528     head = *pnp;
1529     while ((pn2 = *pnp) != NULL) {
1530     size_t length;
1531     const jschar *chars;
1532    
1533     if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1534     goto syntax;
1535    
1536     /* Enforce "Well-formedness constraint: Unique Att Spec". */
1537     for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1538     if (pn3->pn_atom == pn2->pn_atom) {
1539 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2,
1540 siliconforks 332 JSREPORT_ERROR,
1541     JSMSG_DUPLICATE_XML_ATTR,
1542     js_ValueToPrintableString(cx,
1543     ATOM_KEY(pn2->pn_atom)));
1544     goto fail;
1545     }
1546     }
1547    
1548     str = ATOM_TO_STRING(pn2->pn_atom);
1549     pn2 = pn2->pn_next;
1550     JS_ASSERT(pn2);
1551     if (pn2->pn_type != TOK_XMLATTR)
1552     goto syntax;
1553    
1554 siliconforks 507 str->getCharsAndLength(chars, length);
1555 siliconforks 332 if (length >= 5 &&
1556     IS_XMLNS_CHARS(chars) &&
1557     (length == 5 || chars[5] == ':')) {
1558     JSString *uri, *prefix;
1559    
1560     uri = ATOM_TO_STRING(pn2->pn_atom);
1561     if (length == 5) {
1562     /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1563     prefix = cx->runtime->emptyString;
1564     } else {
1565     prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1566     if (!prefix)
1567     goto fail;
1568     }
1569    
1570     /*
1571     * Once the new ns is appended to xml->xml_namespaces, it is
1572     * protected from GC by the object that owns xml -- which is
1573     * either xml->object if outermost, or the object owning xml's
1574     * oldest ancestor if !outermost.
1575     */
1576     ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1577     if (!ns)
1578     goto fail;
1579    
1580     /*
1581     * Don't add a namespace that's already in scope. If someone
1582     * extracts a child property from its parent via [[Get]], then
1583     * we enforce the invariant, noted many times in ECMA-357, that
1584     * the child's namespaces form a possibly-improper superset of
1585     * its ancestors' namespaces.
1586     */
1587     if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1588     if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1589     !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1590     goto fail;
1591     }
1592     }
1593    
1594     JS_ASSERT(n >= 2);
1595     n -= 2;
1596     *pnp = pn2->pn_next;
1597     /* XXXbe recycle pn2 */
1598     continue;
1599     }
1600    
1601     pnp = &pn2->pn_next;
1602     }
1603    
1604     /*
1605     * If called from js_ParseNodeToXMLObject, emulate the effect of the
1606     * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1607     * the String Type" (ECMA-357 10.3.1).
1608     */
1609     if (flags & XSF_PRECOMPILED_ROOT) {
1610     JS_ASSERT(length >= 1);
1611     ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSObject);
1612     JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
1613     namespace_identity));
1614     ns = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_FALSE);
1615     if (!ns)
1616     goto fail;
1617     if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1618     goto fail;
1619     }
1620     XMLArrayTrim(&xml->xml_namespaces);
1621    
1622     /* Second pass: process tag name and attributes, using namespaces. */
1623     pn2 = pn->pn_head;
1624 siliconforks 460 qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_FALSE);
1625 siliconforks 332 if (!qn)
1626     goto fail;
1627     xml->name = qn;
1628    
1629     JS_ASSERT((n & 1) == 0);
1630     n >>= 1;
1631     if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
1632     goto fail;
1633    
1634     for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1635 siliconforks 460 qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_TRUE);
1636 siliconforks 332 if (!qn) {
1637     xml->xml_attrs.length = i;
1638     goto fail;
1639     }
1640    
1641     /*
1642     * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1643     * this time checking local name and namespace URI.
1644     */
1645     for (j = 0; j < i; j++) {
1646     attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1647     attrjqn = attrj->name;
1648     if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
1649     js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
1650 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2,
1651 siliconforks 332 JSREPORT_ERROR,
1652     JSMSG_DUPLICATE_XML_ATTR,
1653     js_ValueToPrintableString(cx,
1654     ATOM_KEY(pn2->pn_atom)));
1655     goto fail;
1656     }
1657     }
1658    
1659     pn2 = pn2->pn_next;
1660     JS_ASSERT(pn2);
1661     JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1662    
1663     attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1664     if (!attr)
1665     goto fail;
1666    
1667     XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1668     attr->parent = xml;
1669     attr->name = qn;
1670     attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1671     }
1672    
1673     /* Point tag closes its own namespace scope. */
1674     if (pn->pn_type == TOK_XMLPTAGC)
1675     XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1676     break;
1677    
1678     case TOK_XMLSPACE:
1679     case TOK_XMLTEXT:
1680     case TOK_XMLCDATA:
1681     case TOK_XMLCOMMENT:
1682     case TOK_XMLPI:
1683     str = ATOM_TO_STRING(pn->pn_atom);
1684     qn = NULL;
1685     if (pn->pn_type == TOK_XMLCOMMENT) {
1686     if (flags & XSF_IGNORE_COMMENTS)
1687     goto skip_child;
1688     xml_class = JSXML_CLASS_COMMENT;
1689     } else if (pn->pn_type == TOK_XMLPI) {
1690     if (IS_XML(str)) {
1691 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn,
1692 siliconforks 332 JSREPORT_ERROR,
1693     JSMSG_RESERVED_ID,
1694     js_ValueToPrintableString(cx,
1695     STRING_TO_JSVAL(str)));
1696     goto fail;
1697     }
1698    
1699     if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1700     goto skip_child;
1701    
1702 siliconforks 460 qn = ParseNodeToQName(jsc, pn, inScopeNSes, JS_FALSE);
1703 siliconforks 332 if (!qn)
1704     goto fail;
1705    
1706     str = pn->pn_atom2
1707     ? ATOM_TO_STRING(pn->pn_atom2)
1708     : cx->runtime->emptyString;
1709     xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1710     } else {
1711     /* CDATA section content, or element text. */
1712     xml_class = JSXML_CLASS_TEXT;
1713     }
1714    
1715     xml = js_NewXML(cx, xml_class);
1716     if (!xml)
1717     goto fail;
1718     xml->name = qn;
1719     if (pn->pn_type == TOK_XMLSPACE)
1720     xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1721     xml->xml_value = str;
1722     break;
1723    
1724     default:
1725     goto syntax;
1726     }
1727    
1728     js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
1729     if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
1730     return NULL;
1731     return xml;
1732    
1733     skip_child:
1734     js_LeaveLocalRootScope(cx);
1735     return PN2X_SKIP_CHILD;
1736    
1737     #undef PN2X_SKIP_CHILD
1738    
1739     syntax:
1740 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR,
1741 siliconforks 332 JSMSG_BAD_XML_MARKUP);
1742     fail:
1743     js_LeaveLocalRootScope(cx);
1744     return NULL;
1745     }
1746    
1747     /*
1748     * XML helper, object-ops, and library functions. We start with the helpers,
1749     * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1750     */
1751     static JSBool
1752     GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1753     {
1754     jsval v;
1755    
1756     if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
1757     return JS_FALSE;
1758     if (!VALUE_IS_FUNCTION(cx, v)) {
1759     *vp = JSVAL_VOID;
1760     return JS_TRUE;
1761     }
1762     return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1763     }
1764    
1765     static JSBool
1766     FillSettingsCache(JSContext *cx)
1767     {
1768     int i;
1769     const char *name;
1770     jsval v;
1771    
1772     /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1773     for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
1774     name = xml_static_props[i].name;
1775     if (!GetXMLSetting(cx, name, &v))
1776     return JS_FALSE;
1777     if (js_ValueToBoolean(v))
1778     cx->xmlSettingFlags |= JS_BIT(i);
1779     else
1780     cx->xmlSettingFlags &= ~JS_BIT(i);
1781     }
1782    
1783     cx->xmlSettingFlags |= XSF_CACHE_VALID;
1784     return JS_TRUE;
1785     }
1786    
1787     static JSBool
1788     GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1789     {
1790     int i;
1791    
1792     if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
1793     return JS_FALSE;
1794    
1795     for (i = 0; xml_static_props[i].name; i++) {
1796     if (!strcmp(xml_static_props[i].name, name)) {
1797     *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
1798     return JS_TRUE;
1799     }
1800     }
1801     *bp = JS_FALSE;
1802     return JS_TRUE;
1803     }
1804    
1805     static JSBool
1806     GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1807     {
1808     jsval v;
1809    
1810     return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1811     }
1812    
1813     static JSBool
1814     GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1815     {
1816     JSBool flag;
1817    
1818     /* Just get the first flag to validate the setting flags cache. */
1819     if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
1820     return JS_FALSE;
1821     *flagsp = cx->xmlSettingFlags;
1822     return JS_TRUE;
1823     }
1824    
1825     static JSXML *
1826     ParseXMLSource(JSContext *cx, JSString *src)
1827     {
1828     jsval nsval;
1829     JSString *uri;
1830     size_t urilen, srclen, length, offset, dstlen;
1831     jschar *chars;
1832     const jschar *srcp, *endp;
1833     JSXML *xml;
1834     const char *filename;
1835     uintN lineno;
1836     JSStackFrame *fp;
1837     JSOp op;
1838     JSParseNode *pn;
1839     JSXMLArray nsarray;
1840     uintN flags;
1841    
1842 siliconforks 399 static const char prefix[] = "<parent xmlns=\"";
1843     static const char middle[] = "\">";
1844 siliconforks 332 static const char suffix[] = "</parent>";
1845    
1846     #define constrlen(constr) (sizeof(constr) - 1)
1847    
1848     if (!js_GetDefaultXMLNamespace(cx, &nsval))
1849     return NULL;
1850     uri = GetURI(JSVAL_TO_OBJECT(nsval));
1851 siliconforks 399 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1852 siliconforks 332
1853 siliconforks 507 urilen = uri->length();
1854     srclen = src->length();
1855 siliconforks 332 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1856     constrlen(suffix);
1857    
1858 siliconforks 507 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
1859 siliconforks 332 if (!chars)
1860     return NULL;
1861    
1862     dstlen = length;
1863     js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1864     offset = dstlen;
1865 siliconforks 507 js_strncpy(chars + offset, uri->chars(), urilen);
1866 siliconforks 332 offset += urilen;
1867     dstlen = length - offset + 1;
1868     js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1869     &dstlen);
1870     offset += dstlen;
1871 siliconforks 507 srcp = src->chars();
1872 siliconforks 332 js_strncpy(chars + offset, srcp, srclen);
1873     offset += srclen;
1874     dstlen = length - offset + 1;
1875     js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1876     &dstlen);
1877     chars [offset + dstlen] = 0;
1878    
1879     xml = NULL;
1880 siliconforks 460 for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down)
1881 siliconforks 332 JS_ASSERT(!fp->script);
1882     filename = NULL;
1883     lineno = 1;
1884     if (fp) {
1885     op = (JSOp) *fp->regs->pc;
1886     if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1887     filename = fp->script->filename;
1888 siliconforks 399 lineno = js_FramePCToLineNumber(cx, fp);
1889 siliconforks 332 for (endp = srcp + srclen; srcp < endp; srcp++) {
1890     if (*srcp == '\n')
1891     --lineno;
1892     }
1893     }
1894     }
1895    
1896 siliconforks 460 {
1897     JSCompiler jsc(cx);
1898     if (jsc.init(chars, length, NULL, filename, lineno)) {
1899     pn = jsc.parseXMLText(js_GetTopStackFrame(cx)->scopeChain, false);
1900     if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1901     if (GetXMLSettingFlags(cx, &flags))
1902     xml = ParseNodeToXML(&jsc, pn, &nsarray, flags);
1903 siliconforks 332
1904 siliconforks 460 XMLArrayFinish(cx, &nsarray);
1905     }
1906     }
1907 siliconforks 332 }
1908    
1909 siliconforks 507 cx->free(chars);
1910 siliconforks 332 return xml;
1911    
1912     #undef constrlen
1913     }
1914    
1915     /*
1916     * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1917     *
1918     * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1919     * the constraint:
1920     *
1921     * for all x belonging to XML:
1922     * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1923     *
1924     * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1925     * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1926     *
1927     * Same goes for 10.4.1 Step 7(a).
1928     *
1929     * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1930     * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1931     * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1932     * undeclared namespaces associated with x not belonging to ancestorNS.
1933     */
1934     static JSXML *
1935     OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1936     {
1937     JSObject *ns;
1938    
1939     ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1940     xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1941     if (!ns || !xml)
1942     return xml;
1943     if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1944     if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1945     return NULL;
1946     ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1947     }
1948     xml->parent = NULL;
1949     return xml;
1950     }
1951    
1952     static JSObject *
1953     ToXML(JSContext *cx, jsval v)
1954     {
1955     JSObject *obj;
1956     JSXML *xml;
1957     JSClass *clasp;
1958     JSString *str;
1959     uint32 length;
1960    
1961     if (JSVAL_IS_PRIMITIVE(v)) {
1962     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1963     goto bad;
1964     } else {
1965     obj = JSVAL_TO_OBJECT(v);
1966     if (OBJECT_IS_XML(cx, obj)) {
1967 siliconforks 507 xml = (JSXML *) obj->getPrivate();
1968 siliconforks 332 if (xml->xml_class == JSXML_CLASS_LIST) {
1969     if (xml->xml_kids.length != 1)
1970     goto bad;
1971     xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1972     if (xml) {
1973     JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1974     return js_GetXMLObject(cx, xml);
1975     }
1976     }
1977     return obj;
1978     }
1979    
1980     clasp = OBJ_GET_CLASS(cx, obj);
1981     if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1982     JS_ASSERT(0);
1983     }
1984    
1985     if (clasp != &js_StringClass &&
1986     clasp != &js_NumberClass &&
1987     clasp != &js_BooleanClass) {
1988     goto bad;
1989     }
1990     }
1991    
1992     str = js_ValueToString(cx, v);
1993     if (!str)
1994     return NULL;
1995 siliconforks 507 if (str->empty()) {
1996 siliconforks 332 length = 0;
1997     #ifdef __GNUC__ /* suppress bogus gcc warnings */
1998     xml = NULL;
1999     #endif
2000     } else {
2001     xml = ParseXMLSource(cx, str);
2002     if (!xml)
2003     return NULL;
2004     length = JSXML_LENGTH(xml);
2005     }
2006    
2007     if (length == 0) {
2008     obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2009     if (!obj)
2010     return NULL;
2011     } else if (length == 1) {
2012     xml = OrphanXMLChild(cx, xml, 0);
2013     if (!xml)
2014     return NULL;
2015     obj = js_GetXMLObject(cx, xml);
2016     if (!obj)
2017     return NULL;
2018     } else {
2019     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2020     return NULL;
2021     }
2022     return obj;
2023    
2024     bad:
2025     js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2026     JSDVG_IGNORE_STACK, v, NULL);
2027     return NULL;
2028     }
2029    
2030     static JSBool
2031     Append(JSContext *cx, JSXML *list, JSXML *kid);
2032    
2033     static JSObject *
2034     ToXMLList(JSContext *cx, jsval v)
2035     {
2036     JSObject *obj, *listobj;
2037     JSXML *xml, *list, *kid;
2038     JSClass *clasp;
2039     JSString *str;
2040     uint32 i, length;
2041    
2042     if (JSVAL_IS_PRIMITIVE(v)) {
2043     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2044     goto bad;
2045     } else {
2046     obj = JSVAL_TO_OBJECT(v);
2047     if (OBJECT_IS_XML(cx, obj)) {
2048 siliconforks 507 xml = (JSXML *) obj->getPrivate();
2049 siliconforks 332 if (xml->xml_class != JSXML_CLASS_LIST) {
2050     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2051     if (!listobj)
2052     return NULL;
2053 siliconforks 507 list = (JSXML *) listobj->getPrivate();
2054 siliconforks 332 if (!Append(cx, list, xml))
2055     return NULL;
2056     return listobj;
2057     }
2058     return obj;
2059     }
2060    
2061     clasp = OBJ_GET_CLASS(cx, obj);
2062     if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2063     JS_ASSERT(0);
2064     }
2065    
2066     if (clasp != &js_StringClass &&
2067     clasp != &js_NumberClass &&
2068     clasp != &js_BooleanClass) {
2069     goto bad;
2070     }
2071     }
2072    
2073     str = js_ValueToString(cx, v);
2074     if (!str)
2075     return NULL;
2076 siliconforks 507 if (str->empty()) {
2077 siliconforks 332 xml = NULL;
2078     length = 0;
2079     } else {
2080     if (!js_EnterLocalRootScope(cx))
2081     return NULL;
2082     xml = ParseXMLSource(cx, str);
2083     if (!xml) {
2084     js_LeaveLocalRootScope(cx);
2085     return NULL;
2086     }
2087     length = JSXML_LENGTH(xml);
2088     }
2089    
2090     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2091     if (listobj) {
2092 siliconforks 507 list = (JSXML *) listobj->getPrivate();
2093 siliconforks 332 for (i = 0; i < length; i++) {
2094     kid = OrphanXMLChild(cx, xml, i);
2095     if (!kid || !Append(cx, list, kid)) {
2096     listobj = NULL;
2097     break;
2098     }
2099     }
2100     }
2101    
2102     if (xml)
2103     js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2104     return listobj;
2105    
2106     bad:
2107     js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2108     JSDVG_IGNORE_STACK, v, NULL);
2109     return NULL;
2110     }
2111    
2112     /*
2113     * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2114     * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2115     * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2116     * MakeXMLSpecialString subroutine.
2117     *
2118 siliconforks 507 * These functions mutate cb, leaving it empty.
2119 siliconforks 332 */
2120     static JSString *
2121 siliconforks 507 MakeXMLSpecialString(JSContext *cx, JSCharBuffer &cb,
2122 siliconforks 332 JSString *str, JSString *str2,
2123     const jschar *prefix, size_t prefixlength,
2124     const jschar *suffix, size_t suffixlength)
2125     {
2126 siliconforks 507 if (!cb.append(prefix, prefixlength) || !AppendString(cb, str))
2127     return NULL;
2128     if (str2 && !str2->empty()) {
2129     if (!cb.append(' ') || !AppendString(cb, str2))
2130     return NULL;
2131 siliconforks 332 }
2132 siliconforks 507 if (!cb.append(suffix, suffixlength))
2133 siliconforks 332 return NULL;
2134    
2135 siliconforks 507 return js_NewStringFromCharBuffer(cx, cb);
2136 siliconforks 332 }
2137    
2138     static JSString *
2139 siliconforks 507 MakeXMLCDATAString(JSContext *cx, JSCharBuffer &cb, JSString *str)
2140 siliconforks 332 {
2141     static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2142     'C', 'D', 'A', 'T', 'A',
2143     '['};
2144     static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2145    
2146 siliconforks 507 return MakeXMLSpecialString(cx, cb, str, NULL,
2147 siliconforks 332 cdata_prefix_ucNstr, 9,
2148     cdata_suffix_ucNstr, 3);
2149     }
2150    
2151     static JSString *
2152 siliconforks 507 MakeXMLCommentString(JSContext *cx, JSCharBuffer &cb, JSString *str)
2153 siliconforks 332 {
2154     static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2155     static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2156    
2157 siliconforks 507 return MakeXMLSpecialString(cx, cb, str, NULL,
2158 siliconforks 332 comment_prefix_ucNstr, 4,
2159     comment_suffix_ucNstr, 3);
2160     }
2161    
2162     static JSString *
2163 siliconforks 507 MakeXMLPIString(JSContext *cx, JSCharBuffer &cb, JSString *name,
2164 siliconforks 332 JSString *value)
2165     {
2166     static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2167     static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2168    
2169 siliconforks 507 return MakeXMLSpecialString(cx, cb, name, value,
2170 siliconforks 332 pi_prefix_ucNstr, 2,
2171     pi_suffix_ucNstr, 2);
2172     }
2173    
2174     /*
2175     * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2176     * equals, a double quote, an attribute value, and a closing double quote.
2177     */
2178 siliconforks 507 static bool
2179     AppendAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *valstr)
2180 siliconforks 332 {
2181 siliconforks 507 if (!cb.append('='))
2182     return false;
2183 siliconforks 332 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2184 siliconforks 507 return valstr && AppendString(cb, valstr);
2185 siliconforks 332 }
2186    
2187     /*
2188     * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2189 siliconforks 507
2190     * These functions mutate cb, leaving it empty.
2191 siliconforks 332 */
2192     static JSString *
2193 siliconforks 507 EscapeElementValue(JSContext *cx, JSCharBuffer &cb, JSString *str)
2194 siliconforks 332 {
2195 siliconforks 507 size_t length;
2196     const jschar *start;
2197     str->getCharsAndLength(start, length);
2198 siliconforks 332
2199 siliconforks 507 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2200     jschar c = *cp;
2201     switch (*cp) {
2202     case '<':
2203     if (!js_AppendLiteral(cb, js_lt_entity_str))
2204     return NULL;
2205     break;
2206     case '>':
2207     if (!js_AppendLiteral(cb, js_gt_entity_str))
2208     return NULL;
2209     break;
2210     case '&':
2211     if (!js_AppendLiteral(cb, js_amp_entity_str))
2212     return NULL;
2213     break;
2214     default:
2215     if (!cb.append(c))
2216     return NULL;
2217 siliconforks 332 }
2218     }
2219 siliconforks 507 return js_NewStringFromCharBuffer(cx, cb);
2220 siliconforks 332 }
2221    
2222     /*
2223     * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2224 siliconforks 507 *
2225     * These functions mutate cb, leaving it empty.
2226 siliconforks 332 */
2227     static JSString *
2228 siliconforks 507 EscapeAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *str,
2229 siliconforks 332 JSBool quote)
2230     {
2231 siliconforks 507 size_t length;
2232     const jschar *start;
2233     str->getCharsAndLength(start, length);
2234 siliconforks 332
2235 siliconforks 507 if (quote && !cb.append('"'))
2236     return NULL;
2237 siliconforks 332
2238 siliconforks 507 for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2239     jschar c = *cp;
2240     switch (c) {
2241     case '"':
2242     if (!js_AppendLiteral(cb, js_quot_entity_str))
2243     return NULL;
2244     break;
2245     case '<':
2246     if (!js_AppendLiteral(cb, js_lt_entity_str))
2247     return NULL;
2248     break;
2249     case '&':
2250     if (!js_AppendLiteral(cb, js_amp_entity_str))
2251     return NULL;
2252     break;
2253     case '\n':
2254     if (!js_AppendLiteral(cb, "&#xA;"))
2255     return NULL;
2256     break;
2257     case '\r':
2258     if (!js_AppendLiteral(cb, "&#xD;"))
2259     return NULL;
2260     break;
2261     case '\t':
2262     if (!js_AppendLiteral(cb, "&#x9;"))
2263     return NULL;
2264     break;
2265     default:
2266     if (!cb.append(c))
2267     return NULL;
2268 siliconforks 332 }
2269     }
2270 siliconforks 507
2271     if (quote && !cb.append('"'))
2272     return NULL;
2273    
2274     return js_NewStringFromCharBuffer(cx, cb);
2275 siliconforks 332 }
2276    
2277     /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2278     static JSObject *
2279     GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2280     {
2281     JSString *uri, *prefix, *nsprefix;
2282     JSObject *match, *ns;
2283     uint32 i, n;
2284     jsval argv[2];
2285    
2286     uri = GetURI(qn);
2287     prefix = GetPrefix(qn);
2288     JS_ASSERT(uri);
2289     if (!uri) {
2290     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2291     JSMSG_BAD_XML_NAMESPACE,
2292     prefix
2293     ? js_ValueToPrintableString(cx,
2294     STRING_TO_JSVAL(prefix))
2295     : js_undefined_str);
2296     return NULL;
2297     }
2298    
2299     /* Look for a matching namespace in inScopeNSes, if provided. */
2300     match = NULL;
2301     if (inScopeNSes) {
2302     for (i = 0, n = inScopeNSes->length; i < n; i++) {
2303     ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2304     if (!ns)
2305     continue;
2306    
2307     /*
2308     * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2309     * If we preserve prefixes, we must match null prefix against
2310     * an empty prefix of ns, in order to avoid generating redundant
2311     * prefixed and default namespaces for cases such as:
2312     *
2313     * x = <t xmlns="http://foo.com"/>
2314     * print(x.toXMLString());
2315     *
2316     * Per 10.3.2.1, the namespace attribute in t has an empty string
2317     * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2318     *
2319     * 1. If the [local name] property of a is "xmlns"
2320     * a. Map ns.prefix to the empty string
2321     *
2322     * But t's name has a null prefix in this implementation, meaning
2323     * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2324     * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2325     * saying how "no value" maps to an ECMA-357 value -- but it must
2326     * map to the *undefined* prefix value).
2327     *
2328     * Since "" != undefined (or null, in the current implementation)
2329     * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2330     * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2331     * This spec bug leads to ToXMLString results that duplicate the
2332     * declared namespace.
2333     */
2334     if (js_EqualStrings(GetURI(ns), uri)) {
2335     nsprefix = GetPrefix(ns);
2336     if (nsprefix == prefix ||
2337     ((nsprefix && prefix)
2338     ? js_EqualStrings(nsprefix, prefix)
2339 siliconforks 507 : (nsprefix ? nsprefix : prefix)->empty())) {
2340 siliconforks 332 match = ns;
2341     break;
2342     }
2343     }
2344     }
2345     }
2346    
2347     /* If we didn't match, make a new namespace from qn. */
2348     if (!match) {
2349     argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2350     argv[1] = STRING_TO_JSVAL(uri);
2351     ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2352     2, argv);
2353     if (!ns)
2354     return NULL;
2355     match = ns;
2356     }
2357     return match;
2358     }
2359    
2360     static JSString *
2361     GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2362     {
2363     const jschar *cp, *start, *end;
2364     size_t length, newlength, offset;
2365     uint32 i, n, m, serial;
2366     jschar *bp, *dp;
2367     JSBool done;
2368     JSObject *ns;
2369     JSString *nsprefix, *prefix;
2370    
2371 siliconforks 507 JS_ASSERT(!uri->empty());
2372 siliconforks 332
2373     /*
2374     * If there are no *declared* namespaces, skip all collision detection and
2375     * return a short prefix quickly; an example of such a situation:
2376     *
2377     * var x = <f/>;
2378     * var n = new Namespace("http://example.com/");
2379     * x.@n::att = "val";
2380     * x.toXMLString();
2381     *
2382     * This is necessary for various log10 uses below to be valid.
2383     */
2384     if (decls->length == 0)
2385     return JS_NewStringCopyZ(cx, "a");
2386    
2387     /*
2388     * Try peeling off the last filename suffix or pathname component till
2389     * we have a valid XML name. This heuristic will prefer "xul" given
2390     * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2391     * likely URI of the form ".../xbl2/2005".
2392     */
2393 siliconforks 507 uri->getCharsAndEnd(start, end);
2394 siliconforks 332 cp = end;
2395     while (--cp > start) {
2396     if (*cp == '.' || *cp == '/' || *cp == ':') {
2397     ++cp;
2398 siliconforks 507 length = end - cp;
2399 siliconforks 332 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2400     break;
2401     end = --cp;
2402     }
2403     }
2404 siliconforks 507 length = end - cp;
2405 siliconforks 332
2406     /*
2407     * If the namespace consisted only of non-XML names or names that begin
2408     * case-insensitively with "xml", arbitrarily create a prefix consisting
2409     * of 'a's of size length (allowing dp-calculating code to work with or
2410     * without this branch executing) plus the space for storing a hyphen and
2411     * the serial number (avoiding reallocation if a collision happens).
2412     */
2413     bp = (jschar *) cp;
2414     newlength = length;
2415     if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2416     newlength = length + 2 + (size_t) log10((double) decls->length);
2417     bp = (jschar *)
2418 siliconforks 507 cx->malloc((newlength + 1) * sizeof(jschar));
2419 siliconforks 332 if (!bp)
2420     return NULL;
2421    
2422     bp[newlength] = 0;
2423     for (i = 0; i < newlength; i++)
2424     bp[i] = 'a';
2425     }
2426    
2427     /*
2428     * Now search through decls looking for a collision. If we collide with
2429     * an existing prefix, start tacking on a hyphen and a serial number.
2430     */
2431     serial = 0;
2432     do {
2433     done = JS_TRUE;
2434     for (i = 0, n = decls->length; i < n; i++) {
2435     ns = XMLARRAY_MEMBER(decls, i, JSObject);
2436     if (ns && (nsprefix = GetPrefix(ns)) &&
2437 siliconforks 507 nsprefix->length() == newlength &&
2438     !memcmp(nsprefix->chars(), bp,
2439 siliconforks 332 newlength * sizeof(jschar))) {
2440     if (bp == cp) {
2441     newlength = length + 2 + (size_t) log10((double) n);
2442     bp = (jschar *)
2443 siliconforks 507 cx->malloc((newlength + 1) * sizeof(jschar));
2444 siliconforks 332 if (!bp)
2445     return NULL;
2446     js_strncpy(bp, cp, length);
2447     }
2448    
2449     ++serial;
2450     JS_ASSERT(serial <= n);
2451     dp = bp + length + 2 + (size_t) log10((double) serial);
2452     *dp = 0;
2453     for (m = serial; m != 0; m /= 10)
2454     *--dp = (jschar)('0' + m % 10);
2455     *--dp = '-';
2456     JS_ASSERT(dp == bp + length);
2457    
2458     done = JS_FALSE;
2459     break;
2460     }
2461     }
2462     } while (!done);
2463    
2464     if (bp == cp) {
2465 siliconforks 507 offset = cp - start;
2466 siliconforks 332 prefix = js_NewDependentString(cx, uri, offset, length);
2467     } else {
2468     prefix = js_NewString(cx, bp, newlength);
2469     if (!prefix)
2470 siliconforks 507 cx->free(bp);
2471 siliconforks 332 }
2472     return prefix;
2473     }
2474    
2475     static JSBool
2476     namespace_match(const void *a, const void *b)
2477     {
2478     const JSObject *nsa = (const JSObject *) a;
2479     const JSObject *nsb = (const JSObject *) b;
2480     JSString *prefixa, *prefixb = GetPrefix(nsb);
2481    
2482     if (prefixb) {
2483     prefixa = GetPrefix(nsa);
2484     return prefixa && js_EqualStrings(prefixa, prefixb);
2485     }
2486     return js_EqualStrings(GetURI(nsa), GetURI(nsb));
2487     }
2488    
2489     /* ECMA-357 10.2.1 and 10.2.2 */
2490     #define TO_SOURCE_FLAG 0x80000000
2491    
2492     static JSString *
2493     XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2494     uint32 indentLevel)
2495     {
2496     JSBool pretty, indentKids;
2497 siliconforks 507 JSCharBuffer cb(cx);
2498     JSString *str, *prefix, *nsuri;
2499 siliconforks 332 uint32 i, n, nextIndentLevel;
2500     JSXMLArray empty, decls, ancdecls;
2501     JSObject *ns, *ns2;
2502    
2503     if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2504     return NULL;
2505    
2506     if (pretty) {
2507 siliconforks 507 if (!cb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2508 siliconforks 332 return NULL;
2509     }
2510 siliconforks 507
2511 siliconforks 332 str = NULL;
2512    
2513     switch (xml->xml_class) {
2514     case JSXML_CLASS_TEXT:
2515     /* Step 4. */
2516     if (pretty) {
2517     str = ChompXMLWhitespace(cx, xml->xml_value);
2518     if (!str)
2519     return NULL;
2520     } else {
2521     str = xml->xml_value;
2522     }
2523 siliconforks 507 return EscapeElementValue(cx, cb, str);
2524 siliconforks 332
2525     case JSXML_CLASS_ATTRIBUTE:
2526     /* Step 5. */
2527 siliconforks 507 return EscapeAttributeValue(cx, cb, xml->xml_value,
2528 siliconforks 332 (indentLevel & TO_SOURCE_FLAG) != 0);
2529    
2530     case JSXML_CLASS_COMMENT:
2531     /* Step 6. */
2532 siliconforks 507 return MakeXMLCommentString(cx, cb, xml->xml_value);
2533 siliconforks 332
2534     case JSXML_CLASS_PROCESSING_INSTRUCTION:
2535     /* Step 7. */
2536 siliconforks 507 return MakeXMLPIString(cx, cb, GetLocalName(xml->name),
2537 siliconforks 332 xml->xml_value);
2538    
2539     case JSXML_CLASS_LIST:
2540     /* ECMA-357 10.2.2. */
2541 siliconforks 507 {
2542     JSXMLArrayCursor cursor(&xml->xml_kids);
2543     i = 0;
2544     while (JSXML *kid = (JSXML *) cursor.getNext()) {
2545     if (pretty && i != 0) {
2546     if (!cb.append('\n'))
2547     return NULL;
2548     }
2549 siliconforks 332
2550 siliconforks 507 JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2551     if (!kidstr || !AppendString(cb, kidstr))
2552     return NULL;
2553     ++i;