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

Contents of /trunk/js/jsxml.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (11 years, 11 months ago) by siliconforks
File size: 251191 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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
1848 urilen = JSSTRING_LENGTH(uri);
1849 srclen = JSSTRING_LENGTH(src);
1850 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1851 constrlen(suffix);
1852
1853 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1854 if (!chars)
1855 return NULL;
1856
1857 dstlen = length;
1858 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1859 offset = dstlen;
1860 js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
1861 offset += urilen;
1862 dstlen = length - offset + 1;
1863 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1864 &dstlen);
1865 offset += dstlen;
1866 srcp = JSSTRING_CHARS(src);
1867 js_strncpy(chars + offset, srcp, srclen);
1868 offset += srclen;
1869 dstlen = length - offset + 1;
1870 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1871 &dstlen);
1872 chars [offset + dstlen] = 0;
1873
1874 xml = NULL;
1875 for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
1876 JS_ASSERT(!fp->script);
1877 filename = NULL;
1878 lineno = 1;
1879 if (fp) {
1880 op = (JSOp) *fp->regs->pc;
1881 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1882 filename = fp->script->filename;
1883 lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
1884 for (endp = srcp + srclen; srcp < endp; srcp++) {
1885 if (*srcp == '\n')
1886 --lineno;
1887 }
1888 }
1889 }
1890
1891 if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
1892 filename, lineno))
1893 goto out;
1894 pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
1895 if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1896 if (GetXMLSettingFlags(cx, &flags))
1897 xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
1898
1899 XMLArrayFinish(cx, &nsarray);
1900 }
1901 js_FinishParseContext(cx, &pc);
1902
1903 out:
1904 JS_free(cx, chars);
1905 return xml;
1906
1907 #undef constrlen
1908 }
1909
1910 /*
1911 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1912 *
1913 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1914 * the constraint:
1915 *
1916 * for all x belonging to XML:
1917 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1918 *
1919 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1920 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1921 *
1922 * Same goes for 10.4.1 Step 7(a).
1923 *
1924 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1925 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1926 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1927 * undeclared namespaces associated with x not belonging to ancestorNS.
1928 */
1929 static JSXML *
1930 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1931 {
1932 JSObject *ns;
1933
1934 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1935 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1936 if (!ns || !xml)
1937 return xml;
1938 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1939 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1940 return NULL;
1941 ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1942 }
1943 xml->parent = NULL;
1944 return xml;
1945 }
1946
1947 static JSObject *
1948 ToXML(JSContext *cx, jsval v)
1949 {
1950 JSObject *obj;
1951 JSXML *xml;
1952 JSClass *clasp;
1953 JSString *str;
1954 uint32 length;
1955
1956 if (JSVAL_IS_PRIMITIVE(v)) {
1957 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1958 goto bad;
1959 } else {
1960 obj = JSVAL_TO_OBJECT(v);
1961 if (OBJECT_IS_XML(cx, obj)) {
1962 xml = (JSXML *) JS_GetPrivate(cx, obj);
1963 if (xml->xml_class == JSXML_CLASS_LIST) {
1964 if (xml->xml_kids.length != 1)
1965 goto bad;
1966 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1967 if (xml) {
1968 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1969 return js_GetXMLObject(cx, xml);
1970 }
1971 }
1972 return obj;
1973 }
1974
1975 clasp = OBJ_GET_CLASS(cx, obj);
1976 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1977 JS_ASSERT(0);
1978 }
1979
1980 if (clasp != &js_StringClass &&
1981 clasp != &js_NumberClass &&
1982 clasp != &js_BooleanClass) {
1983 goto bad;
1984 }
1985 }
1986
1987 str = js_ValueToString(cx, v);
1988 if (!str)
1989 return NULL;
1990 if (IS_EMPTY(str)) {
1991 length = 0;
1992 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1993 xml = NULL;
1994 #endif
1995 } else {
1996 xml = ParseXMLSource(cx, str);
1997 if (!xml)
1998 return NULL;
1999 length = JSXML_LENGTH(xml);
2000 }
2001
2002 if (length == 0) {
2003 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2004 if (!obj)
2005 return NULL;
2006 } else if (length == 1) {
2007 xml = OrphanXMLChild(cx, xml, 0);
2008 if (!xml)
2009 return NULL;
2010 obj = js_GetXMLObject(cx, xml);
2011 if (!obj)
2012 return NULL;
2013 } else {
2014 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2015 return NULL;
2016 }
2017 return obj;
2018
2019 bad:
2020 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2021 JSDVG_IGNORE_STACK, v, NULL);
2022 return NULL;
2023 }
2024
2025 static JSBool
2026 Append(JSContext *cx, JSXML *list, JSXML *kid);
2027
2028 static JSObject *
2029 ToXMLList(JSContext *cx, jsval v)
2030 {
2031 JSObject *obj, *listobj;
2032 JSXML *xml, *list, *kid;
2033 JSClass *clasp;
2034 JSString *str;
2035 uint32 i, length;
2036
2037 if (JSVAL_IS_PRIMITIVE(v)) {
2038 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2039 goto bad;
2040 } else {
2041 obj = JSVAL_TO_OBJECT(v);
2042 if (OBJECT_IS_XML(cx, obj)) {
2043 xml = (JSXML *) JS_GetPrivate(cx, obj);
2044 if (xml->xml_class != JSXML_CLASS_LIST) {
2045 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2046 if (!listobj)
2047 return NULL;
2048 list = (JSXML *) JS_GetPrivate(cx, listobj);
2049 if (!Append(cx, list, xml))
2050 return NULL;
2051 return listobj;
2052 }
2053 return obj;
2054 }
2055
2056 clasp = OBJ_GET_CLASS(cx, obj);
2057 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2058 JS_ASSERT(0);
2059 }
2060
2061 if (clasp != &js_StringClass &&
2062 clasp != &js_NumberClass &&
2063 clasp != &js_BooleanClass) {
2064 goto bad;
2065 }
2066 }
2067
2068 str = js_ValueToString(cx, v);
2069 if (!str)
2070 return NULL;
2071 if (IS_EMPTY(str)) {
2072 xml = NULL;
2073 length = 0;
2074 } else {
2075 if (!js_EnterLocalRootScope(cx))
2076 return NULL;
2077 xml = ParseXMLSource(cx, str);
2078 if (!xml) {
2079 js_LeaveLocalRootScope(cx);
2080 return NULL;
2081 }
2082 length = JSXML_LENGTH(xml);
2083 }
2084
2085 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2086 if (listobj) {
2087 list = (JSXML *) JS_GetPrivate(cx, listobj);
2088 for (i = 0; i < length; i++) {
2089 kid = OrphanXMLChild(cx, xml, i);
2090 if (!kid || !Append(cx, list, kid)) {
2091 listobj = NULL;
2092 break;
2093 }
2094 }
2095 }
2096
2097 if (xml)
2098 js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2099 return listobj;
2100
2101 bad:
2102 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2103 JSDVG_IGNORE_STACK, v, NULL);
2104 return NULL;
2105 }
2106
2107 /*
2108 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2109 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2110 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2111 * MakeXMLSpecialString subroutine.
2112 *
2113 * These functions take ownership of sb->base, if sb is non-null, in all cases
2114 * of success or failure.
2115 */
2116 static JSString *
2117 MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2118 JSString *str, JSString *str2,
2119 const jschar *prefix, size_t prefixlength,
2120 const jschar *suffix, size_t suffixlength)
2121 {
2122 JSStringBuffer localSB;
2123 size_t length, length2, newlength;
2124 jschar *bp, *base;
2125
2126 if (!sb) {
2127 sb = &localSB;
2128 js_InitStringBuffer(sb);
2129 }
2130
2131 length = JSSTRING_LENGTH(str);
2132 length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2133 newlength = STRING_BUFFER_OFFSET(sb) +
2134 prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2135 suffixlength;
2136 bp = base = (jschar *)
2137 JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2138 if (!bp) {
2139 js_FinishStringBuffer(sb);
2140 return NULL;
2141 }
2142
2143 bp += STRING_BUFFER_OFFSET(sb);
2144 js_strncpy(bp, prefix, prefixlength);
2145 bp += prefixlength;
2146 js_strncpy(bp, JSSTRING_CHARS(str), length);
2147 bp += length;
2148 if (length2 != 0) {
2149 *bp++ = (jschar) ' ';
2150 js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2151 bp += length2;
2152 }
2153 js_strncpy(bp, suffix, suffixlength);
2154 bp[suffixlength] = 0;
2155
2156 str = js_NewString(cx, base, newlength);
2157 if (!str)
2158 free(base);
2159 return str;
2160 }
2161
2162 static JSString *
2163 MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2164 {
2165 static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2166 'C', 'D', 'A', 'T', 'A',
2167 '['};
2168 static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2169
2170 return MakeXMLSpecialString(cx, sb, str, NULL,
2171 cdata_prefix_ucNstr, 9,
2172 cdata_suffix_ucNstr, 3);
2173 }
2174
2175 static JSString *
2176 MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2177 {
2178 static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2179 static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2180
2181 return MakeXMLSpecialString(cx, sb, str, NULL,
2182 comment_prefix_ucNstr, 4,
2183 comment_suffix_ucNstr, 3);
2184 }
2185
2186 static JSString *
2187 MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2188 JSString *value)
2189 {
2190 static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2191 static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2192
2193 return MakeXMLSpecialString(cx, sb, name, value,
2194 pi_prefix_ucNstr, 2,
2195 pi_suffix_ucNstr, 2);
2196 }
2197
2198 /*
2199 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2200 * equals, a double quote, an attribute value, and a closing double quote.
2201 */
2202 static void
2203 AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2204 {
2205 js_AppendChar(sb, '=');
2206 valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2207 if (!valstr) {
2208 if (STRING_BUFFER_OK(sb)) {
2209 free(sb->base);
2210 sb->base = STRING_BUFFER_ERROR_BASE;
2211 }
2212 return;
2213 }
2214 js_AppendJSString(sb, valstr);
2215 }
2216
2217 /*
2218 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2219 *
2220 * This function takes ownership of sb->base, if sb is non-null, in all cases
2221 * of success or failure.
2222 */
2223 static JSString *
2224 EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2225 {
2226 size_t length, newlength;
2227 const jschar *cp, *start, *end;
2228 jschar c;
2229
2230 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2231 newlength = length;
2232 for (cp = start, end = cp + length; cp < end; cp++) {
2233 c = *cp;
2234 if (c == '<' || c == '>')
2235 newlength += 3;
2236 else if (c == '&')
2237 newlength += 4;
2238
2239 if (newlength < length) {
2240 js_ReportAllocationOverflow(cx);
2241 return NULL;
2242 }
2243 }
2244 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2245 JSStringBuffer localSB;
2246 if (!sb) {
2247 sb = &localSB;
2248 js_InitStringBuffer(sb);
2249 }
2250 if (!sb->grow(sb, newlength)) {
2251 JS_ReportOutOfMemory(cx);
2252 return NULL;
2253 }
2254 for (cp = start; cp < end; cp++) {
2255 c = *cp;
2256 if (c == '<')
2257 js_AppendCString(sb, js_lt_entity_str);
2258 else if (c == '>')
2259 js_AppendCString(sb, js_gt_entity_str);
2260 else if (c == '&')
2261 js_AppendCString(sb, js_amp_entity_str);
2262 else
2263 js_AppendChar(sb, c);
2264 }
2265 JS_ASSERT(STRING_BUFFER_OK(sb));
2266 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2267 if (!str)
2268 js_FinishStringBuffer(sb);
2269 }
2270 return str;
2271 }
2272
2273 /*
2274 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2275 * This function takes ownership of sb->base, if sb is non-null, in all cases.
2276 */
2277 static JSString *
2278 EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str,
2279 JSBool quote)
2280 {
2281 size_t length, newlength;
2282 const jschar *cp, *start, *end;
2283 jschar c;
2284
2285 JSSTRING_CHARS_AND_LENGTH(str, start, length);
2286 newlength = length + (quote ? 2 : 0);
2287 for (cp = start, end = cp + length; cp < end; cp++) {
2288 c = *cp;
2289 if (c == '"')
2290 newlength += 5;
2291 else if (c == '<')
2292 newlength += 3;
2293 else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2294 newlength += 4;
2295
2296 if (newlength < length) {
2297 js_ReportAllocationOverflow(cx);
2298 return NULL;
2299 }
2300 }
2301 if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2302 JSStringBuffer localSB;
2303 if (!sb) {
2304 sb = &localSB;
2305 js_InitStringBuffer(sb);
2306 }
2307 if (!sb->grow(sb, newlength)) {
2308 JS_ReportOutOfMemory(cx);
2309 return NULL;
2310 }
2311 if (quote)
2312 js_AppendChar(sb, '"');
2313 for (cp = start; cp < end; cp++) {
2314 c = *cp;
2315 if (c == '"')
2316 js_AppendCString(sb, js_quot_entity_str);
2317 else if (c == '<')
2318 js_AppendCString(sb, js_lt_entity_str);
2319 else if (c == '&')
2320 js_AppendCString(sb, js_amp_entity_str);
2321 else if (c == '\n')
2322 js_AppendCString(sb, "&#xA;");
2323 else if (c == '\r')
2324 js_AppendCString(sb, "&#xD;");
2325 else if (c == '\t')
2326 js_AppendCString(sb, "&#x9;");
2327 else
2328 js_AppendChar(sb, c);
2329 }
2330 if (quote)
2331 js_AppendChar(sb, '"');
2332 JS_ASSERT(STRING_BUFFER_OK(sb));
2333 str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2334 if (!str)
2335 js_FinishStringBuffer(sb);
2336 }
2337 return str;
2338 }
2339
2340 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2341 static JSObject *
2342 GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2343 {
2344 JSString *uri, *prefix, *nsprefix;
2345 JSObject *match, *ns;
2346 uint32 i, n;
2347 jsval argv[2];
2348
2349 uri = GetURI(qn);
2350 prefix = GetPrefix(qn);
2351 JS_ASSERT(uri);
2352 if (!uri) {
2353 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2354 JSMSG_BAD_XML_NAMESPACE,
2355 prefix
2356 ? js_ValueToPrintableString(cx,
2357 STRING_TO_JSVAL(prefix))
2358 : js_undefined_str);
2359 return NULL;
2360 }
2361
2362 /* Look for a matching namespace in inScopeNSes, if provided. */
2363 match = NULL;
2364 if (inScopeNSes) {
2365 for (i = 0, n = inScopeNSes->length; i < n; i++) {
2366 ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2367 if (!ns)
2368 continue;
2369
2370 /*
2371 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2372 * If we preserve prefixes, we must match null prefix against
2373 * an empty prefix of ns, in order to avoid generating redundant
2374 * prefixed and default namespaces for cases such as:
2375 *
2376 * x = <t xmlns="http://foo.com"/>
2377 * print(x.toXMLString());
2378 *
2379 * Per 10.3.2.1, the namespace attribute in t has an empty string
2380 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2381 *
2382 * 1. If the [local name] property of a is "xmlns"
2383 * a. Map ns.prefix to the empty string
2384 *
2385 * But t's name has a null prefix in this implementation, meaning
2386 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2387 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2388 * saying how "no value" maps to an ECMA-357 value -- but it must
2389 * map to the *undefined* prefix value).
2390 *
2391 * Since "" != undefined (or null, in the current implementation)
2392 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2393 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2394 * This spec bug leads to ToXMLString results that duplicate the
2395 * declared namespace.
2396 */
2397 if (js_EqualStrings(GetURI(ns), uri)) {
2398 nsprefix = GetPrefix(ns);
2399 if (nsprefix == prefix ||
2400 ((nsprefix && prefix)
2401 ? js_EqualStrings(nsprefix, prefix)
2402 : IS_EMPTY(nsprefix ? nsprefix : prefix))) {
2403 match = ns;
2404 break;
2405 }
2406 }
2407 }
2408 }
2409
2410 /* If we didn't match, make a new namespace from qn. */
2411 if (!match) {
2412 argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2413 argv[1] = STRING_TO_JSVAL(uri);
2414 ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2415 2, argv);
2416 if (!ns)
2417 return NULL;
2418 match = ns;
2419 }
2420 return match;
2421 }
2422
2423 static JSString *
2424 GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2425 {
2426 const jschar *cp, *start, *end;
2427 size_t length, newlength, offset;
2428 uint32 i, n, m, serial;
2429 jschar *bp, *dp;
2430 JSBool done;
2431 JSObject *ns;
2432 JSString *nsprefix, *prefix;
2433
2434 JS_ASSERT(!IS_EMPTY(uri));
2435
2436 /*
2437 * If there are no *declared* namespaces, skip all collision detection and
2438 * return a short prefix quickly; an example of such a situation:
2439 *
2440 * var x = <f/>;
2441 * var n = new Namespace("http://example.com/");
2442 * x.@n::att = "val";
2443 * x.toXMLString();
2444 *
2445 * This is necessary for various log10 uses below to be valid.
2446 */
2447 if (decls->length == 0)
2448 return JS_NewStringCopyZ(cx, "a");
2449
2450 /*
2451 * Try peeling off the last filename suffix or pathname component till
2452 * we have a valid XML name. This heuristic will prefer "xul" given
2453 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2454 * likely URI of the form ".../xbl2/2005".
2455 */
2456 JSSTRING_CHARS_AND_END(uri, start, end);
2457 cp = end;
2458 while (--cp > start) {
2459 if (*cp == '.' || *cp == '/' || *cp == ':') {
2460 ++cp;
2461 length = PTRDIFF(end, cp, jschar);
2462 if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2463 break;
2464 end = --cp;
2465 }
2466 }
2467 length = PTRDIFF(end, cp, jschar);
2468
2469 /*
2470 * If the namespace consisted only of non-XML names or names that begin
2471 * case-insensitively with "xml", arbitrarily create a prefix consisting
2472 * of 'a's of size length (allowing dp-calculating code to work with or
2473 * without this branch executing) plus the space for storing a hyphen and
2474 * the serial number (avoiding reallocation if a collision happens).
2475 */
2476 bp = (jschar *) cp;
2477 newlength = length;
2478 if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2479 newlength = length + 2 + (size_t) log10((double) decls->length);
2480 bp = (jschar *)
2481 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2482 if (!bp)
2483 return NULL;
2484
2485 bp[newlength] = 0;
2486 for (i = 0; i < newlength; i++)
2487 bp[i] = 'a';
2488 }
2489
2490 /*
2491 * Now search through decls looking for a collision. If we collide with
2492 * an existing prefix, start tacking on a hyphen and a serial number.
2493 */
2494 serial = 0;
2495 do {
2496 done = JS_TRUE;
2497 for (i = 0, n = decls->length; i < n; i++) {
2498 ns = XMLARRAY_MEMBER(decls, i, JSObject);
2499 if (ns && (nsprefix = GetPrefix(ns)) &&
2500 JSSTRING_LENGTH(nsprefix) == newlength &&
2501 !memcmp(JSSTRING_CHARS(nsprefix), bp,
2502 newlength * sizeof(jschar))) {
2503 if (bp == cp) {
2504 newlength = length + 2 + (size_t) log10((double) n);
2505 bp = (jschar *)
2506 JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2507 if (!bp)
2508 return NULL;
2509 js_strncpy(bp, cp, length);
2510 }
2511
2512 ++serial;
2513 JS_ASSERT(serial <= n);
2514 dp = bp + length + 2 + (size_t) log10((double) serial);
2515 *dp = 0;
2516 for (m = serial; m != 0; m /= 10)
2517 *--dp = (jschar)('0' + m % 10);
2518 *--dp = '-';
2519 JS_ASSERT(dp == bp + length);
2520
2521 done = JS_FALSE;
2522 break;
2523 }
2524 }
2525 } while (!done);
2526
2527 if (bp == cp) {
2528 offset = PTRDIFF(cp, start, jschar);
2529 prefix = js_NewDependentString(cx, uri, offset, length);
2530 } else {
2531 prefix = js_NewString(cx, bp, newlength);
2532 if (!prefix)
2533 JS_free(cx, bp);
2534 }
2535 return prefix;
2536 }
2537
2538 static JSBool
2539 namespace_match(const void *a, const void *b)
2540 {
2541 const JSObject *nsa = (const JSObject *) a;
2542 const JSObject *nsb = (const JSObject *) b;
2543 JSString *prefixa, *prefixb = GetPrefix(nsb);
2544
2545 if (prefixb) {
2546 prefixa = GetPrefix(nsa);
2547 return prefixa && js_EqualStrings(prefixa, prefixb);
2548 }
2549 return js_EqualStrings(GetURI(nsa), GetURI(nsb));
2550 }
2551
2552 /* ECMA-357 10.2.1 and 10.2.2 */
2553 #define TO_SOURCE_FLAG 0x80000000
2554
2555 static JSString *
2556 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2557 uint32 indentLevel)
2558 {
2559 JSBool pretty, indentKids;
2560 JSStringBuffer sb;
2561 JSString *str, *prefix, *kidstr, *nsuri;
2562 JSXMLArrayCursor cursor;
2563 uint32 i, n, nextIndentLevel;
2564 JSXMLArray empty, decls, ancdecls;
2565 JSObject *ns, *ns2;
2566 JSXML *attr, *kid;
2567
2568 if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2569 return NULL;
2570
2571 js_InitStringBuffer(&sb);
2572 if (pretty) {
2573 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2574
2575 if (!STRING_BUFFER_OK(&sb)) {
2576 JS_ReportOutOfMemory(cx);
2577 return NULL;
2578 }
2579 }
2580 str = NULL;
2581
2582 switch (xml->xml_class) {
2583 case JSXML_CLASS_TEXT:
2584 /* Step 4. */
2585 if (pretty) {
2586 str = ChompXMLWhitespace(cx, xml->xml_value);
2587 if (!str)
2588 return NULL;
2589 } else {
2590 str = xml->xml_value;
2591 }
2592 return EscapeElementValue(cx, &sb, str);
2593
2594 case JSXML_CLASS_ATTRIBUTE:
2595 /* Step 5. */
2596 return EscapeAttributeValue(cx, &sb, xml->xml_value,
2597 (indentLevel & TO_SOURCE_FLAG) != 0);
2598
2599 case JSXML_CLASS_COMMENT:
2600 /* Step 6. */
2601 return MakeXMLCommentString(cx, &sb, xml->xml_value);
2602
2603 case JSXML_CLASS_PROCESSING_INSTRUCTION:
2604 /* Step 7. */
2605 return MakeXMLPIString(cx, &sb, GetLocalName(xml->name),
2606 xml->xml_value);
2607
2608 case JSXML_CLASS_LIST:
2609 /* ECMA-357 10.2.2. */
2610 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2611 i = 0;
2612 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2613 if (pretty && i != 0)
2614 js_AppendChar(&sb, '\n');
2615
2616 kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2617 if (!kidstr)
2618 break;
2619
2620 js_AppendJSString(&sb, kidstr);
2621 ++i;
2622 }
2623 XMLArrayCursorFinish(&cursor);
2624 if (kid)
2625 goto list_out;
2626
2627 if (!sb.base)
2628 return cx->runtime->emptyString;
2629
2630 if (!STRING_BUFFER_OK(&sb)) {
2631 JS_ReportOutOfMemory(cx);
2632 return NULL;
2633 }
2634
2635 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2636 list_out:
2637 if (!str && STRING_BUFFER_OK(&sb))
2638 js_FinishStringBuffer(&sb);
2639 return str;
2640
2641 default:;
2642 }
2643
2644 /* After this point, control must flow through label out: to exit. */
2645 if (!js_EnterLocalRootScope(cx))
2646 return NULL;
2647
2648 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2649 if (!ancestorNSes) {
2650 XMLArrayInit(cx, &empty, 0);
2651 ancestorNSes = &empty;
2652 }
2653 XMLArrayInit(cx, &decls, 0);
2654 ancdecls.capacity = 0;
2655
2656 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2657 XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
2658 while ((ns = (JSObject *) XMLArrayCursorNext(&cursor)) != NULL) {
2659 if (!IsDeclared(ns))
2660 continue;
2661 if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2662 /* NOTE: may want to exclude unused namespaces here. */
2663 ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_TRUE);
2664 if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2))
2665 break;
2666 }
2667 }
2668 XMLArrayCursorFinish(&cursor);
2669 if (ns)
2670 goto out;
2671
2672 /*
2673 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2674 * not own its member references. In the spec, ancdecls has no name, but
2675 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2676 */
2677 if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
2678 goto out;
2679 for (i = 0, n = ancestorNSes->length; i < n; i++) {
2680 ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2681 if (!ns2)
2682 continue;
2683 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
2684 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2685 goto out;
2686 }
2687 for (i = 0, n = decls.length; i < n; i++) {
2688 ns2 = XMLARRAY_MEMBER(&decls, i, JSObject);
2689 if (!ns2)
2690 continue;
2691 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
2692 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2693 goto out;
2694 }
2695
2696 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2697 ns = GetNamespace(cx, xml->name, &ancdecls);
2698 if (!ns)
2699 goto out;
2700
2701 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2702 prefix = GetPrefix(ns);
2703 if (!prefix) {
2704 /*
2705 * Create a namespace prefix that isn't used by any member of decls.
2706 * Assign the new prefix to a copy of ns. Flag this namespace as if
2707 * it were declared, for assertion-testing's sake later below.
2708 *
2709 * Erratum: if prefix and xml->name are both null (*undefined* in
2710 * ECMA-357), we know that xml was named using the default namespace
2711 * (proof: see GetNamespace and the Namespace constructor called with
2712 * two arguments). So we ought not generate a new prefix here, when
2713 * we can declare ns as the default namespace for xml.
2714 *
2715 * This helps descendants inherit the namespace instead of redundantly
2716 * redeclaring it with generated prefixes in each descendant.
2717 */
2718 nsuri = GetURI(ns);
2719 if (!GetPrefix(xml->name)) {
2720 prefix = cx->runtime->emptyString;
2721 } else {
2722 prefix = GeneratePrefix(cx, nsuri, &ancdecls);
2723 if (!prefix)
2724 goto out;
2725 }
2726 ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2727 if (!ns)
2728 goto out;
2729
2730 /*
2731 * If the xml->name was unprefixed, we must remove any declared default
2732 * namespace from decls before appending ns. How can you get a default
2733 * namespace in decls that doesn't match the one from name? Apparently
2734 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2735 * to fix this is to update x's in-scope namespaces when setNamespace
2736 * is called, but that's not specified by ECMA-357.
2737 *
2738 * Likely Erratum here, depending on whether the lack of update to x's
2739 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2740 * erratum or not. Note that changing setNamespace to update the list
2741 * of in-scope namespaces will change x.namespaceDeclarations().
2742 */
2743 if (IS_EMPTY(prefix)) {
2744 i = XMLArrayFindMember(&decls, ns, namespace_match);
2745 if (i != XML_NOT_FOUND)
2746 XMLArrayDelete(cx, &decls, i, JS_TRUE);
2747 }
2748
2749 /*
2750 * In the spec, ancdecls has no name, but is always written out as
2751 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2752 * that union in ancdecls, any time we append a namespace strong
2753 * ref to decls, we must also append a weak ref to ancdecls. Order
2754 * matters here: code at label out: releases strong refs in decls.
2755 */
2756 if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
2757 !XMLARRAY_APPEND(cx, &decls, ns)) {
2758 goto out;
2759 }
2760 }
2761
2762 /* Format the element or point-tag into sb. */
2763 js_AppendChar(&sb, '<');
2764
2765 if (prefix && !IS_EMPTY(prefix)) {
2766 js_AppendJSString(&sb, prefix);
2767 js_AppendChar(&sb, ':');
2768 }
2769 js_AppendJSString(&sb, GetLocalName(xml->name));
2770
2771 /*
2772 * Step 16 makes a union to avoid writing two loops in step 17, to share
2773 * common attribute value appending spec-code. We prefer two loops for
2774 * faster code and less data overhead.
2775 */
2776
2777 /* Step 17(b): append attributes. */
2778 XMLArrayCursorInit(&cursor, &xml->xml_attrs);
2779 while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2780 js_AppendChar(&sb, ' ');
2781 ns2 = GetNamespace(cx, attr->name, &ancdecls);
2782 if (!ns2)
2783 break;
2784
2785 /* 17(b)(ii): NULL means *undefined* here. */
2786 prefix = GetPrefix(ns2);
2787 if (!prefix) {
2788 prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls);
2789 if (!prefix)
2790 break;
2791
2792 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2793 ns2 = NewXMLNamespace(cx, prefix, GetURI(ns2), JS_TRUE);
2794 if (!ns2)
2795 break;
2796
2797 /*
2798 * In the spec, ancdecls has no name, but is always written out as
2799 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2800 * that union in ancdecls, any time we append a namespace strong
2801 * ref to decls, we must also append a weak ref to ancdecls. Order
2802 * matters here: code at label out: releases strong refs in decls.
2803 */
2804 if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
2805 !XMLARRAY_APPEND(cx, &decls, ns2)) {
2806 break;
2807 }
2808 }
2809
2810 /* 17(b)(iii). */
2811 if (!IS_EMPTY(prefix)) {
2812 js_AppendJSString(&sb, prefix);
2813 js_AppendChar(&sb, ':');
2814 }
2815
2816 /* 17(b)(iv). */
2817 js_AppendJSString(&sb, GetLocalName(attr->name));
2818
2819 /* 17(d-g). */
2820 AppendAttributeValue(cx, &sb, attr->xml_value);
2821 }
2822 XMLArrayCursorFinish(&cursor);
2823 if (attr)
2824 goto out;
2825
2826 /* Step 17(c): append XML namespace declarations. */
2827 XMLArrayCursorInit(&cursor, &decls);
2828 while ((ns2 = (JSObject *) XMLArrayCursorNext(&cursor)) != NULL) {
2829 JS_ASSERT(IsDeclared(ns2));
2830
2831 js_AppendCString(&sb, " xmlns");
2832
2833 /* 17(c)(ii): NULL means *undefined* here. */
2834 prefix = GetPrefix(ns2);
2835 if (!prefix) {
2836 prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls);
2837 if (!prefix)
2838 break;
2839 ns2->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
2840 }
2841
2842 /* 17(c)(iii). */
2843 if (!IS_EMPTY(prefix)) {
2844 js_AppendChar(&sb, ':');
2845 js_AppendJSString(&sb, prefix);
2846 }
2847
2848 /* 17(d-g). */
2849 AppendAttributeValue(cx, &sb, GetURI(ns2));
2850 }
2851 XMLArrayCursorFinish(&cursor);
2852 if (ns2)
2853 goto out;
2854
2855 /* Step 18: handle point tags. */
2856 n = xml->xml_kids.length;
2857 if (n == 0) {
2858 js_AppendCString(&sb, "/>");
2859 } else {
2860 /* Steps 19 through 25: handle element content, and open the end-tag. */
2861 js_AppendChar(&sb, '>');
2862 indentKids = n > 1 ||
2863 (n == 1 &&
2864 (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2865 kid->xml_class != JSXML_CLASS_TEXT);
2866
2867 if (pretty && indentKids) {
2868 if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2869 goto out;
2870 nextIndentLevel = indentLevel + i;
2871 } else {
2872 nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2873 }
2874
2875 XMLArrayCursorInit(&cursor, &xml->xml_kids);
2876 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2877 if (pretty && indentKids)
2878 js_AppendChar(&sb, '\n');
2879
2880 kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
2881 if (!kidstr)
2882 break;
2883
2884 js_AppendJSString(&sb, kidstr);
2885 }
2886 XMLArrayCursorFinish(&cursor);
2887 if (kid)
2888 goto out;
2889
2890 if (pretty && indentKids) {
2891 js_AppendChar(&sb, '\n');
2892 js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2893 }
2894 js_AppendCString(&sb, "</");
2895
2896 /* Step 26. */
2897 prefix = GetPrefix(ns);
2898 if (prefix && !IS_EMPTY(prefix)) {
2899 js_AppendJSString(&sb, prefix);
2900 js_AppendChar(&sb, ':');
2901 }
2902
2903 /* Step 27. */
2904 js_AppendJSString(&sb, GetLocalName(xml->name));
2905 js_AppendChar(&sb, '>');
2906 }
2907
2908 if (!STRING_BUFFER_OK(&sb)) {
2909 JS_ReportOutOfMemory(cx);
2910 goto out;
2911 }
2912
2913 str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
2914 out:
2915 js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
2916 if (!str && STRING_BUFFER_OK(&sb))
2917 js_FinishStringBuffer(&sb);
2918 XMLArrayFinish(cx, &decls);
2919 if (ancdecls.capacity != 0)
2920 XMLArrayFinish(cx, &ancdecls);
2921 return str;
2922 }
2923
2924 /* ECMA-357 10.2 */
2925 static JSString *
2926 ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag)
2927 {
2928 JSObject *obj;
2929 JSString *str;
2930 JSXML *xml;
2931
2932 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2933 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2934 JSMSG_BAD_XML_CONVERSION,
2935 JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2936 return NULL;
2937 }
2938
2939 if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2940 return js_ValueToString(cx, v);
2941
2942 if (JSVAL_IS_STRING(v))
2943 return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
2944
2945 obj = JSVAL_TO_OBJECT(v);
2946 if (!OBJECT_IS_XML(cx, obj)) {
2947 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
2948 return NULL;
2949 str = js_ValueToString(cx, v);
2950 if (!str)
2951 return NULL;
2952 return EscapeElementValue(cx, NULL, str);
2953 }
2954
2955 /* Handle non-element cases in this switch, returning from each case. */
2956 xml = (JSXML *) JS_GetPrivate(cx, obj);
2957 return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
2958 }
2959
2960 static JSObject *
2961 ToAttributeName(JSContext *cx, jsval v)
2962 {
2963 JSString *name, *uri, *prefix;
2964 JSObject *obj;
2965 JSClass *clasp;
2966 JSObject *qn;
2967
2968 if (JSVAL_IS_STRING(v)) {
2969 name = JSVAL_TO_STRING(v);
2970 uri = prefix = cx->runtime->emptyString;
2971 } else {
2972 if (JSVAL_IS_PRIMITIVE(v)) {
2973 js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2974 JSDVG_IGNORE_STACK, v, NULL);
2975 return NULL;
2976 }
2977
2978 obj = JSVAL_TO_OBJECT(v);
2979 clasp = OBJ_GET_CLASS(cx, obj);
2980 if (clasp == &js_AttributeNameClass)
2981 return obj;
2982
2983 if (clasp == &js_QNameClass.base) {
2984 qn = obj;
2985 uri = GetURI(qn);
2986 prefix = GetPrefix(qn);
2987 name = GetLocalName(qn);
2988 } else {
2989 if (clasp == &js_AnyNameClass) {
2990 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
2991 } else {
2992 name = js_ValueToString(cx, v);
2993 if (!name)
2994 return NULL;
2995 }
2996 uri = prefix = cx->runtime->emptyString;
2997 }
2998 }
2999
3000 qn = NewXMLQName(cx, uri, prefix, name, &js_AttributeNameClass);
3001 if (!qn)
3002 return NULL;
3003 return qn;
3004 }
3005
3006 static void
3007 ReportBadXMLName(JSContext *cx, jsval id)
3008 {
3009 js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, id, NULL);
3010 }
3011
3012 static JSBool
3013 IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
3014 {
3015 JSAtom *atom;
3016 JSString *uri;
3017
3018 atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom;
3019 uri = GetURI(qn);
3020 if (uri && atom &&
3021 (uri == ATOM_TO_STRING(atom) ||
3022 js_EqualStrings(uri, ATOM_TO_STRING(atom)))) {
3023 return JS_ValueToId(cx, STRING_TO_JSVAL(GetLocalName(qn)), funidp);
3024 }
3025 *funidp = 0;
3026 return JS_TRUE;
3027 }
3028
3029 JSBool
3030 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
3031 {
3032 if (OBJ_GET_CLASS(cx, obj) == &js_QNameClass.base)
3033 return IsFunctionQName(cx, obj, funidp);
3034 *funidp = 0;
3035 return JS_TRUE;
3036 }
3037
3038 static JSObject *
3039 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
3040 {
3041 JSString *name;
3042 JSObject *obj;
3043 JSClass *clasp;
3044 uint32 index;
3045
3046 if (JSVAL_IS_STRING(v)) {
3047 name = JSVAL_TO_STRING(v);
3048 } else {
3049 if (JSVAL_IS_PRIMITIVE(v)) {
3050 ReportBadXMLName(cx, v);
3051 return NULL;
3052 }
3053
3054 obj = JSVAL_TO_OBJECT(v);
3055 clasp = OBJ_GET_CLASS(cx, obj);
3056 if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
3057 goto out;
3058 if (clasp == &js_AnyNameClass) {
3059 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3060 goto construct;
3061 }
3062 name = js_ValueToString(cx, v);
3063 if (!name)
3064 return NULL;
3065 }
3066
3067 /*
3068 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3069 *
3070 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3071 *
3072 * First, _P_ should be _s_, to refer to the given string.
3073 *
3074 * Second, why does ToXMLName applied to the string type throw TypeError
3075 * only for numeric literals without any leading or trailing whitespace?
3076 *
3077 * If the idea is to reject uint32 property names, then the check needs to
3078 * be stricter, to exclude hexadecimal and floating point literals.
3079 */
3080 if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
3081 goto bad;
3082
3083 if (*JSSTRING_CHARS(name) == '@') {
3084 name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1);
3085 if (!name)
3086 return NULL;
3087 *funidp = 0;
3088 return ToAttributeName(cx, STRING_TO_JSVAL(name));
3089 }
3090
3091 construct:
3092 v = STRING_TO_JSVAL(name);
3093 obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
3094 if (!obj)
3095 return NULL;
3096
3097 out:
3098 if (!IsFunctionQName(cx, obj, funidp))
3099 return NULL;
3100 return obj;
3101
3102 bad:
3103 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3104 JSMSG_BAD_XML_NAME,
3105 js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
3106 return NULL;
3107 }
3108
3109 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3110 static JSBool
3111 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
3112 {
3113 JSString *prefix, *prefix2;
3114 JSObject *match, *ns2;
3115 uint32 i, n, m;
3116
3117 if (xml->xml_class != JSXML_CLASS_ELEMENT)
3118 return JS_TRUE;
3119
3120 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
3121 prefix = GetPrefix(ns);
3122 if (!prefix) {
3123 match = NULL;
3124 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3125 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3126 if (ns2 && js_EqualStrings(GetURI(ns2), GetURI(ns))) {
3127 match = ns2;
3128 break;
3129 }
3130 }
3131 if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
3132 return JS_FALSE;
3133 } else {
3134 if (IS_EMPTY(prefix) && IS_EMPTY(GetURI(xml->name)))
3135 return JS_TRUE;
3136 match = NULL;
3137 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3138 m = XML_NOT_FOUND;
3139 #endif
3140 for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3141 ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3142 if (ns2 && (prefix2 = GetPrefix(ns2)) &&
3143 js_EqualStrings(prefix2, prefix)) {
3144 match = ns2;
3145 m = i;
3146 break;
3147 }
3148 }
3149 if (match && !js_EqualStrings(GetURI(match), GetURI(ns))) {
3150 ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
3151 JSObject);
3152 JS_ASSERT(ns2 == match);
3153 match->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
3154 if (!AddInScopeNamespace(cx, xml, match))
3155 return JS_FALSE;
3156 }
3157 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
3158 return JS_FALSE;
3159 }
3160
3161 /* OPTION: enforce that descendants have superset namespaces. */
3162 return JS_TRUE;
3163 }
3164
3165 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3166 static JSBool
3167 Append(JSContext *cx, JSXML *list, JSXML *xml)
3168 {
3169 uint32 i, j, k, n;
3170 JSXML *kid;
3171
3172 JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
3173 i = list->xml_kids.length;
3174 n = 1;
3175 if (xml->xml_class == JSXML_CLASS_LIST) {
3176 list->xml_target = xml->xml_target;
3177 list->xml_targetprop = xml->xml_targetprop;
3178 n = JSXML_LENGTH(xml);
3179 k = i + n;
3180 if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
3181 return JS_FALSE;
3182 for (j = 0; j < n; j++) {
3183 kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
3184 if (kid)
3185 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3186 }
3187 return JS_TRUE;
3188 }
3189
3190 list->xml_target = xml->parent;
3191 if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3192 list->xml_targetprop = NULL;
3193 else
3194 list->xml_targetprop = xml->name;
3195 if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3196 return JS_FALSE;
3197 return JS_TRUE;
3198 }
3199
3200 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3201 static JSXML *
3202 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3203
3204 static JSXML *
3205 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3206 {
3207 JSXML *copy;
3208 JSBool ok;
3209
3210 /* Our caller may not be protecting newborns with a local root scope. */
3211 if (!js_EnterLocalRootScope(cx))
3212 return NULL;
3213 copy = DeepCopyInLRS(cx, xml, flags);
3214 if (copy) {
3215 if (obj) {
3216 /* Caller provided the object for this copy, hook 'em up. */
3217 ok = JS_SetPrivate(cx, obj, copy);
3218 if (ok)
3219 copy->object = obj;
3220 } else {
3221 ok = js_GetXMLObject(cx, copy) != NULL;
3222 }
3223 if (!ok)
3224 copy = NULL;
3225 }
3226 js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
3227 return copy;
3228 }
3229
3230 /*
3231 * (i) We must be in a local root scope (InLRS).
3232 * (ii) parent must have a rooted object.
3233 * (iii) from's owning object must be locked if not thread-local.
3234 */
3235 static JSBool
3236 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3237 uintN flags)
3238 {
3239 uint32 j, n;
3240 JSXMLArrayCursor cursor;
3241 JSBool ok;
3242 JSXML *kid, *kid2;
3243 JSString *str;
3244
3245 JS_ASSERT(cx->localRootStack);
3246
3247 n = from->length;
3248 if (!XMLArraySetCapacity(cx, to, n))
3249 return JS_FALSE;
3250
3251 XMLArrayCursorInit(&cursor, from);
3252 j = 0;
3253 ok = JS_TRUE;
3254 while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3255 if ((flags & XSF_IGNORE_COMMENTS) &&