/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsxml.cpp
C++ | 2138 lines | 1652 code | 255 blank | 231 comment | 438 complexity | 1bad5945bfae6a690cea885d2ba4c3d7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
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
91static 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 */
108const char js_isXMLName_str[] = "isXMLName";
109const char js_XMLList_str[] = "XMLList";
110const char js_localName_str[] = "localName";
111const char js_xml_parent_str[] = "parent";
112const char js_prefix_str[] = "prefix";
113const char js_toXMLString_str[] = "toXMLString";
114const char js_uri_str[] = "uri";
115
116const char js_amp_entity_str[] = "&";
117const char js_gt_entity_str[] = ">";
118const char js_lt_entity_str[] = "<";
119const char js_quot_entity_str[] = """;
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. */
124const uint32 JSSLOT_PREFIX = JSSLOT_PRIVATE;
125const uint32 JSSLOT_URI = JSSLOT_PRIVATE + 1;
126
127/* Namespace-specific slot. */
128const uint32 JSSLOT_DECLARED = JSSLOT_PRIVATE + 2;
129
130/* QName-specific slot. */
131const uint32 JSSLOT_LOCAL_NAME = JSSLOT_PRIVATE + 2;
132
133const uint32 NAMESPACE_RESERVED_SLOTS = 3;
134const uint32 QNAME_RESERVED_SLOTS = 3;
135
136static JSBool
137IsQNameClass(JSClass *clasp)
138{
139 return clasp == &js_QNameClass.base ||
140 clasp == &js_AttributeNameClass ||
141 clasp == &js_AnyNameClass;
142}
143
144static JSString *
145GetSlotString(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
164static JS_INLINE JSString *
165GetPrefix(const JSObject *obj)
166{
167 return GetSlotString(obj, JSSLOT_PREFIX);
168}
169
170static JSString *
171GetURI(const JSObject *obj)
172{
173 return GetSlotString(obj, JSSLOT_URI);
174}
175
176static JSString *
177GetLocalName(const JSObject *obj)
178{
179 return GetSlotString(obj, JSSLOT_LOCAL_NAME);
180}
181
182static JSBool
183IsDeclared(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
193static JSBool
194xml_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 */
204enum namespace_tinyid {
205 NAMESPACE_PREFIX = -1,
206 NAMESPACE_URI = -2
207};
208
209static JSBool
210namespace_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
229static void
230namespace_finalize(JSContext *cx, JSObject *obj)
231{
232 if (cx->runtime->functionNamespaceObject == obj)
233 cx->runtime->functionNamespaceObject = NULL;
234}
235
236static JSBool
237namespace_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
249JS_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
265static 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
271static JSBool
272namespace_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
283static JSFunctionSpec namespace_methods[] = {
284 JS_FN(js_toString_str, namespace_toString, 0,0),
285 JS_FS_END
286};
287
288static JSObject *
289NewXMLNamespace(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 */
312enum qname_tinyid {
313 QNAME_URI = -1,
314 QNAME_LOCALNAME = -2
315};
316
317static JSBool
318qname_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
339static void
340anyname_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
347static JSBool
348qname_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
360static JSBool
361qname_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
373JS_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 */
392JS_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
403JS_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
417static 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
423static JSBool
424qname_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
478static JSFunctionSpec qname_methods[] = {
479 JS_FN(js_toString_str, qname_toString, 0,0),
480 JS_FS_END
481};
482
483
484static void
485InitXMLQName(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
499static JSObject *
500NewXMLQName(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
514JSObject *
515js_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
534static JSBool
535IsXMLName(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
552JSBool
553js_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 */
584static JSBool
585NamespaceHelper(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
679static JSBool
680Namespace(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 */
691static JSBool
692QNameHelper(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
809out:
810 InitXMLQName(obj, uri, prefix, name);
811 return JS_TRUE;
812}
813
814static JSBool
815QName(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
821static JSBool
822AttributeName(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 */
832static JSBool
833namespace_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
850static JSBool
851attr_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
859static void
860XMLArrayCursorInit(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
874static void
875XMLArrayCursorFinish(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
888static void *
889XMLArrayCursorNext(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
899static void *
900XMLArrayCursorItem(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
910static void
911XMLArrayCursorTrace(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. */
926static JSBool
927XMLArraySetCapacity(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
953static void
954XMLArrayTrim(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
962static JSBool
963XMLArrayInit(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
971static void
972XMLArrayFinish(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
988static uint32
989XMLArrayFindMember(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
1018static JSBool
1019XMLArrayAddMember(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
1056static JSBool
1057XMLArrayInsert(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
1081static void *
1082XMLArrayDelete(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
1110static void
1111XMLArrayTruncate(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 */
1156static const char js_ignoreComments_str[] = "ignoreComments";
1157static const char js_ignoreProcessingInstructions_str[]
1158 = "ignoreProcessingInstructions";
1159static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1160static const char js_prettyPrinting_str[] = "prettyPrinting";
1161static 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 */
1169enum 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
1177static JSBool
1178xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1179{
1180 return JS_TRUE;
1181}
1182
1183static JSBool
1184xml_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
1198static 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
1258static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1259static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1260
1261static JSObject *
1262ParseNodeToQName(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
1355static JSString *
1356ChompXMLWhitespace(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
1381static JSXML *
1382ParseNodeToXML(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
1728skip_child:
1729 js_LeaveLocalRootScope(cx);
1730 return PN2X_SKIP_CHILD;
1731
1732#undef PN2X_SKIP_CHILD
1733
1734syntax:
1735 js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1736 JSMSG_BAD_XML_MARKUP);
1737fail:
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 */
1746static JSBool
1747GetXMLSetting(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
1760static JSBool
1761FillSettingsCache(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
1782static JSBool
1783GetBooleanXMLSetting(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
1800static JSBool
1801GetUint32XMLSetting(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
1808static JSBool
1809GetXMLSettingFlags(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
1820static JSXML *
1821ParseXMLSource(JSContext *cx, JSString *src)
1822{
1823 jsval nsval;
1824 JSString *uri;
1825 size_t urilen, srclen, length, offset, dstlen;
1826 jschar *chars;
1827 const jschar *srcp, *endp;
1828 JSXML *xml;
1829 JSParseContext pc;
1830 const char *filename;
1831 uintN lineno;
1832 JSStackFrame *fp;
1833 JSOp op;
1834 JSParseNode *pn;
1835 JSXMLArray nsarray;
1836 uintN flags;
1837
1838 static const char prefix[] = "<parent xmlns=\"";
1839 static const char middle[] = "\">";
1840 static const char suffix[] = "</parent>";
1841
1842#define constrlen(constr) (sizeof(constr) - 1)
1843
1844 if (!js_GetDefaultXMLNamespace(cx, &nsval))
1845 return NULL;
1846 uri = GetURI(JSVAL_TO_OBJECT(nsval));
1847 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1848
1849 urilen = JSSTRING_LENGTH(uri);
1850 srclen = JSSTRING_LENGTH(src);
1851 length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1852 constrlen(suffix);
1853
1854 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1855 if (!chars)
1856 return NULL;
1857
1858 dstlen = length;
1859 js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1860 offset = dstlen;
1861 js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
1862 offset += urilen;
1863 dstlen = length - offset + 1;
1864 js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1865 &dstlen);
1866 offset += dstlen;
1867 srcp = JSSTRING_CHARS(src);
1868 js_strncpy(chars + offset, srcp, srclen);
1869 offset += srclen;
1870 dstlen = length - offset + 1;
1871 js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1872 &dstlen);
1873 chars [offset + dstlen] = 0;
1874
1875 xml = NULL;
1876 for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
1877 JS_ASSERT(!fp->script);
1878 filename = NULL;
1879 lineno = 1;
1880 if (fp) {
1881 op = (JSOp) *fp->regs->pc;
1882 if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1883 filename = fp->script->filename;
1884 lineno = js_FramePCToLineNumber(cx, fp);
1885 for (endp = srcp + srclen; srcp < endp; srcp++) {
1886 if (*srcp == '\n')
1887 --lineno;
1888 }
1889 }
1890 }
1891
1892 if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
1893 filename, lineno))
1894 goto out;
1895 pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
1896 if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1897 if (GetXMLSettingFlags(cx, &flags))
1898 xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
1899
1900 XMLArrayFinish(cx, &nsarray);
1901 }
1902 js_FinishParseContext(cx, &pc);
1903
1904out:
1905 JS_free(cx, chars);
1906 return xml;
1907
1908#undef constrlen
1909}
1910
1911/*
1912 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1913 *
1914 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1915 * the constraint:
1916 *
1917 * for all x belonging to XML:
1918 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1919 *
1920 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1921 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1922 *
1923 * Same goes for 10.4.1 Step 7(a).
1924 *
1925 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1926 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1927 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1928 * undeclared namespaces associated with x not belonging to ancestorNS.
1929 */
1930static JSXML *
1931OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1932{
1933 JSObject *ns;
1934
1935 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1936 xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1937 if (!ns || !xml)
1938 return xml;
1939 if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1940 if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1941 return NULL;
1942 ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1943 }
1944 xml->parent = NULL;
1945 return xml;
1946}
1947
1948static JSObject *
1949ToXML(JSContext *cx, jsval v)
1950{
1951 JSObject *obj;
1952 JSXML *xml;
1953 JSClass *clasp;
1954 JSString *str;
1955 uint32 length;
1956
1957 if (JSVAL_IS_PRIMITIVE(v)) {
1958 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1959 goto bad;
1960 } else {
1961 obj = JSVAL_TO_OBJECT(v);
1962 if (OBJECT_IS_XML(cx, obj)) {
1963 xml = (JSXML *) JS_GetPrivate(cx, obj);
1964 if (xml->xml_class == JSXML_CLASS_LIST) {
1965 if (xml->xml_kids.length != 1)
1966 goto bad;
1967 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1968 if (xml) {
1969 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1970 return js_GetXMLObject(cx, xml);
1971 }
1972 }
1973 return obj;
1974 }
1975
1976 clasp = OBJ_GET_CLASS(cx, obj);
1977 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1978 JS_ASSERT(0);
1979 }
1980
1981 if (clasp != &js_StringClass &&
1982 clasp != &js_NumberClass &&
1983 clasp != &js_BooleanClass) {
1984 goto bad;
1985 }
1986 }
1987
1988 str = js_ValueToString(cx, v);
1989 if (!str)
1990 return NULL;
1991 if (IS_EMPTY(str)) {
1992 length = 0;
1993#ifdef __GNUC__ /* suppress bogus gcc warnings */
1994 xml = NULL;
1995#endif
1996 } else {
1997 xml = ParseXMLSource(cx, str);
1998 if (!xml)
1999 return NULL;
2000 length = JSXML_LENGTH(xml);
2001 }
2002
2003 if (length == 0) {
2004 obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2005 if (!obj)
2006 return NULL;
2007 } else if (length == 1) {
2008 xml = OrphanXMLChild(cx, xml, 0);
2009 if (!xml)
2010 return NULL;
2011 obj = js_GetXMLObject(cx, xml);
2012 if (!obj)
2013 return NULL;
2014 } else {
2015 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2016 return NULL;
2017 }
2018 return obj;
2019
2020bad:
2021 js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2022 JSDVG_IGNORE_STACK, v, NULL);
2023 return NULL;
2024}
2025
2026static JSBool
2027Append(JSContext *cx, JSXML *list, JSXML *kid);
2028
2029static JSObject *
2030ToXMLList(JSContext *cx, jsval v)
2031{
2032 JSObject *obj, *listobj;
2033 JSXML *xml, *list, *kid;
2034 JSClass *clasp;
2035 JSString *str;
2036 uint32 i, length;
2037
2038 if (JSVAL_IS_PRIMITIVE(v)) {
2039 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2040 goto bad;
2041 } else {
2042 obj = JSVAL_TO_OBJECT(v);
2043 if (OBJECT_IS_XML(cx, obj)) {
2044 xml = (JSXML *) JS_GetPrivate(cx, obj);
2045 if (xml->xml_class != JSXML_CLASS_LIST) {
2046 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2047 if (!listobj)
2048 return NULL;
2049 list = (JSXML *) JS_GetPrivate(cx, listobj);
2050 if (!Append(cx, list, xml))
2051 return NULL;
2052 return listobj;
2053 }
2054 return obj;
2055 }
2056
2057 clasp = OBJ_GET_CLASS(cx, obj);
2058 if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2059 JS_ASSERT(0);
2060 }
2061
2062 if (clasp != &js_StringClass &&
2063 clasp != &js_NumberClass &&
2064 clasp != &js_BooleanClass) {
2065 goto bad;
2066 }
2067 }
2068
2069 str = js_ValueToString(cx, v);
2070 if (!str)
2071 return NULL;
2072 if (IS_EMPTY(str)) {
2073 xml = NULL;
2074 length = 0;
2075 } else {
2076 if (!js_EnterLocalRootScope(cx))
2077 return NULL;
2078 xml = ParseXMLSource(cx, str);
2079 if (!xml) {
2080 js_LeaveLocalRootScope(cx);
2081 return NULL;
2082 }
2083 length = JSXML_LENGTH(xml);
2084 }
2085
2086 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2087 if (listobj) {
2088 list = (JSXML *) JS_GetPrivate(cx, listobj);
2089 for (i = 0; i < length; i++) {
2090 kid = OrphanXMLChild(cx, xml, i);
2091 if (!kid || !Append(cx, list, kid)) {
2092 listobj = NULL;
2093 break;
2094 }
2095 }
2096 }
2097
2098 if (xml)
2099 js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2100 return listobj;
2101
2102bad:
2103 js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2104 JSDVG_IGNORE_STACK, v, NULL);
2105 return NULL;
2106}
2107
2108/*
2109 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2110 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2111 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2112 * MakeXMLSpecialString subroutine.
2113 *
2114 * These functions take ownership of sb->base, if sb is non-null, in all cases
2115 * of success or failure.
2116 */
2117static JSString *
2118MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2119 JSString *str, JSString *str2,
2120 const jschar *prefix, size_t prefixlength,
2121 const jschar *suffix, size_t suffixlength)
2122{
2123 JSStringBuffer localSB;
2124 size_t length, length2, newlength;
2125 jschar *bp, *base;
2126
2127 if (!sb) {
2128 sb = &localSB;
2129 js_InitStringBuffer(sb);
2130 }
2131
2132 length = JSSTRING_LENGTH(str);
2133 length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2134 newlength = STRING_BUFFER_OFFSET(sb) +
2135 prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2136 suffixlength;
2137 bp = base = (jschar *)
2138 JS_r