~ubuntu-branches/debian/wheezy/couchdb/wheezy

« back to all changes in this revision

Viewing changes to src/js/jsxml.c

  • Committer: Bazaar Package Importer
  • Author(s): Noah Slater
  • Date: 2008-02-06 17:03:38 UTC
  • Revision ID: james.westby@ubuntu.com-20080206170338-y411anylx3oplqid
Tags: upstream-0.7.3~svn684
ImportĀ upstreamĀ versionĀ 0.7.3~svn684

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "jsconfig.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
 
 
71
#ifdef DEBUG
 
72
#include <string.h>     /* for #ifdef DEBUG memset calls */
 
73
#endif
 
74
 
 
75
/*
 
76
 * NOTES
 
77
 * - in the js shell, you must use the -x command line option, or call
 
78
 *   options('xml') before compiling anything that uses XML literals
 
79
 *
 
80
 * TODO
 
81
 * - XXXbe patrol
 
82
 * - Fuse objects and their JSXML* private data into single GC-things
 
83
 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
 
84
 * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
 
85
 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
 
86
 * - JS_TypeOfValue sure could use a cleaner interface to "types"
 
87
 */
 
88
 
 
89
#ifdef DEBUG_brendan
 
90
#define METERING        1
 
91
#endif
 
92
 
 
93
#ifdef METERING
 
94
static struct {
 
95
    jsrefcount  qname;
 
96
    jsrefcount  qnameobj;
 
97
    jsrefcount  liveqname;
 
98
    jsrefcount  liveqnameobj;
 
99
    jsrefcount  namespace;
 
100
    jsrefcount  namespaceobj;
 
101
    jsrefcount  livenamespace;
 
102
    jsrefcount  livenamespaceobj;
 
103
    jsrefcount  xml;
 
104
    jsrefcount  xmlobj;
 
105
    jsrefcount  livexml;
 
106
    jsrefcount  livexmlobj;
 
107
} xml_stats;
 
108
 
 
109
#define METER(x)        JS_ATOMIC_INCREMENT(&(x))
 
110
#define UNMETER(x)      JS_ATOMIC_DECREMENT(&(x))
 
111
#else
 
112
#define METER(x)        /* nothing */
 
113
#define UNMETER(x)      /* nothing */
 
114
#endif
 
115
 
 
116
/*
 
117
 * Random utilities and global functions.
 
118
 */
 
119
const char js_isXMLName_str[]     = "isXMLName";
 
120
const char js_XMLList_str[]       = "XMLList";
 
121
const char js_localName_str[]     = "localName";
 
122
const char js_xml_parent_str[]    = "parent";
 
123
const char js_prefix_str[]        = "prefix";
 
124
const char js_toXMLString_str[]   = "toXMLString";
 
125
const char js_uri_str[]           = "uri";
 
126
 
 
127
const char js_amp_entity_str[]    = "&amp;";
 
128
const char js_gt_entity_str[]     = "&gt;";
 
129
const char js_lt_entity_str[]     = "&lt;";
 
130
const char js_quot_entity_str[]   = "&quot;";
 
131
 
 
132
#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
 
133
#define IS_STAR(str)  (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
 
134
 
 
135
static JSBool
 
136
xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
137
              jsval *rval)
 
138
{
 
139
    *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
 
140
    return JS_TRUE;
 
141
}
 
142
 
 
143
/*
 
144
 * Namespace class and library functions.
 
145
 */
 
146
enum namespace_tinyid {
 
147
    NAMESPACE_PREFIX = -1,
 
148
    NAMESPACE_URI = -2
 
149
};
 
150
 
 
151
static JSBool
 
152
namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
153
{
 
154
    JSXMLNamespace *ns;
 
155
 
 
156
    if (!JSVAL_IS_INT(id))
 
157
        return JS_TRUE;
 
158
 
 
159
    ns = (JSXMLNamespace *)
 
160
         JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL);
 
161
    if (!ns)
 
162
        return JS_TRUE;
 
163
 
 
164
    switch (JSVAL_TO_INT(id)) {
 
165
      case NAMESPACE_PREFIX:
 
166
        *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID;
 
167
        break;
 
168
      case NAMESPACE_URI:
 
169
        *vp = STRING_TO_JSVAL(ns->uri);
 
170
        break;
 
171
    }
 
172
    return JS_TRUE;
 
173
}
 
174
 
 
175
static void
 
176
namespace_finalize(JSContext *cx, JSObject *obj)
 
177
{
 
178
    JSXMLNamespace *ns;
 
179
    JSRuntime *rt;
 
180
 
 
181
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
 
182
    if (!ns)
 
183
        return;
 
184
    JS_ASSERT(ns->object == obj);
 
185
    ns->object = NULL;
 
186
    UNMETER(xml_stats.livenamespaceobj);
 
187
 
 
188
    rt = cx->runtime;
 
189
    if (rt->functionNamespaceObject == obj)
 
190
        rt->functionNamespaceObject = NULL;
 
191
}
 
192
 
 
193
static void
 
194
namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len)
 
195
{
 
196
    uint32 i;
 
197
    JSXMLNamespace *ns;
 
198
 
 
199
    for (i = 0; i < len; i++) {
 
200
        ns = vec[i];
 
201
        {
 
202
#ifdef GC_MARK_DEBUG
 
203
            char buf[100];
 
204
 
 
205
            JS_snprintf(buf, sizeof buf, "%s=%s",
 
206
                        ns->prefix ? JS_GetStringBytes(ns->prefix) : "",
 
207
                        JS_GetStringBytes(ns->uri));
 
208
#endif
 
209
            GC_MARK(cx, ns, buf);
 
210
        }
 
211
    }
 
212
}
 
213
 
 
214
static uint32
 
215
namespace_mark(JSContext *cx, JSObject *obj, void *arg)
 
216
{
 
217
    JSXMLNamespace *ns;
 
218
 
 
219
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
 
220
    GC_MARK(cx, ns, "private");
 
221
    return 0;
 
222
}
 
223
 
 
224
static JSBool
 
225
namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 
226
{
 
227
    JSXMLNamespace *ns, *ns2;
 
228
    JSObject *obj2;
 
229
 
 
230
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
 
231
    JS_ASSERT(JSVAL_IS_OBJECT(v));
 
232
    obj2 = JSVAL_TO_OBJECT(v);
 
233
    if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) {
 
234
        *bp = JS_FALSE;
 
235
    } else {
 
236
        ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2);
 
237
        *bp = js_EqualStrings(ns->uri, ns2->uri);
 
238
    }
 
239
    return JS_TRUE;
 
240
}
 
241
 
 
242
JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
 
243
  { "Namespace",
 
244
    JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
 
245
    JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
 
246
    JS_PropertyStub,   JS_PropertyStub,   namespace_getProperty, NULL,
 
247
    JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    namespace_finalize,
 
248
    NULL,              NULL,              NULL,              NULL,
 
249
    NULL,              NULL,              namespace_mark,    NULL },
 
250
    namespace_equality,NULL,              NULL,              NULL,
 
251
    NULL,              NULL,              NULL,              NULL
 
252
};
 
253
 
 
254
#define NAMESPACE_ATTRS                                                       \
 
255
    (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
 
256
 
 
257
static JSPropertySpec namespace_props[] = {
 
258
    {js_prefix_str,    NAMESPACE_PREFIX,  NAMESPACE_ATTRS,   0, 0},
 
259
    {js_uri_str,       NAMESPACE_URI,     NAMESPACE_ATTRS,   0, 0},
 
260
    {0,0,0,0,0}
 
261
};
 
262
 
 
263
static JSBool
 
264
namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
265
                   jsval *rval)
 
266
{
 
267
    JSXMLNamespace *ns;
 
268
 
 
269
    ns = (JSXMLNamespace *)
 
270
         JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv);
 
271
    if (!ns)
 
272
        return JS_FALSE;
 
273
 
 
274
    *rval = STRING_TO_JSVAL(ns->uri);
 
275
    return JS_TRUE;
 
276
}
 
277
 
 
278
static JSFunctionSpec namespace_methods[] = {
 
279
    {js_toString_str,  namespace_toString,        0,0,0},
 
280
    {0,0,0,0,0}
 
281
};
 
282
 
 
283
JSXMLNamespace *
 
284
js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri,
 
285
                   JSBool declared)
 
286
{
 
287
    JSXMLNamespace *ns;
 
288
 
 
289
    ns = (JSXMLNamespace *)
 
290
         js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace));
 
291
    if (!ns)
 
292
        return NULL;
 
293
    ns->object = NULL;
 
294
    ns->prefix = prefix;
 
295
    ns->uri = uri;
 
296
    ns->declared = declared;
 
297
    METER(xml_stats.namespace);
 
298
    METER(xml_stats.livenamespace);
 
299
    return ns;
 
300
}
 
301
 
 
302
void
 
303
js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
 
304
{
 
305
    GC_MARK(cx, ns->object, "object");
 
306
    GC_MARK(cx, ns->prefix, "prefix");
 
307
    GC_MARK(cx, ns->uri, "uri");
 
308
}
 
309
 
 
310
void
 
311
js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
 
312
{
 
313
    UNMETER(xml_stats.livenamespace);
 
314
}
 
315
 
 
316
JSObject *
 
317
js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri,
 
318
                         JSBool declared)
 
319
{
 
320
    JSXMLNamespace *ns;
 
321
 
 
322
    ns = js_NewXMLNamespace(cx, prefix, uri, declared);
 
323
    if (!ns)
 
324
        return NULL;
 
325
    return js_GetXMLNamespaceObject(cx, ns);
 
326
}
 
327
 
 
328
JSObject *
 
329
js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns)
 
330
{
 
331
    JSObject *obj;
 
332
 
 
333
    obj = ns->object;
 
334
    if (obj) {
 
335
        JS_ASSERT(JS_GetPrivate(cx, obj) == ns);
 
336
        return obj;
 
337
    }
 
338
    obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
 
339
    if (!obj || !JS_SetPrivate(cx, obj, ns)) {
 
340
        cx->weakRoots.newborn[GCX_OBJECT] = NULL;
 
341
        return NULL;
 
342
    }
 
343
    ns->object = obj;
 
344
    METER(xml_stats.namespaceobj);
 
345
    METER(xml_stats.livenamespaceobj);
 
346
    return obj;
 
347
}
 
348
 
 
349
/*
 
350
 * QName class and library functions.
 
351
 */
 
352
enum qname_tinyid {
 
353
    QNAME_URI = -1,
 
354
    QNAME_LOCALNAME = -2
 
355
};
 
356
 
 
357
static JSBool
 
358
qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
359
{
 
360
    JSXMLQName *qn;
 
361
 
 
362
    if (!JSVAL_IS_INT(id))
 
363
        return JS_TRUE;
 
364
 
 
365
    qn = (JSXMLQName *)
 
366
         JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL);
 
367
    if (!qn)
 
368
        return JS_TRUE;
 
369
 
 
370
    switch (JSVAL_TO_INT(id)) {
 
371
      case QNAME_URI:
 
372
        *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL;
 
373
        break;
 
374
      case QNAME_LOCALNAME:
 
375
        *vp = STRING_TO_JSVAL(qn->localName);
 
376
        break;
 
377
    }
 
378
    return JS_TRUE;
 
379
}
 
380
 
 
381
static void
 
382
qname_finalize(JSContext *cx, JSObject *obj)
 
383
{
 
384
    JSXMLQName *qn;
 
385
 
 
386
    qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
 
387
    if (!qn)
 
388
        return;
 
389
    JS_ASSERT(qn->object == obj);
 
390
    qn->object = NULL;
 
391
    UNMETER(xml_stats.liveqnameobj);
 
392
}
 
393
 
 
394
static void
 
395
anyname_finalize(JSContext* cx, JSObject* obj)
 
396
{
 
397
    JSRuntime *rt;
 
398
 
 
399
    /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
 
400
    rt = cx->runtime;
 
401
    if (rt->anynameObject == obj)
 
402
        rt->anynameObject = NULL;
 
403
 
 
404
    qname_finalize(cx, obj);
 
405
}
 
406
 
 
407
static uint32
 
408
qname_mark(JSContext *cx, JSObject *obj, void *arg)
 
409
{
 
410
    JSXMLQName *qn;
 
411
 
 
412
    qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
 
413
    GC_MARK(cx, qn, "private");
 
414
    return 0;
 
415
}
 
416
 
 
417
static JSBool
 
418
qname_identity(JSXMLQName *qna, JSXMLQName *qnb)
 
419
{
 
420
    if (!qna->uri ^ !qnb->uri)
 
421
        return JS_FALSE;
 
422
    if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri))
 
423
        return JS_FALSE;
 
424
    return js_EqualStrings(qna->localName, qnb->localName);
 
425
}
 
426
 
 
427
static JSBool
 
428
qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 
429
{
 
430
    JSXMLQName *qn, *qn2;
 
431
    JSObject *obj2;
 
432
 
 
433
    qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
 
434
    JS_ASSERT(JSVAL_IS_OBJECT(v));
 
435
    obj2 = JSVAL_TO_OBJECT(v);
 
436
    if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) {
 
437
        *bp = JS_FALSE;
 
438
    } else {
 
439
        qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2);
 
440
        *bp = qname_identity(qn, qn2);
 
441
    }
 
442
    return JS_TRUE;
 
443
}
 
444
 
 
445
JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
 
446
  { "QName",
 
447
    JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
 
448
    JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
 
449
    JS_PropertyStub,   JS_PropertyStub,   qname_getProperty, NULL,
 
450
    JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    qname_finalize,
 
451
    NULL,              NULL,              NULL,              NULL,
 
452
    NULL,              NULL,              qname_mark,        NULL },
 
453
    qname_equality,    NULL,              NULL,              NULL,
 
454
    NULL,              NULL,              NULL,              NULL
 
455
};
 
456
 
 
457
/*
 
458
 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
 
459
 * are like QName, except that they have no property getters.  They share the
 
460
 * qname_toString method, and therefore are exposed as constructable objects
 
461
 * in this implementation.
 
462
 */
 
463
JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
 
464
    js_AttributeName_str,
 
465
    JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE |
 
466
    JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
 
467
    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
 
468
    JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    qname_finalize,
 
469
    NULL,              NULL,              NULL,              NULL,
 
470
    NULL,              NULL,              qname_mark,        NULL
 
471
};
 
472
 
 
473
JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
 
474
    js_AnyName_str,
 
475
    JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE |
 
476
    JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
 
477
    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
 
478
    JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    anyname_finalize,
 
479
    NULL,              NULL,              NULL,              NULL,
 
480
    NULL,              NULL,              qname_mark,        NULL
 
481
};
 
482
 
 
483
#define QNAME_ATTRS                                                           \
 
484
    (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
 
485
 
 
486
static JSPropertySpec qname_props[] = {
 
487
    {js_uri_str,       QNAME_URI,         QNAME_ATTRS,       0, 0},
 
488
    {js_localName_str, QNAME_LOCALNAME,   QNAME_ATTRS,       0, 0},
 
489
    {0,0,0,0,0}
 
490
};
 
491
 
 
492
static JSBool
 
493
qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
494
               jsval *rval)
 
495
{
 
496
    JSClass *clasp;
 
497
    JSXMLQName *qn;
 
498
    JSString *str, *qualstr;
 
499
    size_t length;
 
500
    jschar *chars;
 
501
 
 
502
    clasp = OBJ_GET_CLASS(cx, obj);
 
503
    if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) {
 
504
        qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
 
505
    } else {
 
506
        qn = (JSXMLQName *)
 
507
             JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv);
 
508
        if (!qn)
 
509
            return JS_FALSE;
 
510
    }
 
511
 
 
512
    if (!qn->uri) {
 
513
        /* No uri means wildcard qualifier. */
 
514
        str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
 
515
    } else if (IS_EMPTY(qn->uri)) {
 
516
        /* Empty string for uri means localName is in no namespace. */
 
517
        str = cx->runtime->emptyString;
 
518
    } else {
 
519
        qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
 
520
        str = js_ConcatStrings(cx, qn->uri, qualstr);
 
521
        if (!str)
 
522
            return JS_FALSE;
 
523
    }
 
524
    str = js_ConcatStrings(cx, str, qn->localName);
 
525
    if (!str)
 
526
        return JS_FALSE;
 
527
 
 
528
    if (str && clasp == &js_AttributeNameClass) {
 
529
        length = JSSTRING_LENGTH(str);
 
530
        chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
 
531
        if (!chars)
 
532
            return JS_FALSE;
 
533
        *chars = '@';
 
534
        js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
 
535
        chars[++length] = 0;
 
536
        str = js_NewString(cx, chars, length, 0);
 
537
        if (!str) {
 
538
            JS_free(cx, chars);
 
539
            return JS_FALSE;
 
540
        }
 
541
    }
 
542
 
 
543
    *rval = STRING_TO_JSVAL(str);
 
544
    return JS_TRUE;
 
545
}
 
546
 
 
547
static JSFunctionSpec qname_methods[] = {
 
548
    {js_toString_str,  qname_toString,    0,0,0},
 
549
    {0,0,0,0,0}
 
550
};
 
551
 
 
552
JSXMLQName *
 
553
js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix,
 
554
               JSString *localName)
 
555
{
 
556
    JSXMLQName *qn;
 
557
 
 
558
    qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName));
 
559
    if (!qn)
 
560
        return NULL;
 
561
    qn->object = NULL;
 
562
    qn->uri = uri;
 
563
    qn->prefix = prefix;
 
564
    qn->localName = localName;
 
565
    METER(xml_stats.qname);
 
566
    METER(xml_stats.liveqname);
 
567
    return qn;
 
568
}
 
569
 
 
570
void
 
571
js_MarkXMLQName(JSContext *cx, JSXMLQName *qn)
 
572
{
 
573
    GC_MARK(cx, qn->object, "object");
 
574
    GC_MARK(cx, qn->uri, "uri");
 
575
    GC_MARK(cx, qn->prefix, "prefix");
 
576
    GC_MARK(cx, qn->localName, "localName");
 
577
}
 
578
 
 
579
void
 
580
js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn)
 
581
{
 
582
    UNMETER(xml_stats.liveqname);
 
583
}
 
584
 
 
585
JSObject *
 
586
js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix,
 
587
                     JSString *localName)
 
588
{
 
589
    JSXMLQName *qn;
 
590
 
 
591
    qn = js_NewXMLQName(cx, uri, prefix, localName);
 
592
    if (!qn)
 
593
        return NULL;
 
594
    return js_GetXMLQNameObject(cx, qn);
 
595
}
 
596
 
 
597
JSObject *
 
598
js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn)
 
599
{
 
600
    JSObject *obj;
 
601
 
 
602
    obj = qn->object;
 
603
    if (obj) {
 
604
        JS_ASSERT(JS_GetPrivate(cx, obj) == qn);
 
605
        return obj;
 
606
    }
 
607
    obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL);
 
608
    if (!obj || !JS_SetPrivate(cx, obj, qn)) {
 
609
        cx->weakRoots.newborn[GCX_OBJECT] = NULL;
 
610
        return NULL;
 
611
    }
 
612
    qn->object = obj;
 
613
    METER(xml_stats.qnameobj);
 
614
    METER(xml_stats.liveqnameobj);
 
615
    return obj;
 
616
}
 
617
 
 
618
JSObject *
 
619
js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn)
 
620
{
 
621
    JSObject *obj;
 
622
 
 
623
    obj = qn->object;
 
624
    if (obj) {
 
625
        if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass)
 
626
            return obj;
 
627
        qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
 
628
        if (!qn)
 
629
            return NULL;
 
630
    }
 
631
 
 
632
    obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL);
 
633
    if (!obj || !JS_SetPrivate(cx, obj, qn)) {
 
634
        cx->weakRoots.newborn[GCX_OBJECT] = NULL;
 
635
        return NULL;
 
636
    }
 
637
 
 
638
    qn->object = obj;
 
639
    METER(xml_stats.qnameobj);
 
640
    METER(xml_stats.liveqnameobj);
 
641
    return obj;
 
642
}
 
643
 
 
644
JSObject *
 
645
js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
 
646
{
 
647
    jsval argv[2];
 
648
 
 
649
    /*
 
650
     * ECMA-357 11.1.2,
 
651
     * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
 
652
     * production, step 2.
 
653
     */
 
654
    if (!JSVAL_IS_PRIMITIVE(nsval) &&
 
655
        OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
 
656
        nsval = JSVAL_NULL;
 
657
    }
 
658
 
 
659
    argv[0] = nsval;
 
660
    argv[1] = lnval;
 
661
    return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
 
662
}
 
663
 
 
664
static JSBool
 
665
IsXMLName(const jschar *cp, size_t n)
 
666
{
 
667
    JSBool rv;
 
668
    jschar c;
 
669
 
 
670
    rv = JS_FALSE;
 
671
    if (n != 0 && JS_ISXMLNSSTART(*cp)) {
 
672
        while (--n != 0) {
 
673
            c = *++cp;
 
674
            if (!JS_ISXMLNS(c))
 
675
                return rv;
 
676
        }
 
677
        rv = JS_TRUE;
 
678
    }
 
679
    return rv;
 
680
}
 
681
 
 
682
JSBool
 
683
js_IsXMLName(JSContext *cx, jsval v)
 
684
{
 
685
    JSClass *clasp;
 
686
    JSXMLQName *qn;
 
687
    JSString *name;
 
688
    JSErrorReporter older;
 
689
 
 
690
    /*
 
691
     * Inline specialization of the QName constructor called with v passed as
 
692
     * the only argument, to compute the localName for the constructed qname,
 
693
     * without actually allocating the object or computing its uri and prefix.
 
694
     * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
 
695
     */
 
696
    if (!JSVAL_IS_PRIMITIVE(v) &&
 
697
        (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)),
 
698
         clasp == &js_QNameClass.base ||
 
699
         clasp == &js_AttributeNameClass ||
 
700
         clasp == &js_AnyNameClass)) {
 
701
        qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
702
        name = qn->localName;
 
703
    } else {
 
704
        older = JS_SetErrorReporter(cx, NULL);
 
705
        name = js_ValueToString(cx, v);
 
706
        JS_SetErrorReporter(cx, older);
 
707
        if (!name) {
 
708
            JS_ClearPendingException(cx);
 
709
            return JS_FALSE;
 
710
        }
 
711
    }
 
712
 
 
713
    return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
 
714
}
 
715
 
 
716
static JSBool
 
717
Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
718
{
 
719
    jsval urival, prefixval;
 
720
    JSObject *uriobj;
 
721
    JSBool isNamespace, isQName;
 
722
    JSClass *clasp;
 
723
    JSString *empty, *prefix;
 
724
    JSXMLNamespace *ns, *ns2;
 
725
    JSXMLQName *qn;
 
726
 
 
727
    urival = argv[argc > 1];
 
728
    isNamespace = isQName = JS_FALSE;
 
729
    if (!JSVAL_IS_PRIMITIVE(urival)) {
 
730
        uriobj = JSVAL_TO_OBJECT(urival);
 
731
        clasp = OBJ_GET_CLASS(cx, uriobj);
 
732
        isNamespace = (clasp == &js_NamespaceClass.base);
 
733
        isQName = (clasp == &js_QNameClass.base);
 
734
    }
 
735
#ifdef __GNUC__         /* suppress bogus gcc warnings */
 
736
    else uriobj = NULL;
 
737
#endif
 
738
 
 
739
    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
 
740
        /* Namespace called as function. */
 
741
        if (argc == 1 && isNamespace) {
 
742
            /* Namespace called with one Namespace argument is identity. */
 
743
            *rval = urival;
 
744
            return JS_TRUE;
 
745
        }
 
746
 
 
747
        /* Create and return a new QName object exactly as if constructed. */
 
748
        obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
 
749
        if (!obj)
 
750
            return JS_FALSE;
 
751
        *rval = OBJECT_TO_JSVAL(obj);
 
752
    }
 
753
    METER(xml_stats.namespaceobj);
 
754
    METER(xml_stats.livenamespaceobj);
 
755
 
 
756
    /*
 
757
     * Create and connect private data to rooted obj early, so we don't have
 
758
     * to worry about rooting string newborns hanging off of the private data
 
759
     * further below.
 
760
     */
 
761
    empty = cx->runtime->emptyString;
 
762
    ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE);
 
763
    if (!ns)
 
764
        return JS_FALSE;
 
765
    if (!JS_SetPrivate(cx, obj, ns))
 
766
        return JS_FALSE;
 
767
    ns->object = obj;
 
768
 
 
769
    if (argc == 1) {
 
770
        if (isNamespace) {
 
771
            ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj);
 
772
            ns->uri = ns2->uri;
 
773
            ns->prefix = ns2->prefix;
 
774
        } else if (isQName &&
 
775
                   (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
 
776
            ns->uri = qn->uri;
 
777
            ns->prefix = qn->prefix;
 
778
        } else {
 
779
            ns->uri = js_ValueToString(cx, urival);
 
780
            if (!ns->uri)
 
781
                return JS_FALSE;
 
782
 
 
783
            /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
 
784
            if (!IS_EMPTY(ns->uri))
 
785
                ns->prefix = NULL;
 
786
        }
 
787
    } else if (argc == 2) {
 
788
        if (isQName &&
 
789
            (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
 
790
            ns->uri = qn->uri;
 
791
        } else {
 
792
            ns->uri = js_ValueToString(cx, urival);
 
793
            if (!ns->uri)
 
794
                return JS_FALSE;
 
795
        }
 
796
 
 
797
        prefixval = argv[0];
 
798
        if (IS_EMPTY(ns->uri)) {
 
799
            if (!JSVAL_IS_VOID(prefixval)) {
 
800
                prefix = js_ValueToString(cx, prefixval);
 
801
                if (!prefix)
 
802
                    return JS_FALSE;
 
803
                if (!IS_EMPTY(prefix)) {
 
804
                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
805
                                         JSMSG_BAD_XML_NAMESPACE,
 
806
                                         js_ValueToPrintableString(cx,
 
807
                                             STRING_TO_JSVAL(prefix)));
 
808
                    return JS_FALSE;
 
809
                }
 
810
            }
 
811
        } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
 
812
            /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */
 
813
            ns->prefix = NULL;
 
814
        } else {
 
815
            prefix = js_ValueToString(cx, prefixval);
 
816
            if (!prefix)
 
817
                return JS_FALSE;
 
818
            ns->prefix = prefix;
 
819
        }
 
820
    }
 
821
 
 
822
    return JS_TRUE;
 
823
}
 
824
 
 
825
static JSBool
 
826
QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
827
{
 
828
    jsval nameval, nsval;
 
829
    JSBool isQName, isNamespace;
 
830
    JSXMLQName *qn;
 
831
    JSString *uri, *prefix, *name;
 
832
    JSObject *nsobj;
 
833
    JSClass *clasp;
 
834
    JSXMLNamespace *ns;
 
835
 
 
836
    nameval = argv[argc > 1];
 
837
    isQName =
 
838
        !JSVAL_IS_PRIMITIVE(nameval) &&
 
839
        OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
 
840
 
 
841
    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
 
842
        /* QName called as function. */
 
843
        if (argc == 1 && isQName) {
 
844
            /* QName called with one QName argument is identity. */
 
845
            *rval = nameval;
 
846
            return JS_TRUE;
 
847
        }
 
848
 
 
849
        /*
 
850
         * Create and return a new QName object exactly as if constructed.
 
851
         * Use the constructor's clasp so we can be shared by AttributeName
 
852
         * (see below after this function).
 
853
         */
 
854
        obj = js_NewObject(cx,
 
855
                           JS_ValueToFunction(cx, argv[-2])->clasp,
 
856
                           NULL, NULL);
 
857
        if (!obj)
 
858
            return JS_FALSE;
 
859
        *rval = OBJECT_TO_JSVAL(obj);
 
860
    }
 
861
    METER(xml_stats.qnameobj);
 
862
    METER(xml_stats.liveqnameobj);
 
863
 
 
864
    if (isQName) {
 
865
        /* If namespace is not specified and name is a QName, clone it. */
 
866
        qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval));
 
867
        if (argc == 1) {
 
868
            uri = qn->uri;
 
869
            prefix = qn->prefix;
 
870
            name = qn->localName;
 
871
            goto out;
 
872
        }
 
873
 
 
874
        /* Namespace and qname were passed -- use the qname's localName. */
 
875
        nameval = STRING_TO_JSVAL(qn->localName);
 
876
    }
 
877
 
 
878
    if (argc == 0) {
 
879
        name = cx->runtime->emptyString;
 
880
    } else {
 
881
        name = js_ValueToString(cx, nameval);
 
882
        if (!name)
 
883
            return JS_FALSE;
 
884
 
 
885
        /* Use argv[1] as a local root for name, even if it was not passed. */
 
886
        argv[1] = STRING_TO_JSVAL(name);
 
887
    }
 
888
 
 
889
    nsval = argv[0];
 
890
    if (argc == 1 || JSVAL_IS_VOID(nsval)) {
 
891
        if (IS_STAR(name)) {
 
892
            nsval = JSVAL_NULL;
 
893
        } else {
 
894
            if (!js_GetDefaultXMLNamespace(cx, &nsval))
 
895
                return JS_FALSE;
 
896
        }
 
897
    }
 
898
 
 
899
    if (JSVAL_IS_NULL(nsval)) {
 
900
        /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
 
901
        uri = prefix = NULL;
 
902
    } else {
 
903
        /*
 
904
         * Inline specialization of the Namespace constructor called with
 
905
         * nsval passed as the only argument, to compute the uri and prefix
 
906
         * for the constructed namespace, without actually allocating the
 
907
         * object or computing other members.  See ECMA-357 13.3.2 6(a) and
 
908
         * 13.2.2.
 
909
         */
 
910
        isNamespace = isQName = JS_FALSE;
 
911
        if (!JSVAL_IS_PRIMITIVE(nsval)) {
 
912
            nsobj = JSVAL_TO_OBJECT(nsval);
 
913
            clasp = OBJ_GET_CLASS(cx, nsobj);
 
914
            isNamespace = (clasp == &js_NamespaceClass.base);
 
915
            isQName = (clasp == &js_QNameClass.base);
 
916
        }
 
917
#ifdef __GNUC__         /* suppress bogus gcc warnings */
 
918
        else nsobj = NULL;
 
919
#endif
 
920
 
 
921
        if (isNamespace) {
 
922
            ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
 
923
            uri = ns->uri;
 
924
            prefix = ns->prefix;
 
925
        } else if (isQName &&
 
926
                   (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) {
 
927
            uri = qn->uri;
 
928
            prefix = qn->prefix;
 
929
        } else {
 
930
            uri = js_ValueToString(cx, nsval);
 
931
            if (!uri)
 
932
                return JS_FALSE;
 
933
            argv[0] = STRING_TO_JSVAL(uri);     /* local root */
 
934
 
 
935
            /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
 
936
            prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
 
937
        }
 
938
    }
 
939
 
 
940
out:
 
941
    qn = js_NewXMLQName(cx, uri, prefix, name);
 
942
    if (!qn)
 
943
        return JS_FALSE;
 
944
    if (!JS_SetPrivate(cx, obj, qn))
 
945
        return JS_FALSE;
 
946
    qn->object = obj;
 
947
    return JS_TRUE;
 
948
}
 
949
 
 
950
static JSBool
 
951
AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
952
              jsval *rval)
 
953
{
 
954
    /*
 
955
     * Since js_AttributeNameClass was initialized, obj will have that as its
 
956
     * class, not js_QNameClass.
 
957
     */
 
958
    return QName(cx, obj, argc, argv, rval);
 
959
}
 
960
 
 
961
/*
 
962
 * XMLArray library functions.
 
963
 */
 
964
static JSBool
 
965
namespace_identity(const void *a, const void *b)
 
966
{
 
967
    const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
 
968
    const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
 
969
 
 
970
    if (nsa->prefix && nsb->prefix) {
 
971
        if (!js_EqualStrings(nsa->prefix, nsb->prefix))
 
972
            return JS_FALSE;
 
973
    } else {
 
974
        if (nsa->prefix || nsb->prefix)
 
975
            return JS_FALSE;
 
976
    }
 
977
    return js_EqualStrings(nsa->uri, nsb->uri);
 
978
}
 
979
 
 
980
static JSBool
 
981
attr_identity(const void *a, const void *b)
 
982
{
 
983
    const JSXML *xmla = (const JSXML *) a;
 
984
    const JSXML *xmlb = (const JSXML *) b;
 
985
 
 
986
    return qname_identity(xmla->name, xmlb->name);
 
987
}
 
988
 
 
989
static void
 
990
XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
 
991
{
 
992
    JSXMLArrayCursor *next;
 
993
 
 
994
    cursor->array = array;
 
995
    cursor->index = 0;
 
996
    next = cursor->next = array->cursors;
 
997
    if (next)
 
998
        next->prevp = &cursor->next;
 
999
    cursor->prevp = &array->cursors;
 
1000
    array->cursors = cursor;
 
1001
    cursor->root = NULL;
 
1002
}
 
1003
 
 
1004
static void
 
1005
XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
 
1006
{
 
1007
    JSXMLArrayCursor *next;
 
1008
 
 
1009
    if (!cursor->array)
 
1010
        return;
 
1011
    next = cursor->next;
 
1012
    if (next)
 
1013
        next->prevp = cursor->prevp;
 
1014
    *cursor->prevp = next;
 
1015
    cursor->array = NULL;
 
1016
}
 
1017
 
 
1018
static void *
 
1019
XMLArrayCursorNext(JSXMLArrayCursor *cursor)
 
1020
{
 
1021
    JSXMLArray *array;
 
1022
 
 
1023
    array = cursor->array;
 
1024
    if (!array || cursor->index >= array->length)
 
1025
        return NULL;
 
1026
    return cursor->root = array->vector[cursor->index++];
 
1027
}
 
1028
 
 
1029
static void *
 
1030
XMLArrayCursorItem(JSXMLArrayCursor *cursor)
 
1031
{
 
1032
    JSXMLArray *array;
 
1033
 
 
1034
    array = cursor->array;
 
1035
    if (!array || cursor->index >= array->length)
 
1036
        return NULL;
 
1037
    return cursor->root = array->vector[cursor->index];
 
1038
}
 
1039
 
 
1040
static void
 
1041
XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor)
 
1042
{
 
1043
    while (cursor) {
 
1044
        GC_MARK(cx, cursor->root, "cursor->root");
 
1045
        cursor = cursor->next;
 
1046
    }
 
1047
}
 
1048
 
 
1049
/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */
 
1050
static JSBool
 
1051
XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
 
1052
{
 
1053
    void **vector;
 
1054
 
 
1055
    if (capacity == 0) {
 
1056
        /* We could let realloc(p, 0) free this, but purify gets confused. */
 
1057
        if (array->vector)
 
1058
            free(array->vector);
 
1059
        vector = NULL;
 
1060
    } else {
 
1061
        if ((size_t)capacity > ~(size_t)0 / sizeof(void *) ||
 
1062
            !(vector = (void **)
 
1063
                       realloc(array->vector, capacity * sizeof(void *)))) {
 
1064
            if (cx)
 
1065
                JS_ReportOutOfMemory(cx);
 
1066
            return JS_FALSE;
 
1067
        }
 
1068
    }
 
1069
    array->capacity = JSXML_PRESET_CAPACITY | capacity;
 
1070
    array->vector = vector;
 
1071
    return JS_TRUE;
 
1072
}
 
1073
 
 
1074
static void
 
1075
XMLArrayTrim(JSXMLArray *array)
 
1076
{
 
1077
    if (array->capacity & JSXML_PRESET_CAPACITY)
 
1078
        return;
 
1079
    if (array->length < array->capacity)
 
1080
        XMLArraySetCapacity(NULL, array, array->length);
 
1081
}
 
1082
 
 
1083
static JSBool
 
1084
XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
 
1085
{
 
1086
    array->length = array->capacity = 0;
 
1087
    array->vector = NULL;
 
1088
    array->cursors = NULL;
 
1089
    return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
 
1090
}
 
1091
 
 
1092
static void
 
1093
XMLArrayFinish(JSContext *cx, JSXMLArray *array)
 
1094
{
 
1095
    JSXMLArrayCursor *cursor;
 
1096
 
 
1097
    JS_free(cx, array->vector);
 
1098
 
 
1099
    while ((cursor = array->cursors) != NULL)
 
1100
        XMLArrayCursorFinish(cursor);
 
1101
 
 
1102
#ifdef DEBUG
 
1103
    memset(array, 0xd5, sizeof *array);
 
1104
#endif
 
1105
}
 
1106
 
 
1107
#define XML_NOT_FOUND   ((uint32) -1)
 
1108
 
 
1109
static uint32
 
1110
XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
 
1111
{
 
1112
    void **vector;
 
1113
    uint32 i, n;
 
1114
 
 
1115
    /* The identity op must not reallocate array->vector. */
 
1116
    vector = array->vector;
 
1117
    if (identity) {
 
1118
        for (i = 0, n = array->length; i < n; i++) {
 
1119
            if (identity(vector[i], elt))
 
1120
                return i;
 
1121
        }
 
1122
    } else {
 
1123
        for (i = 0, n = array->length; i < n; i++) {
 
1124
            if (vector[i] == elt)
 
1125
                return i;
 
1126
        }
 
1127
    }
 
1128
    return XML_NOT_FOUND;
 
1129
}
 
1130
 
 
1131
/*
 
1132
 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
 
1133
 * that, grow by LINEAR_INCREMENT.  Both must be powers of two, and threshold
 
1134
 * should be greater than increment.
 
1135
 */
 
1136
#define LINEAR_THRESHOLD        256
 
1137
#define LINEAR_INCREMENT        32
 
1138
 
 
1139
static JSBool
 
1140
XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
 
1141
{
 
1142
    uint32 capacity, i;
 
1143
    int log2;
 
1144
    void **vector;
 
1145
 
 
1146
    if (index >= array->length) {
 
1147
        if (index >= JSXML_CAPACITY(array)) {
 
1148
            /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
 
1149
            capacity = index + 1;
 
1150
            if (index >= LINEAR_THRESHOLD) {
 
1151
                capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
 
1152
            } else {
 
1153
                JS_CEILING_LOG2(log2, capacity);
 
1154
                capacity = JS_BIT(log2);
 
1155
            }
 
1156
            if ((size_t)capacity > ~(size_t)0 / sizeof(void *) ||
 
1157
                !(vector = (void **)
 
1158
                           realloc(array->vector, capacity * sizeof(void *)))) {
 
1159
                JS_ReportOutOfMemory(cx);
 
1160
                return JS_FALSE;
 
1161
            }
 
1162
            array->capacity = capacity;
 
1163
            array->vector = vector;
 
1164
            for (i = array->length; i < index; i++)
 
1165
                vector[i] = NULL;
 
1166
        }
 
1167
        array->length = index + 1;
 
1168
    }
 
1169
 
 
1170
    array->vector[index] = elt;
 
1171
    return JS_TRUE;
 
1172
}
 
1173
 
 
1174
static JSBool
 
1175
XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
 
1176
{
 
1177
    uint32 j;
 
1178
    JSXMLArrayCursor *cursor;
 
1179
 
 
1180
    j = array->length;
 
1181
    JS_ASSERT(i <= j);
 
1182
    if (!XMLArraySetCapacity(cx, array, j + n))
 
1183
        return JS_FALSE;
 
1184
 
 
1185
    array->length = j + n;
 
1186
    JS_ASSERT(n != (uint32)-1);
 
1187
    while (j != i) {
 
1188
        --j;
 
1189
        array->vector[j + n] = array->vector[j];
 
1190
    }
 
1191
 
 
1192
    for (cursor = array->cursors; cursor; cursor = cursor->next) {
 
1193
        if (cursor->index > i)
 
1194
            cursor->index += n;
 
1195
    }
 
1196
    return JS_TRUE;
 
1197
}
 
1198
 
 
1199
static void *
 
1200
XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
 
1201
{
 
1202
    uint32 length;
 
1203
    void **vector, *elt;
 
1204
    JSXMLArrayCursor *cursor;
 
1205
 
 
1206
    length = array->length;
 
1207
    if (index >= length)
 
1208
        return NULL;
 
1209
 
 
1210
    vector = array->vector;
 
1211
    elt = vector[index];
 
1212
    if (compress) {
 
1213
        while (++index < length)
 
1214
            vector[index-1] = vector[index];
 
1215
        array->length = length - 1;
 
1216
        array->capacity = JSXML_CAPACITY(array);
 
1217
    } else {
 
1218
        vector[index] = NULL;
 
1219
    }
 
1220
 
 
1221
    for (cursor = array->cursors; cursor; cursor = cursor->next) {
 
1222
        if (cursor->index > index)
 
1223
            --cursor->index;
 
1224
    }
 
1225
    return elt;
 
1226
}
 
1227
 
 
1228
static void
 
1229
XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
 
1230
{
 
1231
    void **vector;
 
1232
 
 
1233
    JS_ASSERT(!array->cursors);
 
1234
    if (length >= array->length)
 
1235
        return;
 
1236
 
 
1237
    if (length == 0) {
 
1238
        if (array->vector)
 
1239
            free(array->vector);
 
1240
        vector = NULL;
 
1241
    } else {
 
1242
        vector = realloc(array->vector, length * sizeof(void *));
 
1243
        if (!vector)
 
1244
            return;
 
1245
    }
 
1246
 
 
1247
    if (array->length > length)
 
1248
        array->length = length;
 
1249
    array->capacity = length;
 
1250
    array->vector = vector;
 
1251
}
 
1252
 
 
1253
#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
 
1254
#define XMLARRAY_HAS_MEMBER(a,e,f)  (XMLArrayFindMember(a, (void *)(e), f) != \
 
1255
                                     XML_NOT_FOUND)
 
1256
#define XMLARRAY_MEMBER(a,i,t)      (((i) < (a)->length)                      \
 
1257
                                     ? (t *) (a)->vector[i]                   \
 
1258
                                     : NULL)
 
1259
#define XMLARRAY_SET_MEMBER(a,i,e)  JS_BEGIN_MACRO                            \
 
1260
                                        if ((a)->length <= (i))               \
 
1261
                                            (a)->length = (i) + 1;            \
 
1262
                                        ((a)->vector[i] = (void *)(e));       \
 
1263
                                    JS_END_MACRO
 
1264
#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
 
1265
#define XMLARRAY_INSERT(x,a,i,n)    XMLArrayInsert(x, a, i, n)
 
1266
#define XMLARRAY_APPEND(x,a,e)      XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
 
1267
#define XMLARRAY_DELETE(x,a,i,c,t)  ((t *) XMLArrayDelete(x, a, i, c))
 
1268
#define XMLARRAY_TRUNCATE(x,a,n)    XMLArrayTruncate(x, a, n)
 
1269
 
 
1270
/*
 
1271
 * Define XML setting property strings and constants early, so everyone can
 
1272
 * use the same names and their magic numbers (tinyids, flags).
 
1273
 */
 
1274
static const char js_ignoreComments_str[]   = "ignoreComments";
 
1275
static const char js_ignoreProcessingInstructions_str[]
 
1276
                                            = "ignoreProcessingInstructions";
 
1277
static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
 
1278
static const char js_prettyPrinting_str[]   = "prettyPrinting";
 
1279
static const char js_prettyIndent_str[]     = "prettyIndent";
 
1280
 
 
1281
/*
 
1282
 * NB: These XML static property tinyids must
 
1283
 * (a) not collide with the generic negative tinyids at the top of jsfun.c;
 
1284
 * (b) index their corresponding xml_static_props array elements.
 
1285
 * Don't change 'em!
 
1286
 */
 
1287
enum xml_static_tinyid {
 
1288
    XML_IGNORE_COMMENTS,
 
1289
    XML_IGNORE_PROCESSING_INSTRUCTIONS,
 
1290
    XML_IGNORE_WHITESPACE,
 
1291
    XML_PRETTY_PRINTING,
 
1292
    XML_PRETTY_INDENT
 
1293
};
 
1294
 
 
1295
static JSBool
 
1296
xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
1297
{
 
1298
    return JS_TRUE;
 
1299
}
 
1300
 
 
1301
static JSBool
 
1302
xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
1303
{
 
1304
    JSBool b;
 
1305
    uint8 flag;
 
1306
 
 
1307
    JS_ASSERT(JSVAL_IS_INT(id));
 
1308
    if (!js_ValueToBoolean(cx, *vp, &b))
 
1309
        return JS_FALSE;
 
1310
 
 
1311
    flag = JS_BIT(JSVAL_TO_INT(id));
 
1312
    if (b)
 
1313
        cx->xmlSettingFlags |= flag;
 
1314
    else
 
1315
        cx->xmlSettingFlags &= ~flag;
 
1316
    return JS_TRUE;
 
1317
}
 
1318
 
 
1319
static JSPropertySpec xml_static_props[] = {
 
1320
    {js_ignoreComments_str,     XML_IGNORE_COMMENTS,   JSPROP_PERMANENT,
 
1321
                                xml_setting_getter, xml_setting_setter},
 
1322
    {js_ignoreProcessingInstructions_str,
 
1323
                   XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
 
1324
                                xml_setting_getter, xml_setting_setter},
 
1325
    {js_ignoreWhitespace_str,   XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
 
1326
                                xml_setting_getter, xml_setting_setter},
 
1327
    {js_prettyPrinting_str,     XML_PRETTY_PRINTING,   JSPROP_PERMANENT,
 
1328
                                xml_setting_getter, xml_setting_setter},
 
1329
    {js_prettyIndent_str,       XML_PRETTY_INDENT,     JSPROP_PERMANENT,
 
1330
                                xml_setting_getter, NULL},
 
1331
    {0,0,0,0,0}
 
1332
};
 
1333
 
 
1334
/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
 
1335
#define XSF_IGNORE_COMMENTS     JS_BIT(XML_IGNORE_COMMENTS)
 
1336
#define XSF_IGNORE_PROCESSING_INSTRUCTIONS                                    \
 
1337
                                JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
 
1338
#define XSF_IGNORE_WHITESPACE   JS_BIT(XML_IGNORE_WHITESPACE)
 
1339
#define XSF_PRETTY_PRINTING     JS_BIT(XML_PRETTY_PRINTING)
 
1340
#define XSF_CACHE_VALID         JS_BIT(XML_PRETTY_INDENT)
 
1341
 
 
1342
/*
 
1343
 * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
 
1344
 * This flag means a couple of things:
 
1345
 *
 
1346
 * - The top JSXML created for a parse tree must have an object owning it.
 
1347
 *
 
1348
 * - That the default namespace normally inherited from the temporary
 
1349
 *   <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
 
1350
 *   string must, in the case of a precompiled XML object tree, inherit via
 
1351
 *   ad-hoc code in ParseNodeToXML.
 
1352
 *
 
1353
 * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
 
1354
 */
 
1355
#define XSF_PRECOMPILED_ROOT    (XSF_CACHE_VALID << 1)
 
1356
 
 
1357
/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
 
1358
#define IS_XML(str)                                                           \
 
1359
    (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
 
1360
 
 
1361
#define IS_XMLNS(str)                                                         \
 
1362
    (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
 
1363
 
 
1364
#define IS_XML_CHARS(chars)                                                   \
 
1365
    (JS_TOLOWER((chars)[0]) == 'x' &&                                         \
 
1366
     JS_TOLOWER((chars)[1]) == 'm' &&                                         \
 
1367
     JS_TOLOWER((chars)[2]) == 'l')
 
1368
 
 
1369
#define HAS_NS_AFTER_XML(chars)                                               \
 
1370
    (JS_TOLOWER((chars)[3]) == 'n' &&                                         \
 
1371
     JS_TOLOWER((chars)[4]) == 's')
 
1372
 
 
1373
#define IS_XMLNS_CHARS(chars)                                                 \
 
1374
    (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
 
1375
 
 
1376
#define STARTS_WITH_XML(chars,length)                                         \
 
1377
    (length >= 3 && IS_XML_CHARS(chars))
 
1378
 
 
1379
static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
 
1380
static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
 
1381
 
 
1382
static JSXMLQName *
 
1383
ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
 
1384
                 JSBool isAttributeName)
 
1385
{
 
1386
    JSString *str, *uri, *prefix, *localName;
 
1387
    size_t length, offset;
 
1388
    const jschar *start, *limit, *colon;
 
1389
    uint32 n;
 
1390
    JSXMLNamespace *ns;
 
1391
 
 
1392
    JS_ASSERT(pn->pn_arity == PN_NULLARY);
 
1393
    str = ATOM_TO_STRING(pn->pn_atom);
 
1394
    length = JSSTRING_LENGTH(str);
 
1395
    start = JSSTRING_CHARS(str);
 
1396
    JS_ASSERT(length != 0 && *start != '@');
 
1397
    JS_ASSERT(length != 1 || *start != '*');
 
1398
 
 
1399
    uri = cx->runtime->emptyString;
 
1400
    limit = start + length;
 
1401
    colon = js_strchr_limit(start, ':', limit);
 
1402
    if (colon) {
 
1403
        offset = PTRDIFF(colon, start, jschar);
 
1404
        prefix = js_NewDependentString(cx, str, 0, offset, 0);
 
1405
        if (!prefix)
 
1406
            return NULL;
 
1407
 
 
1408
        if (STARTS_WITH_XML(start, offset)) {
 
1409
            if (offset == 3) {
 
1410
                uri = JS_InternString(cx, xml_namespace_str);
 
1411
                if (!uri)
 
1412
                    return NULL;
 
1413
            } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
 
1414
                uri = JS_InternString(cx, xmlns_namespace_str);
 
1415
                if (!uri)
 
1416
                    return NULL;
 
1417
            } else {
 
1418
                uri = NULL;
 
1419
            }
 
1420
        } else {
 
1421
            uri = NULL;
 
1422
            n = inScopeNSes->length;
 
1423
            while (n != 0) {
 
1424
                --n;
 
1425
                ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
 
1426
                if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) {
 
1427
                    uri = ns->uri;
 
1428
                    break;
 
1429
                }
 
1430
            }
 
1431
        }
 
1432
 
 
1433
        if (!uri) {
 
1434
            js_ReportCompileErrorNumber(cx, pn,
 
1435
                                        JSREPORT_PN | JSREPORT_ERROR,
 
1436
                                        JSMSG_BAD_XML_NAMESPACE,
 
1437
                                        js_ValueToPrintableString(cx,
 
1438
                                            STRING_TO_JSVAL(prefix)));
 
1439
            return NULL;
 
1440
        }
 
1441
 
 
1442
        localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0);
 
1443
        if (!localName)
 
1444
            return NULL;
 
1445
    } else {
 
1446
        if (isAttributeName) {
 
1447
            /*
 
1448
             * An unprefixed attribute is not in any namespace, so set prefix
 
1449
             * as well as uri to the empty string.
 
1450
             */
 
1451
            prefix = uri;
 
1452
        } else {
 
1453
            /*
 
1454
             * Loop from back to front looking for the closest declared default
 
1455
             * namespace.
 
1456
             */
 
1457
            n = inScopeNSes->length;
 
1458
            while (n != 0) {
 
1459
                --n;
 
1460
                ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
 
1461
                if (!ns->prefix || IS_EMPTY(ns->prefix)) {
 
1462
                    uri = ns->uri;
 
1463
                    break;
 
1464
                }
 
1465
            }
 
1466
            prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
 
1467
        }
 
1468
        localName = str;
 
1469
    }
 
1470
 
 
1471
    return js_NewXMLQName(cx, uri, prefix, localName);
 
1472
}
 
1473
 
 
1474
static JSString *
 
1475
ChompXMLWhitespace(JSContext *cx, JSString *str)
 
1476
{
 
1477
    size_t length, newlength, offset;
 
1478
    const jschar *cp, *start, *end;
 
1479
    jschar c;
 
1480
 
 
1481
    length = JSSTRING_LENGTH(str);
 
1482
    for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
 
1483
        c = *cp;
 
1484
        if (!JS_ISXMLSPACE(c))
 
1485
            break;
 
1486
    }
 
1487
    while (end > cp) {
 
1488
        c = end[-1];
 
1489
        if (!JS_ISXMLSPACE(c))
 
1490
            break;
 
1491
        --end;
 
1492
    }
 
1493
    newlength = PTRDIFF(end, cp, jschar);
 
1494
    if (newlength == length)
 
1495
        return str;
 
1496
    offset = PTRDIFF(cp, start, jschar);
 
1497
    return js_NewDependentString(cx, str, offset, newlength, 0);
 
1498
}
 
1499
 
 
1500
static JSXML *
 
1501
ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
 
1502
               uintN flags)
 
1503
{
 
1504
    JSXML *xml, *kid, *attr, *attrj;
 
1505
    JSString *str;
 
1506
    uint32 length, n, i, j;
 
1507
    JSParseNode *pn2, *pn3, *head, **pnp;
 
1508
    JSXMLNamespace *ns;
 
1509
    JSXMLQName *qn, *attrjqn;
 
1510
    JSXMLClass xml_class;
 
1511
    int stackDummy;
 
1512
 
 
1513
    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
 
1514
        js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
 
1515
                                    JSMSG_OVER_RECURSED);
 
1516
        return NULL;
 
1517
    }
 
1518
 
 
1519
#define PN2X_SKIP_CHILD ((JSXML *) 1)
 
1520
 
 
1521
    /*
 
1522
     * Cases return early to avoid common code that gets an outermost xml's
 
1523
     * object, which protects GC-things owned by xml and its descendants from
 
1524
     * garbage collection.
 
1525
     */
 
1526
    xml = NULL;
 
1527
    if (!js_EnterLocalRootScope(cx))
 
1528
        return NULL;
 
1529
    switch (pn->pn_type) {
 
1530
      case TOK_XMLELEM:
 
1531
        length = inScopeNSes->length;
 
1532
        pn2 = pn->pn_head;
 
1533
        xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
 
1534
        if (!xml)
 
1535
            goto fail;
 
1536
 
 
1537
        flags &= ~XSF_PRECOMPILED_ROOT;
 
1538
        n = pn->pn_count;
 
1539
        JS_ASSERT(n >= 2);
 
1540
        n -= 2;
 
1541
        if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
 
1542
            goto fail;
 
1543
 
 
1544
        i = 0;
 
1545
        while ((pn2 = pn2->pn_next) != NULL) {
 
1546
            if (!pn2->pn_next) {
 
1547
                /* Don't append the end tag! */
 
1548
                JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
 
1549
                break;
 
1550
            }
 
1551
 
 
1552
            if ((flags & XSF_IGNORE_WHITESPACE) &&
 
1553
                n > 1 && pn2->pn_type == TOK_XMLSPACE) {
 
1554
                --n;
 
1555
                continue;
 
1556
            }
 
1557
 
 
1558
            kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
 
1559
            if (kid == PN2X_SKIP_CHILD) {
 
1560
                --n;
 
1561
                continue;
 
1562
            }
 
1563
 
 
1564
            if (!kid)
 
1565
                goto fail;
 
1566
 
 
1567
            /* Store kid in xml right away, to protect it from GC. */
 
1568
            XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
 
1569
            kid->parent = xml;
 
1570
            ++i;
 
1571
 
 
1572
            /* XXX where is this documented in an XML spec, or in E4X? */
 
1573
            if ((flags & XSF_IGNORE_WHITESPACE) &&
 
1574
                n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
 
1575
                str = ChompXMLWhitespace(cx, kid->xml_value);
 
1576
                if (!str)
 
1577
                    goto fail;
 
1578
                kid->xml_value = str;
 
1579
            }
 
1580
        }
 
1581
 
 
1582
        JS_ASSERT(i == n);
 
1583
        if (n < pn->pn_count - 2)
 
1584
            XMLArrayTrim(&xml->xml_kids);
 
1585
        XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
 
1586
        break;
 
1587
 
 
1588
      case TOK_XMLLIST:
 
1589
        xml = js_NewXML(cx, JSXML_CLASS_LIST);
 
1590
        if (!xml)
 
1591
            goto fail;
 
1592
 
 
1593
        n = pn->pn_count;
 
1594
        if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
 
1595
            goto fail;
 
1596
 
 
1597
        i = 0;
 
1598
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
 
1599
            /*
 
1600
             * Always ignore insignificant whitespace in lists -- we shouldn't
 
1601
             * condition this on an XML.ignoreWhitespace setting when the list
 
1602
             * constructor is XMLList (note XML/XMLList unification hazard).
 
1603
             */
 
1604
            if (pn2->pn_type == TOK_XMLSPACE) {
 
1605
                --n;
 
1606
                continue;
 
1607
            }
 
1608
 
 
1609
            kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
 
1610
            if (kid == PN2X_SKIP_CHILD) {
 
1611
                --n;
 
1612
                continue;
 
1613
            }
 
1614
 
 
1615
            if (!kid)
 
1616
                goto fail;
 
1617
 
 
1618
            XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
 
1619
            ++i;
 
1620
        }
 
1621
 
 
1622
        if (n < pn->pn_count)
 
1623
            XMLArrayTrim(&xml->xml_kids);
 
1624
        break;
 
1625
 
 
1626
      case TOK_XMLSTAGO:
 
1627
      case TOK_XMLPTAGC:
 
1628
        length = inScopeNSes->length;
 
1629
        pn2 = pn->pn_head;
 
1630
        JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
 
1631
        if (pn2->pn_arity == PN_LIST)
 
1632
            goto syntax;
 
1633
 
 
1634
        xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
 
1635
        if (!xml)
 
1636
            goto fail;
 
1637
 
 
1638
        /* First pass: check syntax and process namespace declarations. */
 
1639
        JS_ASSERT(pn->pn_count >= 1);
 
1640
        n = pn->pn_count - 1;
 
1641
        pnp = &pn2->pn_next;
 
1642
        head = *pnp;
 
1643
        while ((pn2 = *pnp) != NULL) {
 
1644
            size_t length;
 
1645
            const jschar *chars;
 
1646
 
 
1647
            if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
 
1648
                goto syntax;
 
1649
 
 
1650
            /* Enforce "Well-formedness constraint: Unique Att Spec". */
 
1651
            for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
 
1652
                if (pn3->pn_atom == pn2->pn_atom) {
 
1653
                    js_ReportCompileErrorNumber(cx, pn2,
 
1654
                                                JSREPORT_PN | JSREPORT_ERROR,
 
1655
                                                JSMSG_DUPLICATE_XML_ATTR,
 
1656
                                                js_ValueToPrintableString(cx,
 
1657
                                                    ATOM_KEY(pn2->pn_atom)));
 
1658
                    goto fail;
 
1659
                }
 
1660
            }
 
1661
 
 
1662
            str = ATOM_TO_STRING(pn2->pn_atom);
 
1663
            pn2 = pn2->pn_next;
 
1664
            JS_ASSERT(pn2);
 
1665
            if (pn2->pn_type != TOK_XMLATTR)
 
1666
                goto syntax;
 
1667
 
 
1668
            length = JSSTRING_LENGTH(str);
 
1669
            chars = JSSTRING_CHARS(str);
 
1670
            if (length >= 5 &&
 
1671
                IS_XMLNS_CHARS(chars) &&
 
1672
                (length == 5 || chars[5] == ':')) {
 
1673
                JSString *uri, *prefix;
 
1674
 
 
1675
                uri = ATOM_TO_STRING(pn2->pn_atom);
 
1676
                if (length == 5) {
 
1677
                    /* 10.3.2.1. Step 6(h)(i)(1)(a). */
 
1678
                    prefix = cx->runtime->emptyString;
 
1679
                } else {
 
1680
                    prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0);
 
1681
                    if (!prefix)
 
1682
                        goto fail;
 
1683
                }
 
1684
 
 
1685
                /*
 
1686
                 * Once the new ns is appended to xml->xml_namespaces, it is
 
1687
                 * protected from GC by the object that owns xml -- which is
 
1688
                 * either xml->object if outermost, or the object owning xml's
 
1689
                 * oldest ancestor if !outermost.
 
1690
                 */
 
1691
                ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE);
 
1692
                if (!ns)
 
1693
                    goto fail;
 
1694
 
 
1695
                /*
 
1696
                 * Don't add a namespace that's already in scope.  If someone
 
1697
                 * extracts a child property from its parent via [[Get]], then
 
1698
                 * we enforce the invariant, noted many times in ECMA-357, that
 
1699
                 * the child's namespaces form a possibly-improper superset of
 
1700
                 * its ancestors' namespaces.
 
1701
                 */
 
1702
                if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
 
1703
                    if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
 
1704
                        !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
 
1705
                        goto fail;
 
1706
                    }
 
1707
                }
 
1708
 
 
1709
                JS_ASSERT(n >= 2);
 
1710
                n -= 2;
 
1711
                *pnp = pn2->pn_next;
 
1712
                /* XXXbe recycle pn2 */
 
1713
                continue;
 
1714
            }
 
1715
 
 
1716
            pnp = &pn2->pn_next;
 
1717
        }
 
1718
 
 
1719
        /*
 
1720
         * If called from js_ParseNodeToXMLObject, emulate the effect of the
 
1721
         * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
 
1722
         * the String Type" (ECMA-357 10.3.1).
 
1723
         */
 
1724
        if (flags & XSF_PRECOMPILED_ROOT) {
 
1725
            JS_ASSERT(length >= 1);
 
1726
            ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace);
 
1727
            JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
 
1728
                                           namespace_identity));
 
1729
            ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE);
 
1730
            if (!ns)
 
1731
                goto fail;
 
1732
            if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
 
1733
                goto fail;
 
1734
        }
 
1735
        XMLArrayTrim(&xml->xml_namespaces);
 
1736
 
 
1737
        /* Second pass: process tag name and attributes, using namespaces. */
 
1738
        pn2 = pn->pn_head;
 
1739
        qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE);
 
1740
        if (!qn)
 
1741
            goto fail;
 
1742
        xml->name = qn;
 
1743
 
 
1744
        JS_ASSERT((n & 1) == 0);
 
1745
        n >>= 1;
 
1746
        if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
 
1747
            goto fail;
 
1748
 
 
1749
        for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
 
1750
            qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE);
 
1751
            if (!qn) {
 
1752
                xml->xml_attrs.length = i;
 
1753
                goto fail;
 
1754
            }
 
1755
 
 
1756
            /*
 
1757
             * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
 
1758
             * this time checking local name and namespace URI.
 
1759
             */
 
1760
            for (j = 0; j < i; j++) {
 
1761
                attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
 
1762
                attrjqn = attrj->name;
 
1763
                if (js_EqualStrings(attrjqn->uri, qn->uri) &&
 
1764
                    js_EqualStrings(attrjqn->localName, qn->localName)) {
 
1765
                    js_ReportCompileErrorNumber(cx, pn2,
 
1766
                                                JSREPORT_PN | JSREPORT_ERROR,
 
1767
                                                JSMSG_DUPLICATE_XML_ATTR,
 
1768
                                                js_ValueToPrintableString(cx,
 
1769
                                                    ATOM_KEY(pn2->pn_atom)));
 
1770
                    goto fail;
 
1771
                }
 
1772
            }
 
1773
 
 
1774
            pn2 = pn2->pn_next;
 
1775
            JS_ASSERT(pn2);
 
1776
            JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
 
1777
 
 
1778
            attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
 
1779
            if (!attr)
 
1780
                goto fail;
 
1781
 
 
1782
            XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
 
1783
            attr->parent = xml;
 
1784
            attr->name = qn;
 
1785
            attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
 
1786
        }
 
1787
 
 
1788
        /* Point tag closes its own namespace scope. */
 
1789
        if (pn->pn_type == TOK_XMLPTAGC)
 
1790
            XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
 
1791
        break;
 
1792
 
 
1793
      case TOK_XMLSPACE:
 
1794
      case TOK_XMLTEXT:
 
1795
      case TOK_XMLCDATA:
 
1796
      case TOK_XMLCOMMENT:
 
1797
      case TOK_XMLPI:
 
1798
        str = ATOM_TO_STRING(pn->pn_atom);
 
1799
        qn = NULL;
 
1800
        if (pn->pn_type == TOK_XMLCOMMENT) {
 
1801
            if (flags & XSF_IGNORE_COMMENTS)
 
1802
                goto skip_child;
 
1803
            xml_class = JSXML_CLASS_COMMENT;
 
1804
        } else if (pn->pn_type == TOK_XMLPI) {
 
1805
            if (IS_XML(str)) {
 
1806
                js_ReportCompileErrorNumber(cx, pn,
 
1807
                                            JSREPORT_PN | JSREPORT_ERROR,
 
1808
                                            JSMSG_RESERVED_ID,
 
1809
                                            js_ValueToPrintableString(cx,
 
1810
                                                STRING_TO_JSVAL(str)));
 
1811
                goto fail;
 
1812
            }
 
1813
 
 
1814
            if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
 
1815
                goto skip_child;
 
1816
 
 
1817
            qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE);
 
1818
            if (!qn)
 
1819
                goto fail;
 
1820
 
 
1821
            str = pn->pn_atom2
 
1822
                  ? ATOM_TO_STRING(pn->pn_atom2)
 
1823
                  : cx->runtime->emptyString;
 
1824
            xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
 
1825
        } else {
 
1826
            /* CDATA section content, or element text. */
 
1827
            xml_class = JSXML_CLASS_TEXT;
 
1828
        }
 
1829
 
 
1830
        xml = js_NewXML(cx, xml_class);
 
1831
        if (!xml)
 
1832
            goto fail;
 
1833
        xml->name = qn;
 
1834
        if (pn->pn_type == TOK_XMLSPACE)
 
1835
            xml->xml_flags |= XMLF_WHITESPACE_TEXT;
 
1836
        xml->xml_value = str;
 
1837
        break;
 
1838
 
 
1839
      default:
 
1840
        goto syntax;
 
1841
    }
 
1842
 
 
1843
    js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
 
1844
    if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
 
1845
        return NULL;
 
1846
    return xml;
 
1847
 
 
1848
skip_child:
 
1849
    js_LeaveLocalRootScope(cx);
 
1850
    return PN2X_SKIP_CHILD;
 
1851
 
 
1852
#undef PN2X_SKIP_CHILD
 
1853
 
 
1854
syntax:
 
1855
    js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
 
1856
                                JSMSG_BAD_XML_MARKUP);
 
1857
fail:
 
1858
    js_LeaveLocalRootScope(cx);
 
1859
    return NULL;
 
1860
}
 
1861
 
 
1862
/*
 
1863
 * XML helper, object-ops, and library functions.  We start with the helpers,
 
1864
 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
 
1865
 */
 
1866
static JSBool
 
1867
GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
 
1868
{
 
1869
    jsval v;
 
1870
 
 
1871
    if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
 
1872
        return JS_FALSE;
 
1873
    if (!VALUE_IS_FUNCTION(cx, v)) {
 
1874
        *vp = JSVAL_VOID;
 
1875
        return JS_TRUE;
 
1876
    }
 
1877
    return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
 
1878
}
 
1879
 
 
1880
static JSBool
 
1881
FillSettingsCache(JSContext *cx)
 
1882
{
 
1883
    int i;
 
1884
    const char *name;
 
1885
    jsval v;
 
1886
    JSBool isSet;
 
1887
 
 
1888
    /* Note: XML_PRETTY_INDENT is not a boolean setting. */
 
1889
    for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
 
1890
        name = xml_static_props[i].name;
 
1891
        if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet))
 
1892
            return JS_FALSE;
 
1893
        if (isSet)
 
1894
            cx->xmlSettingFlags |= JS_BIT(i);
 
1895
        else
 
1896
            cx->xmlSettingFlags &= ~JS_BIT(i);
 
1897
    }
 
1898
 
 
1899
    cx->xmlSettingFlags |= XSF_CACHE_VALID;
 
1900
    return JS_TRUE;
 
1901
}
 
1902
 
 
1903
static JSBool
 
1904
GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
 
1905
{
 
1906
    int i;
 
1907
 
 
1908
    if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
 
1909
        return JS_FALSE;
 
1910
 
 
1911
    for (i = 0; xml_static_props[i].name; i++) {
 
1912
        if (!strcmp(xml_static_props[i].name, name)) {
 
1913
            *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
 
1914
            return JS_TRUE;
 
1915
        }
 
1916
    }
 
1917
    *bp = JS_FALSE;
 
1918
    return JS_TRUE;
 
1919
}
 
1920
 
 
1921
static JSBool
 
1922
GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
 
1923
{
 
1924
    jsval v;
 
1925
 
 
1926
    return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip);
 
1927
}
 
1928
 
 
1929
static JSBool
 
1930
GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
 
1931
{
 
1932
    JSBool flag;
 
1933
 
 
1934
    /* Just get the first flag to validate the setting flags cache. */
 
1935
    if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
 
1936
        return JS_FALSE;
 
1937
    *flagsp = cx->xmlSettingFlags;
 
1938
    return JS_TRUE;
 
1939
}
 
1940
 
 
1941
static JSXML *
 
1942
ParseXMLSource(JSContext *cx, JSString *src)
 
1943
{
 
1944
    jsval nsval;
 
1945
    JSXMLNamespace *ns;
 
1946
    size_t urilen, srclen, length, offset, dstlen;
 
1947
    jschar *chars;
 
1948
    const jschar *srcp, *endp;
 
1949
    void *mark;
 
1950
    JSTokenStream *ts;
 
1951
    uintN lineno;
 
1952
    JSStackFrame *fp;
 
1953
    JSOp op;
 
1954
    JSParseNode *pn;
 
1955
    JSXML *xml;
 
1956
    JSXMLArray nsarray;
 
1957
    uintN flags;
 
1958
 
 
1959
    static const char prefix[] = "<parent xmlns='";
 
1960
    static const char middle[] = "'>";
 
1961
    static const char suffix[] = "</parent>";
 
1962
 
 
1963
#define constrlen(constr)   (sizeof(constr) - 1)
 
1964
 
 
1965
    if (!js_GetDefaultXMLNamespace(cx, &nsval))
 
1966
        return NULL;
 
1967
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
 
1968
 
 
1969
    urilen = JSSTRING_LENGTH(ns->uri);
 
1970
    srclen = JSSTRING_LENGTH(src);
 
1971
    length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
 
1972
             constrlen(suffix);
 
1973
 
 
1974
    chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
 
1975
    if (!chars)
 
1976
        return NULL;
 
1977
 
 
1978
    dstlen = length;
 
1979
    js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
 
1980
    offset = dstlen;
 
1981
    js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen);
 
1982
    offset += urilen;
 
1983
    dstlen = length - offset + 1;
 
1984
    js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
 
1985
                             &dstlen);
 
1986
    offset += dstlen;
 
1987
    srcp = JSSTRING_CHARS(src);
 
1988
    js_strncpy(chars + offset, srcp, srclen);
 
1989
    offset += srclen;
 
1990
    dstlen = length - offset + 1;
 
1991
    js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
 
1992
                             &dstlen);
 
1993
    chars [offset + dstlen] = 0;
 
1994
 
 
1995
    mark = JS_ARENA_MARK(&cx->tempPool);
 
1996
    ts = js_NewBufferTokenStream(cx, chars, length);
 
1997
    if (!ts)
 
1998
        return NULL;
 
1999
    for (fp = cx->fp; fp && !fp->pc; fp = fp->down)
 
2000
        continue;
 
2001
    if (fp) {
 
2002
        op = (JSOp) *fp->pc;
 
2003
        if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
 
2004
            ts->filename = fp->script->filename;
 
2005
            lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
 
2006
            for (endp = srcp + srclen; srcp < endp; srcp++)
 
2007
                if (*srcp == '\n')
 
2008
                    --lineno;
 
2009
            ts->lineno = lineno;
 
2010
        }
 
2011
    }
 
2012
 
 
2013
    JS_KEEP_ATOMS(cx->runtime);
 
2014
    pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE);
 
2015
    xml = NULL;
 
2016
    if (pn && XMLArrayInit(cx, &nsarray, 1)) {
 
2017
        if (GetXMLSettingFlags(cx, &flags))
 
2018
            xml = ParseNodeToXML(cx, pn, &nsarray, flags);
 
2019
 
 
2020
        XMLArrayFinish(cx, &nsarray);
 
2021
    }
 
2022
    JS_UNKEEP_ATOMS(cx->runtime);
 
2023
 
 
2024
    JS_ARENA_RELEASE(&cx->tempPool, mark);
 
2025
    JS_free(cx, chars);
 
2026
    return xml;
 
2027
 
 
2028
#undef constrlen
 
2029
}
 
2030
 
 
2031
/*
 
2032
 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
 
2033
 *
 
2034
 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
 
2035
 * the constraint:
 
2036
 *
 
2037
 *     for all x belonging to XML:
 
2038
 *         x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
 
2039
 *
 
2040
 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
 
2041
 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
 
2042
 *
 
2043
 * Same goes for 10.4.1 Step 7(a).
 
2044
 *
 
2045
 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
 
2046
 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
 
2047
 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
 
2048
 * undeclared namespaces associated with x not belonging to ancestorNS.
 
2049
 */
 
2050
static JSXML *
 
2051
OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
 
2052
{
 
2053
    JSXMLNamespace *ns;
 
2054
 
 
2055
    ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace);
 
2056
    xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
2057
    if (!ns || !xml)
 
2058
        return xml;
 
2059
    if (xml->xml_class == JSXML_CLASS_ELEMENT) {
 
2060
        if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
 
2061
            return NULL;
 
2062
        ns->declared = JS_FALSE;
 
2063
    }
 
2064
    xml->parent = NULL;
 
2065
    return xml;
 
2066
}
 
2067
 
 
2068
static JSObject *
 
2069
ToXML(JSContext *cx, jsval v)
 
2070
{
 
2071
    JSObject *obj;
 
2072
    JSXML *xml;
 
2073
    JSClass *clasp;
 
2074
    JSString *str;
 
2075
    uint32 length;
 
2076
 
 
2077
    if (JSVAL_IS_PRIMITIVE(v)) {
 
2078
        if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
 
2079
            goto bad;
 
2080
    } else {
 
2081
        obj = JSVAL_TO_OBJECT(v);
 
2082
        if (OBJECT_IS_XML(cx, obj)) {
 
2083
            xml = (JSXML *) JS_GetPrivate(cx, obj);
 
2084
            if (xml->xml_class == JSXML_CLASS_LIST) {
 
2085
                if (xml->xml_kids.length != 1)
 
2086
                    goto bad;
 
2087
                xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
2088
                if (xml) {
 
2089
                    JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
 
2090
                    return js_GetXMLObject(cx, xml);
 
2091
                }
 
2092
            }
 
2093
            return obj;
 
2094
        }
 
2095
 
 
2096
        clasp = OBJ_GET_CLASS(cx, obj);
 
2097
        if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
 
2098
            JS_ASSERT(0);
 
2099
        }
 
2100
 
 
2101
        if (clasp != &js_StringClass &&
 
2102
            clasp != &js_NumberClass &&
 
2103
            clasp != &js_BooleanClass) {
 
2104
            goto bad;
 
2105
        }
 
2106
    }
 
2107
 
 
2108
    str = js_ValueToString(cx, v);
 
2109
    if (!str)
 
2110
        return NULL;
 
2111
    if (IS_EMPTY(str)) {
 
2112
        length = 0;
 
2113
#ifdef __GNUC__         /* suppress bogus gcc warnings */
 
2114
        xml = NULL;
 
2115
#endif
 
2116
    } else {
 
2117
        xml = ParseXMLSource(cx, str);
 
2118
        if (!xml)
 
2119
            return NULL;
 
2120
        length = JSXML_LENGTH(xml);
 
2121
    }
 
2122
 
 
2123
    if (length == 0) {
 
2124
        obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
 
2125
        if (!obj)
 
2126
            return NULL;
 
2127
    } else if (length == 1) {
 
2128
        xml = OrphanXMLChild(cx, xml, 0);
 
2129
        if (!xml)
 
2130
            return NULL;
 
2131
        obj = js_GetXMLObject(cx, xml);
 
2132
        if (!obj)
 
2133
            return NULL;
 
2134
    } else {
 
2135
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
 
2136
        return NULL;
 
2137
    }
 
2138
    return obj;
 
2139
 
 
2140
bad:
 
2141
    str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
 
2142
    if (str) {
 
2143
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
2144
                             JSMSG_BAD_XML_CONVERSION,
 
2145
                             JS_GetStringBytes(str));
 
2146
    }
 
2147
    return NULL;
 
2148
}
 
2149
 
 
2150
static JSBool
 
2151
Append(JSContext *cx, JSXML *list, JSXML *kid);
 
2152
 
 
2153
static JSObject *
 
2154
ToXMLList(JSContext *cx, jsval v)
 
2155
{
 
2156
    JSObject *obj, *listobj;
 
2157
    JSXML *xml, *list, *kid;
 
2158
    JSClass *clasp;
 
2159
    JSString *str;
 
2160
    uint32 i, length;
 
2161
 
 
2162
    if (JSVAL_IS_PRIMITIVE(v)) {
 
2163
        if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
 
2164
            goto bad;
 
2165
    } else {
 
2166
        obj = JSVAL_TO_OBJECT(v);
 
2167
        if (OBJECT_IS_XML(cx, obj)) {
 
2168
            xml = (JSXML *) JS_GetPrivate(cx, obj);
 
2169
            if (xml->xml_class != JSXML_CLASS_LIST) {
 
2170
                listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
2171
                if (!listobj)
 
2172
                    return NULL;
 
2173
                list = (JSXML *) JS_GetPrivate(cx, listobj);
 
2174
                if (!Append(cx, list, xml))
 
2175
                    return NULL;
 
2176
                return listobj;
 
2177
            }
 
2178
            return obj;
 
2179
        }
 
2180
 
 
2181
        clasp = OBJ_GET_CLASS(cx, obj);
 
2182
        if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
 
2183
            JS_ASSERT(0);
 
2184
        }
 
2185
 
 
2186
        if (clasp != &js_StringClass &&
 
2187
            clasp != &js_NumberClass &&
 
2188
            clasp != &js_BooleanClass) {
 
2189
            goto bad;
 
2190
        }
 
2191
    }
 
2192
 
 
2193
    str = js_ValueToString(cx, v);
 
2194
    if (!str)
 
2195
        return NULL;
 
2196
    if (IS_EMPTY(str)) {
 
2197
        xml = NULL;
 
2198
        length = 0;
 
2199
    } else {
 
2200
        if (!js_EnterLocalRootScope(cx))
 
2201
            return NULL;
 
2202
        xml = ParseXMLSource(cx, str);
 
2203
        if (!xml) {
 
2204
            js_LeaveLocalRootScope(cx);
 
2205
            return NULL;
 
2206
        }
 
2207
        length = JSXML_LENGTH(xml);
 
2208
    }
 
2209
 
 
2210
    listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
2211
    if (listobj) {
 
2212
        list = (JSXML *) JS_GetPrivate(cx, listobj);
 
2213
        for (i = 0; i < length; i++) {
 
2214
            kid = OrphanXMLChild(cx, xml, i);
 
2215
            if (!kid || !Append(cx, list, kid)) {
 
2216
                listobj = NULL;
 
2217
                break;
 
2218
            }
 
2219
        }
 
2220
    }
 
2221
 
 
2222
    if (xml)
 
2223
        js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
 
2224
    return listobj;
 
2225
 
 
2226
bad:
 
2227
    str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
 
2228
    if (str) {
 
2229
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
2230
                             JSMSG_BAD_XMLLIST_CONVERSION,
 
2231
                             JS_GetStringBytes(str));
 
2232
    }
 
2233
    return NULL;
 
2234
}
 
2235
 
 
2236
/*
 
2237
 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
 
2238
 * and their library-public js_* counterparts.  The guts of MakeXMLCDataString,
 
2239
 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
 
2240
 * MakeXMLSpecialString subroutine.
 
2241
 *
 
2242
 * These functions take ownership of sb->base, if sb is non-null, in all cases
 
2243
 * of success or failure.
 
2244
 */
 
2245
static JSString *
 
2246
MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
 
2247
                     JSString *str, JSString *str2,
 
2248
                     const jschar *prefix, size_t prefixlength,
 
2249
                     const jschar *suffix, size_t suffixlength)
 
2250
{
 
2251
    JSStringBuffer localSB;
 
2252
    size_t length, length2, newlength;
 
2253
    jschar *bp, *base;
 
2254
 
 
2255
    if (!sb) {
 
2256
        sb = &localSB;
 
2257
        js_InitStringBuffer(sb);
 
2258
    }
 
2259
 
 
2260
    length = JSSTRING_LENGTH(str);
 
2261
    length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
 
2262
    newlength = STRING_BUFFER_OFFSET(sb) +
 
2263
                prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
 
2264
                suffixlength;
 
2265
    bp = base = (jschar *)
 
2266
                JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
 
2267
    if (!bp) {
 
2268
        js_FinishStringBuffer(sb);
 
2269
        return NULL;
 
2270
    }
 
2271
 
 
2272
    bp += STRING_BUFFER_OFFSET(sb);
 
2273
    js_strncpy(bp, prefix, prefixlength);
 
2274
    bp += prefixlength;
 
2275
    js_strncpy(bp, JSSTRING_CHARS(str), length);
 
2276
    bp += length;
 
2277
    if (length2 != 0) {
 
2278
        *bp++ = (jschar) ' ';
 
2279
        js_strncpy(bp, JSSTRING_CHARS(str2), length2);
 
2280
        bp += length2;
 
2281
    }
 
2282
    js_strncpy(bp, suffix, suffixlength);
 
2283
    bp[suffixlength] = 0;
 
2284
 
 
2285
    str = js_NewString(cx, base, newlength, 0);
 
2286
    if (!str)
 
2287
        free(base);
 
2288
    return str;
 
2289
}
 
2290
 
 
2291
static JSString *
 
2292
MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
 
2293
{
 
2294
    static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
 
2295
                                                 'C', 'D', 'A', 'T', 'A',
 
2296
                                                 '['};
 
2297
    static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
 
2298
 
 
2299
    return MakeXMLSpecialString(cx, sb, str, NULL,
 
2300
                                cdata_prefix_ucNstr, 9,
 
2301
                                cdata_suffix_ucNstr, 3);
 
2302
}
 
2303
 
 
2304
static JSString *
 
2305
MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
 
2306
{
 
2307
    static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
 
2308
    static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
 
2309
 
 
2310
    return MakeXMLSpecialString(cx, sb, str, NULL,
 
2311
                                comment_prefix_ucNstr, 4,
 
2312
                                comment_suffix_ucNstr, 3);
 
2313
}
 
2314
 
 
2315
static JSString *
 
2316
MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
 
2317
                JSString *value)
 
2318
{
 
2319
    static const jschar pi_prefix_ucNstr[] = {'<', '?'};
 
2320
    static const jschar pi_suffix_ucNstr[] = {'?', '>'};
 
2321
 
 
2322
    return MakeXMLSpecialString(cx, sb, name, value,
 
2323
                                pi_prefix_ucNstr, 2,
 
2324
                                pi_suffix_ucNstr, 2);
 
2325
}
 
2326
 
 
2327
/*
 
2328
 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
 
2329
 * equals, a double quote, an attribute value, and a closing double quote.
 
2330
 */
 
2331
static void
 
2332
AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
 
2333
{
 
2334
    js_AppendCString(sb, "=\"");
 
2335
    valstr = js_EscapeAttributeValue(cx, valstr);
 
2336
    if (!valstr) {
 
2337
        free(sb->base);
 
2338
        sb->base = STRING_BUFFER_ERROR_BASE;
 
2339
        return;
 
2340
    }
 
2341
    js_AppendJSString(sb, valstr);
 
2342
    js_AppendChar(sb, '"');
 
2343
}
 
2344
 
 
2345
/*
 
2346
 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
 
2347
 *
 
2348
 * This function takes ownership of sb->base, if sb is non-null, in all cases
 
2349
 * of success or failure.
 
2350
 */
 
2351
static JSString *
 
2352
EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
 
2353
{
 
2354
    size_t length, newlength;
 
2355
    const jschar *cp, *start, *end;
 
2356
    jschar c;
 
2357
 
 
2358
    length = newlength = JSSTRING_LENGTH(str);
 
2359
    for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
 
2360
        c = *cp;
 
2361
        if (c == '<' || c == '>')
 
2362
            newlength += 3;
 
2363
        else if (c == '&')
 
2364
            newlength += 4;
 
2365
 
 
2366
        if (newlength < length) {
 
2367
            JS_ReportOutOfMemory(cx);
 
2368
            return NULL;
 
2369
        }
 
2370
    }
 
2371
    if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
 
2372
        JSStringBuffer localSB;
 
2373
        if (!sb) {
 
2374
            sb = &localSB;
 
2375
            js_InitStringBuffer(sb);
 
2376
        }
 
2377
        if (!sb->grow(sb, newlength)) {
 
2378
            JS_ReportOutOfMemory(cx);
 
2379
            return NULL;
 
2380
        }
 
2381
        for (cp = start; cp < end; cp++) {
 
2382
            c = *cp;
 
2383
            if (c == '<')
 
2384
                js_AppendCString(sb, js_lt_entity_str);
 
2385
            else if (c == '>')
 
2386
                js_AppendCString(sb, js_gt_entity_str);
 
2387
            else if (c == '&')
 
2388
                js_AppendCString(sb, js_amp_entity_str);
 
2389
            else
 
2390
                js_AppendChar(sb, c);
 
2391
        }
 
2392
        JS_ASSERT(STRING_BUFFER_OK(sb));
 
2393
        str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
 
2394
        if (!str)
 
2395
            js_FinishStringBuffer(sb);
 
2396
    }
 
2397
    return str;
 
2398
}
 
2399
 
 
2400
/*
 
2401
 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
 
2402
 * This function takes ownership of sb->base, if sb is non-null, in all cases.
 
2403
 */
 
2404
static JSString *
 
2405
EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
 
2406
{
 
2407
    size_t length, newlength;
 
2408
    const jschar *cp, *start, *end;
 
2409
    jschar c;
 
2410
 
 
2411
    length = newlength = JSSTRING_LENGTH(str);
 
2412
    for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
 
2413
        c = *cp;
 
2414
        if (c == '"')
 
2415
            newlength += 5;
 
2416
        else if (c == '<')
 
2417
            newlength += 3;
 
2418
        else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
 
2419
            newlength += 4;
 
2420
 
 
2421
        if (newlength < length) {
 
2422
            JS_ReportOutOfMemory(cx);
 
2423
            return NULL;
 
2424
        }
 
2425
    }
 
2426
    if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
 
2427
        JSStringBuffer localSB;
 
2428
        if (!sb) {
 
2429
            sb = &localSB;
 
2430
            js_InitStringBuffer(sb);
 
2431
        }
 
2432
        if (!sb->grow(sb, newlength)) {
 
2433
            JS_ReportOutOfMemory(cx);
 
2434
            return NULL;
 
2435
        }
 
2436
        for (cp = start; cp < end; cp++) {
 
2437
            c = *cp;
 
2438
            if (c == '"')
 
2439
                js_AppendCString(sb, js_quot_entity_str);
 
2440
            else if (c == '<')
 
2441
                js_AppendCString(sb, js_lt_entity_str);
 
2442
            else if (c == '&')
 
2443
                js_AppendCString(sb, js_amp_entity_str);
 
2444
            else if (c == '\n')
 
2445
                js_AppendCString(sb, "&#xA;");
 
2446
            else if (c == '\r')
 
2447
                js_AppendCString(sb, "&#xD;");
 
2448
            else if (c == '\t')
 
2449
                js_AppendCString(sb, "&#x9;");
 
2450
            else
 
2451
                js_AppendChar(sb, c);
 
2452
        }
 
2453
        JS_ASSERT(STRING_BUFFER_OK(sb));
 
2454
        str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
 
2455
        if (!str)
 
2456
            js_FinishStringBuffer(sb);
 
2457
    }
 
2458
    return str;
 
2459
}
 
2460
 
 
2461
/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
 
2462
static JSXMLNamespace *
 
2463
GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes)
 
2464
{
 
2465
    JSXMLNamespace *match, *ns;
 
2466
    uint32 i, n;
 
2467
    jsval argv[2];
 
2468
    JSObject *nsobj;
 
2469
 
 
2470
    JS_ASSERT(qn->uri);
 
2471
    if (!qn->uri) {
 
2472
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
2473
                             JSMSG_BAD_XML_NAMESPACE,
 
2474
                             qn->prefix
 
2475
                             ? js_ValueToPrintableString(cx,
 
2476
                                   STRING_TO_JSVAL(qn->prefix))
 
2477
                             : js_type_strs[JSTYPE_VOID]);
 
2478
        return NULL;
 
2479
    }
 
2480
 
 
2481
    /* Look for a matching namespace in inScopeNSes, if provided. */
 
2482
    match = NULL;
 
2483
    if (inScopeNSes) {
 
2484
        for (i = 0, n = inScopeNSes->length; i < n; i++) {
 
2485
            ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace);
 
2486
            if (!ns)
 
2487
                continue;
 
2488
 
 
2489
            /*
 
2490
             * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
 
2491
             * If we preserve prefixes, we must match null qn->prefix against
 
2492
             * an empty ns->prefix, in order to avoid generating redundant
 
2493
             * prefixed and default namespaces for cases such as:
 
2494
             *
 
2495
             *   x = <t xmlns="http://foo.com"/>
 
2496
             *   print(x.toXMLString());
 
2497
             *
 
2498
             * Per 10.3.2.1, the namespace attribute in t has an empty string
 
2499
             * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
 
2500
             *
 
2501
             *   1. If the [local name] property of a is "xmlns"
 
2502
             *      a. Map ns.prefix to the empty string
 
2503
             *
 
2504
             * But t's name has a null prefix in this implementation, meaning
 
2505
             * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
 
2506
             * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
 
2507
             * saying how "no value" maps to an ECMA-357 value -- but it must
 
2508
             * map to the *undefined* prefix value).
 
2509
             *
 
2510
             * Since "" != undefined (or null, in the current implementation)
 
2511
             * the ECMA-357 spec will fail to match in [[GetNamespace]] called
 
2512
             * on t with argument {} U {(prefix="", uri="http://foo.com")}.
 
2513
             * This spec bug leads to ToXMLString results that duplicate the
 
2514
             * declared namespace.
 
2515
             */
 
2516
            if (js_EqualStrings(ns->uri, qn->uri) &&
 
2517
                (ns->prefix == qn->prefix ||
 
2518
                 ((ns->prefix && qn->prefix)
 
2519
                  ? js_EqualStrings(ns->prefix, qn->prefix)
 
2520
                  : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) {
 
2521
                match = ns;
 
2522
                break;
 
2523
            }
 
2524
        }
 
2525
    }
 
2526
 
 
2527
    /* If we didn't match, make a new namespace from qn. */
 
2528
    if (!match) {
 
2529
        argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID;
 
2530
        argv[1] = STRING_TO_JSVAL(qn->uri);
 
2531
        nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
 
2532
                                   2, argv);
 
2533
        if (!nsobj)
 
2534
            return NULL;
 
2535
        match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
 
2536
    }
 
2537
    return match;
 
2538
}
 
2539
 
 
2540
static JSString *
 
2541
GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
 
2542
{
 
2543
    const jschar *cp, *start, *end;
 
2544
    size_t length, newlength, offset;
 
2545
    uint32 i, n, m, serial;
 
2546
    jschar *bp, *dp;
 
2547
    JSBool done;
 
2548
    JSXMLNamespace *ns;
 
2549
    JSString *prefix;
 
2550
 
 
2551
    JS_ASSERT(!IS_EMPTY(uri));
 
2552
 
 
2553
    /*
 
2554
     * If there are no *declared* namespaces, skip all collision detection and
 
2555
     * return a short prefix quickly; an example of such a situation:
 
2556
     *
 
2557
     *   var x = <f/>;
 
2558
     *   var n = new Namespace("http://example.com/");
 
2559
     *   x.@n::att = "val";
 
2560
     *   x.toXMLString();
 
2561
     *
 
2562
     * This is necessary for various log10 uses below to be valid.
 
2563
     */
 
2564
    if (decls->length == 0)
 
2565
        return JS_NewStringCopyZ(cx, "a");
 
2566
 
 
2567
    /*
 
2568
     * Try peeling off the last filename suffix or pathname component till
 
2569
     * we have a valid XML name.  This heuristic will prefer "xul" given
 
2570
     * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
 
2571
     * likely URI of the form ".../xbl2/2005".
 
2572
     */
 
2573
    start = JSSTRING_CHARS(uri);
 
2574
    cp = end = start + JSSTRING_LENGTH(uri);
 
2575
    while (--cp > start) {
 
2576
        if (*cp == '.' || *cp == '/' || *cp == ':') {
 
2577
            ++cp;
 
2578
            length = PTRDIFF(end, cp, jschar);
 
2579
            if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
 
2580
                break;
 
2581
            end = --cp;
 
2582
        }
 
2583
    }
 
2584
    length = PTRDIFF(end, cp, jschar);
 
2585
 
 
2586
    /*
 
2587
     * If the namespace consisted only of non-XML names or names that begin
 
2588
     * case-insensitively with "xml", arbitrarily create a prefix consisting
 
2589
     * of 'a's of size length (allowing dp-calculating code to work with or
 
2590
     * without this branch executing) plus the space for storing a hyphen and
 
2591
     * the serial number (avoiding reallocation if a collision happens).
 
2592
     */
 
2593
    bp = (jschar *) cp;
 
2594
    newlength = length;
 
2595
    if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
 
2596
        newlength = length + 2 + (size_t) log10(decls->length);
 
2597
        bp = (jschar *)
 
2598
             JS_malloc(cx, (newlength + 1) * sizeof(jschar));
 
2599
        if (!bp)
 
2600
            return NULL;
 
2601
 
 
2602
        bp[newlength] = 0;
 
2603
        for (i = 0; i < newlength; i++)
 
2604
             bp[i] = 'a';
 
2605
    }
 
2606
 
 
2607
    /*
 
2608
     * Now search through decls looking for a collision.  If we collide with
 
2609
     * an existing prefix, start tacking on a hyphen and a serial number.
 
2610
     */
 
2611
    serial = 0;
 
2612
    do {
 
2613
        done = JS_TRUE;
 
2614
        for (i = 0, n = decls->length; i < n; i++) {
 
2615
            ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace);
 
2616
            if (ns && ns->prefix &&
 
2617
                JSSTRING_LENGTH(ns->prefix) == newlength &&
 
2618
                !memcmp(JSSTRING_CHARS(ns->prefix), bp,
 
2619
                        newlength * sizeof(jschar))) {
 
2620
                if (bp == cp) {
 
2621
                    newlength = length + 2 + (size_t) log10(n);
 
2622
                    bp = (jschar *)
 
2623
                         JS_malloc(cx, (newlength + 1) * sizeof(jschar));
 
2624
                    if (!bp)
 
2625
                        return NULL;
 
2626
                    js_strncpy(bp, cp, length);
 
2627
                }
 
2628
 
 
2629
                ++serial;
 
2630
                JS_ASSERT(serial <= n);
 
2631
                dp = bp + length + 2 + (size_t) log10(serial);
 
2632
                *dp = 0;
 
2633
                for (m = serial; m != 0; m /= 10)
 
2634
                    *--dp = (jschar)('0' + m % 10);
 
2635
                *--dp = '-';
 
2636
                JS_ASSERT(dp == bp + length);
 
2637
 
 
2638
                done = JS_FALSE;
 
2639
                break;
 
2640
            }
 
2641
        }
 
2642
    } while (!done);
 
2643
 
 
2644
    if (bp == cp) {
 
2645
        offset = PTRDIFF(cp, start, jschar);
 
2646
        prefix = js_NewDependentString(cx, uri, offset, length, 0);
 
2647
    } else {
 
2648
        prefix = js_NewString(cx, bp, newlength, 0);
 
2649
        if (!prefix)
 
2650
            JS_free(cx, bp);
 
2651
    }
 
2652
    return prefix;
 
2653
}
 
2654
 
 
2655
static JSBool
 
2656
namespace_match(const void *a, const void *b)
 
2657
{
 
2658
    const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
 
2659
    const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
 
2660
 
 
2661
    if (nsb->prefix)
 
2662
        return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix);
 
2663
    return js_EqualStrings(nsa->uri, nsb->uri);
 
2664
}
 
2665
 
 
2666
/* ECMA-357 10.2.1 and 10.2.2 */
 
2667
static JSString *
 
2668
XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
 
2669
               uintN indentLevel)
 
2670
{
 
2671
    JSBool pretty, indentKids;
 
2672
    JSStringBuffer sb;
 
2673
    JSString *str, *prefix, *kidstr;
 
2674
    JSXMLArrayCursor cursor;
 
2675
    uint32 i, n;
 
2676
    JSXMLArray empty, decls, ancdecls;
 
2677
    JSXMLNamespace *ns, *ns2;
 
2678
    uintN nextIndentLevel;
 
2679
    JSXML *attr, *kid;
 
2680
 
 
2681
    if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
 
2682
        return NULL;
 
2683
 
 
2684
    js_InitStringBuffer(&sb);
 
2685
    if (pretty)
 
2686
        js_RepeatChar(&sb, ' ', indentLevel);
 
2687
    str = NULL;
 
2688
 
 
2689
    switch (xml->xml_class) {
 
2690
      case JSXML_CLASS_TEXT:
 
2691
        /* Step 4. */
 
2692
        if (pretty) {
 
2693
            str = ChompXMLWhitespace(cx, xml->xml_value);
 
2694
            if (!str)
 
2695
                return NULL;
 
2696
        } else {
 
2697
            str = xml->xml_value;
 
2698
        }
 
2699
        return EscapeElementValue(cx, &sb, str);
 
2700
 
 
2701
      case JSXML_CLASS_ATTRIBUTE:
 
2702
        /* Step 5. */
 
2703
        return EscapeAttributeValue(cx, &sb, xml->xml_value);
 
2704
 
 
2705
      case JSXML_CLASS_COMMENT:
 
2706
        /* Step 6. */
 
2707
        return MakeXMLCommentString(cx, &sb, xml->xml_value);
 
2708
 
 
2709
      case JSXML_CLASS_PROCESSING_INSTRUCTION:
 
2710
        /* Step 7. */
 
2711
        return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value);
 
2712
 
 
2713
      case JSXML_CLASS_LIST:
 
2714
        /* ECMA-357 10.2.2. */
 
2715
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
2716
        i = 0;
 
2717
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
2718
            if (pretty && i != 0)
 
2719
                js_AppendChar(&sb, '\n');
 
2720
 
 
2721
            kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
 
2722
            if (!kidstr)
 
2723
                break;
 
2724
 
 
2725
            js_AppendJSString(&sb, kidstr);
 
2726
            ++i;
 
2727
        }
 
2728
        XMLArrayCursorFinish(&cursor);
 
2729
        if (kid)
 
2730
            goto list_out;
 
2731
 
 
2732
        if (!sb.base) {
 
2733
            if (!STRING_BUFFER_OK(&sb)) {
 
2734
                JS_ReportOutOfMemory(cx);
 
2735
                return NULL;
 
2736
            }
 
2737
            return cx->runtime->emptyString;
 
2738
        }
 
2739
 
 
2740
        str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
 
2741
      list_out:
 
2742
        if (!str)
 
2743
            js_FinishStringBuffer(&sb);
 
2744
        return str;
 
2745
 
 
2746
      default:;
 
2747
    }
 
2748
 
 
2749
    /* After this point, control must flow through label out: to exit. */
 
2750
    if (!js_EnterLocalRootScope(cx))
 
2751
        return NULL;
 
2752
 
 
2753
    /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
 
2754
    if (!ancestorNSes) {
 
2755
        XMLArrayInit(cx, &empty, 0);
 
2756
        ancestorNSes = &empty;
 
2757
    }
 
2758
    XMLArrayInit(cx, &decls, 0);
 
2759
    ancdecls.capacity = 0;
 
2760
 
 
2761
    /* Clone in-scope namespaces not in ancestorNSes into decls. */
 
2762
    XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
 
2763
    while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
 
2764
        if (!ns->declared)
 
2765
            continue;
 
2766
        if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
 
2767
            /* NOTE: may want to exclude unused namespaces here. */
 
2768
            ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE);
 
2769
            if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2))
 
2770
                break;
 
2771
        }
 
2772
    }
 
2773
    XMLArrayCursorFinish(&cursor);
 
2774
    if (ns)
 
2775
        goto out;
 
2776
 
 
2777
    /*
 
2778
     * Union ancestorNSes and decls into ancdecls.  Note that ancdecls does
 
2779
     * not own its member references.  In the spec, ancdecls has no name, but
 
2780
     * is always written out as (AncestorNamespaces U namespaceDeclarations).
 
2781
     */
 
2782
    if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
 
2783
        goto out;
 
2784
    for (i = 0, n = ancestorNSes->length; i < n; i++) {
 
2785
        ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace);
 
2786
        if (!ns2)
 
2787
            continue;
 
2788
        JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
 
2789
        if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
 
2790
            goto out;
 
2791
    }
 
2792
    for (i = 0, n = decls.length; i < n; i++) {
 
2793
        ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace);
 
2794
        if (!ns2)
 
2795
            continue;
 
2796
        JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
 
2797
        if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
 
2798
            goto out;
 
2799
    }
 
2800
 
 
2801
    /* Step 11, except we don't clone ns unless its prefix is undefined. */
 
2802
    ns = GetNamespace(cx, xml->name, &ancdecls);
 
2803
    if (!ns)
 
2804
        goto out;
 
2805
 
 
2806
    /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
 
2807
    if (!ns->prefix) {
 
2808
        /*
 
2809
         * Create a namespace prefix that isn't used by any member of decls.
 
2810
         * Assign the new prefix to a copy of ns.  Flag this namespace as if
 
2811
         * it were declared, for assertion-testing's sake later below.
 
2812
         *
 
2813
         * Erratum: if ns->prefix and xml->name are both null (*undefined* in
 
2814
         * ECMA-357), we know that xml was named using the default namespace
 
2815
         * (proof: see GetNamespace and the Namespace constructor called with
 
2816
         * two arguments).  So we ought not generate a new prefix here, when
 
2817
         * we can declare ns as the default namespace for xml.
 
2818
         *
 
2819
         * This helps descendants inherit the namespace instead of redundantly
 
2820
         * redeclaring it with generated prefixes in each descendant.
 
2821
         */
 
2822
        if (!xml->name->prefix) {
 
2823
            prefix = cx->runtime->emptyString;
 
2824
        } else {
 
2825
            prefix = GeneratePrefix(cx, ns->uri, &ancdecls);
 
2826
            if (!prefix)
 
2827
                goto out;
 
2828
        }
 
2829
        ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE);
 
2830
        if (!ns)
 
2831
            goto out;
 
2832
 
 
2833
        /*
 
2834
         * If the xml->name was unprefixed, we must remove any declared default
 
2835
         * namespace from decls before appending ns.  How can you get a default
 
2836
         * namespace in decls that doesn't match the one from name?  Apparently
 
2837
         * by calling x.setNamespace(ns) where ns has no prefix.  The other way
 
2838
         * to fix this is to update x's in-scope namespaces when setNamespace
 
2839
         * is called, but that's not specified by ECMA-357.
 
2840
         *
 
2841
         * Likely Erratum here, depending on whether the lack of update to x's
 
2842
         * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
 
2843
         * erratum or not.  Note that changing setNamespace to update the list
 
2844
         * of in-scope namespaces will change x.namespaceDeclarations().
 
2845
         */
 
2846
        if (IS_EMPTY(prefix)) {
 
2847
            i = XMLArrayFindMember(&decls, ns, namespace_match);
 
2848
            if (i != XML_NOT_FOUND)
 
2849
                XMLArrayDelete(cx, &decls, i, JS_TRUE);
 
2850
        }
 
2851
 
 
2852
        /*
 
2853
         * In the spec, ancdecls has no name, but is always written out as
 
2854
         * (AncestorNamespaces U namespaceDeclarations).  Since we compute
 
2855
         * that union in ancdecls, any time we append a namespace strong
 
2856
         * ref to decls, we must also append a weak ref to ancdecls.  Order
 
2857
         * matters here: code at label out: releases strong refs in decls.
 
2858
         */
 
2859
        if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
 
2860
            !XMLARRAY_APPEND(cx, &decls, ns)) {
 
2861
            goto out;
 
2862
        }
 
2863
    }
 
2864
 
 
2865
    /* Format the element or point-tag into sb. */
 
2866
    js_AppendChar(&sb, '<');
 
2867
 
 
2868
    if (ns->prefix && !IS_EMPTY(ns->prefix)) {
 
2869
        js_AppendJSString(&sb, ns->prefix);
 
2870
        js_AppendChar(&sb, ':');
 
2871
    }
 
2872
    js_AppendJSString(&sb, xml->name->localName);
 
2873
 
 
2874
    /*
 
2875
     * Step 16 makes a union to avoid writing two loops in step 17, to share
 
2876
     * common attribute value appending spec-code.  We prefer two loops for
 
2877
     * faster code and less data overhead.
 
2878
     */
 
2879
 
 
2880
    /* Step 17(b): append attributes. */
 
2881
    XMLArrayCursorInit(&cursor, &xml->xml_attrs);
 
2882
    while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
2883
        js_AppendChar(&sb, ' ');
 
2884
        ns2 = GetNamespace(cx, attr->name, &ancdecls);
 
2885
        if (!ns2)
 
2886
            break;
 
2887
 
 
2888
        /* 17(b)(ii): NULL means *undefined* here. */
 
2889
        if (!ns2->prefix) {
 
2890
            prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
 
2891
            if (!prefix)
 
2892
                break;
 
2893
 
 
2894
            /* Again, we avoid copying ns2 until we know it's prefix-less. */
 
2895
            ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE);
 
2896
            if (!ns2)
 
2897
                break;
 
2898
 
 
2899
            /*
 
2900
             * In the spec, ancdecls has no name, but is always written out as
 
2901
             * (AncestorNamespaces U namespaceDeclarations).  Since we compute
 
2902
             * that union in ancdecls, any time we append a namespace strong
 
2903
             * ref to decls, we must also append a weak ref to ancdecls.  Order
 
2904
             * matters here: code at label out: releases strong refs in decls.
 
2905
             */
 
2906
            if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
 
2907
                !XMLARRAY_APPEND(cx, &decls, ns2)) {
 
2908
                break;
 
2909
            }
 
2910
        }
 
2911
 
 
2912
        /* 17(b)(iii). */
 
2913
        if (!IS_EMPTY(ns2->prefix)) {
 
2914
            js_AppendJSString(&sb, ns2->prefix);
 
2915
            js_AppendChar(&sb, ':');
 
2916
        }
 
2917
 
 
2918
        /* 17(b)(iv). */
 
2919
        js_AppendJSString(&sb, attr->name->localName);
 
2920
 
 
2921
        /* 17(d-g). */
 
2922
        AppendAttributeValue(cx, &sb, attr->xml_value);
 
2923
    }
 
2924
    XMLArrayCursorFinish(&cursor);
 
2925
    if (attr)
 
2926
        goto out;
 
2927
 
 
2928
    /* Step 17(c): append XML namespace declarations. */
 
2929
    XMLArrayCursorInit(&cursor, &decls);
 
2930
    while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
 
2931
        JS_ASSERT(ns2->declared);
 
2932
 
 
2933
        js_AppendCString(&sb, " xmlns");
 
2934
 
 
2935
        /* 17(c)(ii): NULL means *undefined* here. */
 
2936
        if (!ns2->prefix) {
 
2937
            prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
 
2938
            if (!prefix)
 
2939
                break;
 
2940
            ns2->prefix = prefix;
 
2941
        }
 
2942
 
 
2943
        /* 17(c)(iii). */
 
2944
        if (!IS_EMPTY(ns2->prefix)) {
 
2945
            js_AppendChar(&sb, ':');
 
2946
            js_AppendJSString(&sb, ns2->prefix);
 
2947
        }
 
2948
 
 
2949
        /* 17(d-g). */
 
2950
        AppendAttributeValue(cx, &sb, ns2->uri);
 
2951
    }
 
2952
    XMLArrayCursorFinish(&cursor);
 
2953
    if (ns2)
 
2954
        goto out;
 
2955
 
 
2956
    /* Step 18: handle point tags. */
 
2957
    n = xml->xml_kids.length;
 
2958
    if (n == 0) {
 
2959
        js_AppendCString(&sb, "/>");
 
2960
    } else {
 
2961
        /* Steps 19 through 25: handle element content, and open the end-tag. */
 
2962
        js_AppendChar(&sb, '>');
 
2963
        indentKids = n > 1 ||
 
2964
                     (n == 1 &&
 
2965
                      (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
 
2966
                      kid->xml_class != JSXML_CLASS_TEXT);
 
2967
 
 
2968
        if (pretty && indentKids) {
 
2969
            if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
 
2970
                goto out;
 
2971
            nextIndentLevel = indentLevel + i;
 
2972
        } else {
 
2973
            nextIndentLevel = 0;
 
2974
        }
 
2975
 
 
2976
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
2977
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
2978
            if (pretty && indentKids)
 
2979
                js_AppendChar(&sb, '\n');
 
2980
 
 
2981
            kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
 
2982
            if (!kidstr)
 
2983
                break;
 
2984
 
 
2985
            js_AppendJSString(&sb, kidstr);
 
2986
        }
 
2987
        XMLArrayCursorFinish(&cursor);
 
2988
        if (kid)
 
2989
            goto out;
 
2990
 
 
2991
        if (pretty && indentKids) {
 
2992
            js_AppendChar(&sb, '\n');
 
2993
            js_RepeatChar(&sb, ' ', indentLevel);
 
2994
        }
 
2995
        js_AppendCString(&sb, "</");
 
2996
 
 
2997
        /* Step 26. */
 
2998
        if (ns->prefix && !IS_EMPTY(ns->prefix)) {
 
2999
            js_AppendJSString(&sb, ns->prefix);
 
3000
            js_AppendChar(&sb, ':');
 
3001
        }
 
3002
 
 
3003
        /* Step 27. */
 
3004
        js_AppendJSString(&sb, xml->name->localName);
 
3005
        js_AppendChar(&sb, '>');
 
3006
    }
 
3007
 
 
3008
    if (!STRING_BUFFER_OK(&sb)) {
 
3009
        JS_ReportOutOfMemory(cx);
 
3010
        goto out;
 
3011
    }
 
3012
 
 
3013
    str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
 
3014
out:
 
3015
    js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
 
3016
    if (!str && STRING_BUFFER_OK(&sb))
 
3017
        js_FinishStringBuffer(&sb);
 
3018
    XMLArrayFinish(cx, &decls);
 
3019
    if (ancdecls.capacity != 0)
 
3020
        XMLArrayFinish(cx, &ancdecls);
 
3021
    return str;
 
3022
}
 
3023
 
 
3024
/* ECMA-357 10.2 */
 
3025
static JSString *
 
3026
ToXMLString(JSContext *cx, jsval v)
 
3027
{
 
3028
    JSObject *obj;
 
3029
    JSString *str;
 
3030
    JSXML *xml;
 
3031
 
 
3032
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
 
3033
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3034
                             JSMSG_BAD_XML_CONVERSION,
 
3035
                             js_type_strs[JSVAL_IS_NULL(v)
 
3036
                                          ? JSTYPE_NULL
 
3037
                                          : JSTYPE_VOID]);
 
3038
        return NULL;
 
3039
    }
 
3040
 
 
3041
    if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
 
3042
        return js_ValueToString(cx, v);
 
3043
 
 
3044
    if (JSVAL_IS_STRING(v))
 
3045
        return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
 
3046
 
 
3047
    obj = JSVAL_TO_OBJECT(v);
 
3048
    if (!OBJECT_IS_XML(cx, obj)) {
 
3049
        if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
 
3050
            return NULL;
 
3051
        str = js_ValueToString(cx, v);
 
3052
        if (!str)
 
3053
            return NULL;
 
3054
        return EscapeElementValue(cx, NULL, str);
 
3055
    }
 
3056
 
 
3057
    /* Handle non-element cases in this switch, returning from each case. */
 
3058
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
3059
    return XMLToXMLString(cx, xml, NULL, 0);
 
3060
}
 
3061
 
 
3062
static JSXMLQName *
 
3063
ToAttributeName(JSContext *cx, jsval v)
 
3064
{
 
3065
    JSString *name, *uri, *prefix;
 
3066
    JSObject *obj;
 
3067
    JSClass *clasp;
 
3068
    JSXMLQName *qn;
 
3069
    JSTempValueRooter tvr;
 
3070
 
 
3071
    if (JSVAL_IS_STRING(v)) {
 
3072
        name = JSVAL_TO_STRING(v);
 
3073
        uri = prefix = cx->runtime->emptyString;
 
3074
    } else {
 
3075
        if (JSVAL_IS_PRIMITIVE(v)) {
 
3076
            name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
 
3077
            if (name) {
 
3078
                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3079
                                     JSMSG_BAD_XML_ATTR_NAME,
 
3080
                                     JS_GetStringBytes(name));
 
3081
            }
 
3082
            return NULL;
 
3083
        }
 
3084
 
 
3085
        obj = JSVAL_TO_OBJECT(v);
 
3086
        clasp = OBJ_GET_CLASS(cx, obj);
 
3087
        if (clasp == &js_AttributeNameClass)
 
3088
            return (JSXMLQName *) JS_GetPrivate(cx, obj);
 
3089
 
 
3090
        if (clasp == &js_QNameClass.base) {
 
3091
            qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
 
3092
            uri = qn->uri;
 
3093
            prefix = qn->prefix;
 
3094
            name = qn->localName;
 
3095
        } else {
 
3096
            if (clasp == &js_AnyNameClass) {
 
3097
                name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
 
3098
            } else {
 
3099
                name = js_ValueToString(cx, v);
 
3100
                if (!name)
 
3101
                    return NULL;
 
3102
            }
 
3103
            uri = prefix = cx->runtime->emptyString;
 
3104
        }
 
3105
    }
 
3106
 
 
3107
    qn = js_NewXMLQName(cx, uri, prefix, name);
 
3108
    if (!qn)
 
3109
        return NULL;
 
3110
 
 
3111
    JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr);
 
3112
    obj = js_GetAttributeNameObject(cx, qn);
 
3113
    JS_POP_TEMP_ROOT(cx, &tvr);
 
3114
    if (!obj)
 
3115
        return NULL;
 
3116
    return qn;
 
3117
}
 
3118
 
 
3119
static JSXMLQName *
 
3120
ToXMLName(JSContext *cx, jsval v, jsid *funidp)
 
3121
{
 
3122
    JSString *name;
 
3123
    JSObject *obj;
 
3124
    JSClass *clasp;
 
3125
    uint32 index;
 
3126
    JSXMLQName *qn;
 
3127
    JSAtom *atom;
 
3128
 
 
3129
    if (JSVAL_IS_STRING(v)) {
 
3130
        name = JSVAL_TO_STRING(v);
 
3131
    } else {
 
3132
        if (JSVAL_IS_PRIMITIVE(v)) {
 
3133
            name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
 
3134
            if (name)
 
3135
                goto bad;
 
3136
            return NULL;
 
3137
        }
 
3138
 
 
3139
        obj = JSVAL_TO_OBJECT(v);
 
3140
        clasp = OBJ_GET_CLASS(cx, obj);
 
3141
        if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
 
3142
            goto out;
 
3143
        if (clasp == &js_AnyNameClass) {
 
3144
            name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
 
3145
            goto construct;
 
3146
        }
 
3147
        name = js_ValueToString(cx, v);
 
3148
        if (!name)
 
3149
            return NULL;
 
3150
    }
 
3151
 
 
3152
    /*
 
3153
     * ECMA-357 10.6.1 step 1 seems to be incorrect.  The spec says:
 
3154
     *
 
3155
     * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
 
3156
     *
 
3157
     * First, _P_ should be _s_, to refer to the given string.
 
3158
     *
 
3159
     * Second, why does ToXMLName applied to the string type throw TypeError
 
3160
     * only for numeric literals without any leading or trailing whitespace?
 
3161
     *
 
3162
     * If the idea is to reject uint32 property names, then the check needs to
 
3163
     * be stricter, to exclude hexadecimal and floating point literals.
 
3164
     */
 
3165
    if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
 
3166
        goto bad;
 
3167
 
 
3168
    if (*JSSTRING_CHARS(name) == '@') {
 
3169
        name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0);
 
3170
        if (!name)
 
3171
            return NULL;
 
3172
        *funidp = 0;
 
3173
        return ToAttributeName(cx, STRING_TO_JSVAL(name));
 
3174
    }
 
3175
 
 
3176
construct:
 
3177
    v = STRING_TO_JSVAL(name);
 
3178
    obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
 
3179
    if (!obj)
 
3180
        return NULL;
 
3181
 
 
3182
out:
 
3183
    qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
 
3184
    atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom;
 
3185
    if (qn->uri && atom &&
 
3186
        (qn->uri == ATOM_TO_STRING(atom) ||
 
3187
         js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) {
 
3188
        if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp))
 
3189
            return NULL;
 
3190
    } else {
 
3191
        *funidp = 0;
 
3192
    }
 
3193
    return qn;
 
3194
 
 
3195
bad:
 
3196
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3197
                         JSMSG_BAD_XML_NAME,
 
3198
                         js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
 
3199
    return NULL;
 
3200
}
 
3201
 
 
3202
/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
 
3203
static JSBool
 
3204
AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
 
3205
{
 
3206
    JSXMLNamespace *match, *ns2;
 
3207
    uint32 i, n, m;
 
3208
 
 
3209
    if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
3210
        return JS_TRUE;
 
3211
 
 
3212
    /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
 
3213
    if (!ns->prefix) {
 
3214
        match = NULL;
 
3215
        for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
 
3216
            ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
 
3217
            if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) {
 
3218
                match = ns2;
 
3219
                break;
 
3220
            }
 
3221
        }
 
3222
        if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
 
3223
            return JS_FALSE;
 
3224
    } else {
 
3225
        if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri))
 
3226
            return JS_TRUE;
 
3227
        match = NULL;
 
3228
#ifdef __GNUC__         /* suppress bogus gcc warnings */
 
3229
        m = XML_NOT_FOUND;
 
3230
#endif
 
3231
        for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
 
3232
            ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
 
3233
            if (ns2 && ns2->prefix &&
 
3234
                js_EqualStrings(ns2->prefix, ns->prefix)) {
 
3235
                match = ns2;
 
3236
                m = i;
 
3237
                break;
 
3238
            }
 
3239
        }
 
3240
        if (match && !js_EqualStrings(match->uri, ns->uri)) {
 
3241
            ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
 
3242
                                  JSXMLNamespace);
 
3243
            JS_ASSERT(ns2 == match);
 
3244
            match->prefix = NULL;
 
3245
            if (!AddInScopeNamespace(cx, xml, match))
 
3246
                return JS_FALSE;
 
3247
        }
 
3248
        if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
 
3249
            return JS_FALSE;
 
3250
    }
 
3251
 
 
3252
    /* OPTION: enforce that descendants have superset namespaces. */
 
3253
    return JS_TRUE;
 
3254
}
 
3255
 
 
3256
/* ECMA-357 9.2.1.6 XMLList [[Append]]. */
 
3257
static JSBool
 
3258
Append(JSContext *cx, JSXML *list, JSXML *xml)
 
3259
{
 
3260
    uint32 i, j, k, n;
 
3261
    JSXML *kid;
 
3262
 
 
3263
    JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
 
3264
    i = list->xml_kids.length;
 
3265
    n = 1;
 
3266
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
3267
        list->xml_target = xml->xml_target;
 
3268
        list->xml_targetprop = xml->xml_targetprop;
 
3269
        n = JSXML_LENGTH(xml);
 
3270
        k = i + n;
 
3271
        if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
 
3272
            return JS_FALSE;
 
3273
        for (j = 0; j < n; j++) {
 
3274
            kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
 
3275
            if (kid)
 
3276
                XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
 
3277
        }
 
3278
        return JS_TRUE;
 
3279
    }
 
3280
 
 
3281
    list->xml_target = xml->parent;
 
3282
    if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
 
3283
        list->xml_targetprop = NULL;
 
3284
    else
 
3285
        list->xml_targetprop = xml->name;
 
3286
    if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
 
3287
        return JS_FALSE;
 
3288
    return JS_TRUE;
 
3289
}
 
3290
 
 
3291
/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
 
3292
static JSXML *
 
3293
DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
 
3294
 
 
3295
static JSXML *
 
3296
DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
 
3297
{
 
3298
    JSXML *copy;
 
3299
    JSBool ok;
 
3300
 
 
3301
    /* Our caller may not be protecting newborns with a local root scope. */
 
3302
    if (!js_EnterLocalRootScope(cx))
 
3303
        return NULL;
 
3304
    copy = DeepCopyInLRS(cx, xml, flags);
 
3305
    if (copy) {
 
3306
        if (obj) {
 
3307
            /* Caller provided the object for this copy, hook 'em up. */
 
3308
            ok = JS_SetPrivate(cx, obj, copy);
 
3309
            if (ok)
 
3310
                copy->object = obj;
 
3311
        } else {
 
3312
            ok = js_GetXMLObject(cx, copy) != NULL;
 
3313
        }
 
3314
        if (!ok)
 
3315
            copy = NULL;
 
3316
    }
 
3317
    js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
 
3318
    return copy;
 
3319
}
 
3320
 
 
3321
/*
 
3322
 * (i) We must be in a local root scope (InLRS).
 
3323
 * (ii) parent must have a rooted object.
 
3324
 * (iii) from's owning object must be locked if not thread-local.
 
3325
 */
 
3326
static JSBool
 
3327
DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
 
3328
                 uintN flags)
 
3329
{
 
3330
    uint32 j, n;
 
3331
    JSXMLArrayCursor cursor;
 
3332
    JSBool ok;
 
3333
    JSXML *kid, *kid2;
 
3334
    JSString *str;
 
3335
 
 
3336
    JS_ASSERT(cx->localRootStack);
 
3337
 
 
3338
    n = from->length;
 
3339
    if (!XMLArraySetCapacity(cx, to, n))
 
3340
        return JS_FALSE;
 
3341
 
 
3342
    XMLArrayCursorInit(&cursor, from);
 
3343
    j = 0;
 
3344
    ok = JS_TRUE;
 
3345
    while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
3346
        if ((flags & XSF_IGNORE_COMMENTS) &&
 
3347
            kid->xml_class == JSXML_CLASS_COMMENT) {
 
3348
            continue;
 
3349
        }
 
3350
        if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
 
3351
            kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
 
3352
            continue;
 
3353
        }
 
3354
        if ((flags & XSF_IGNORE_WHITESPACE) &&
 
3355
            (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
 
3356
            continue;
 
3357
        }
 
3358
        kid2 = DeepCopyInLRS(cx, kid, flags);
 
3359
        if (!kid2) {
 
3360
            to->length = j;
 
3361
            ok = JS_FALSE;
 
3362
            break;
 
3363
        }
 
3364
 
 
3365
        if ((flags & XSF_IGNORE_WHITESPACE) &&
 
3366
            n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
 
3367
            str = ChompXMLWhitespace(cx, kid2->xml_value);
 
3368
            if (!str) {
 
3369
                to->length = j;
 
3370
                ok = JS_FALSE;
 
3371
                break;
 
3372
            }
 
3373
            kid2->xml_value = str;
 
3374
        }
 
3375
 
 
3376
        XMLARRAY_SET_MEMBER(to, j, kid2);
 
3377
        ++j;
 
3378
        if (parent->xml_class != JSXML_CLASS_LIST)
 
3379
            kid2->parent = parent;
 
3380
    }
 
3381
    XMLArrayCursorFinish(&cursor);
 
3382
    if (!ok)
 
3383
        return JS_FALSE;
 
3384
 
 
3385
    if (j < n)
 
3386
        XMLArrayTrim(to);
 
3387
    return JS_TRUE;
 
3388
}
 
3389
 
 
3390
static JSXML *
 
3391
DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
 
3392
{
 
3393
    JSXML *copy;
 
3394
    JSXMLQName *qn;
 
3395
    JSBool ok;
 
3396
    uint32 i, n;
 
3397
    JSXMLNamespace *ns, *ns2;
 
3398
 
 
3399
    /* Our caller must be protecting newborn objects. */
 
3400
    JS_ASSERT(cx->localRootStack);
 
3401
 
 
3402
    copy = js_NewXML(cx, xml->xml_class);
 
3403
    if (!copy)
 
3404
        return NULL;
 
3405
    qn = xml->name;
 
3406
    if (qn) {
 
3407
        qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
 
3408
        if (!qn) {
 
3409
            ok = JS_FALSE;
 
3410
            goto out;
 
3411
        }
 
3412
    }
 
3413
    copy->name = qn;
 
3414
    copy->xml_flags = xml->xml_flags;
 
3415
 
 
3416
    if (JSXML_HAS_VALUE(xml)) {
 
3417
        copy->xml_value = xml->xml_value;
 
3418
        ok = JS_TRUE;
 
3419
    } else {
 
3420
        ok = DeepCopySetInLRS(cx, &xml->xml_kids, &copy->xml_kids, copy, flags);
 
3421
        if (!ok)
 
3422
            goto out;
 
3423
 
 
3424
        if (xml->xml_class == JSXML_CLASS_LIST) {
 
3425
            copy->xml_target = xml->xml_target;
 
3426
            copy->xml_targetprop = xml->xml_targetprop;
 
3427
        } else {
 
3428
            n = xml->xml_namespaces.length;
 
3429
            ok = XMLArraySetCapacity(cx, &copy->xml_namespaces, n);
 
3430
            if (!ok)
 
3431
                goto out;
 
3432
            for (i = 0; i < n; i++) {
 
3433
                ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
 
3434
                if (!ns)
 
3435
                    continue;
 
3436
                ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared);
 
3437
                if (!ns2) {
 
3438
                    copy->xml_namespaces.length = i;
 
3439
                    ok = JS_FALSE;
 
3440
                    goto out;
 
3441
                }
 
3442
                XMLARRAY_SET_MEMBER(&copy->xml_namespaces, i, ns2);
 
3443
            }
 
3444
 
 
3445
            ok = DeepCopySetInLRS(cx, &xml->xml_attrs, &copy->xml_attrs, copy,
 
3446
                                  0);
 
3447
            if (!ok)
 
3448
                goto out;
 
3449
        }
 
3450
    }
 
3451
 
 
3452
out:
 
3453
    if (!ok)
 
3454
        return NULL;
 
3455
    return copy;
 
3456
}
 
3457
 
 
3458
static void
 
3459
ReportBadXMLName(JSContext *cx, jsval id)
 
3460
{
 
3461
    JSString *name;
 
3462
 
 
3463
    name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL);
 
3464
    if (name) {
 
3465
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3466
                             JSMSG_BAD_XML_NAME,
 
3467
                             JS_GetStringBytes(name));
 
3468
    }
 
3469
}
 
3470
 
 
3471
/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
 
3472
static JSBool
 
3473
DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp)
 
3474
{
 
3475
    uint32 index;
 
3476
    JSXML *kid;
 
3477
 
 
3478
    if (!js_IdIsIndex(id, &index)) {
 
3479
        ReportBadXMLName(cx, id);
 
3480
        return JS_FALSE;
 
3481
    }
 
3482
 
 
3483
    if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
 
3484
        kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
 
3485
        if (kid)
 
3486
            kid->parent = NULL;
 
3487
        XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
 
3488
    }
 
3489
 
 
3490
    *vp = JSVAL_TRUE;
 
3491
    return JS_TRUE;
 
3492
}
 
3493
 
 
3494
typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml);
 
3495
 
 
3496
static JSBool
 
3497
MatchAttrName(JSXMLQName *nameqn, JSXML *attr)
 
3498
{
 
3499
    JSXMLQName *attrqn = attr->name;
 
3500
 
 
3501
    return (IS_STAR(nameqn->localName) ||
 
3502
            js_EqualStrings(attrqn->localName, nameqn->localName)) &&
 
3503
           (!nameqn->uri ||
 
3504
            js_EqualStrings(attrqn->uri, nameqn->uri));
 
3505
}
 
3506
 
 
3507
static JSBool
 
3508
MatchElemName(JSXMLQName *nameqn, JSXML *elem)
 
3509
{
 
3510
    return (IS_STAR(nameqn->localName) ||
 
3511
            (elem->xml_class == JSXML_CLASS_ELEMENT &&
 
3512
             js_EqualStrings(elem->name->localName, nameqn->localName))) &&
 
3513
           (!nameqn->uri ||
 
3514
            (elem->xml_class == JSXML_CLASS_ELEMENT &&
 
3515
             js_EqualStrings(elem->name->uri, nameqn->uri)));
 
3516
}
 
3517
 
 
3518
/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
 
3519
static JSBool
 
3520
DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list)
 
3521
{
 
3522
    uint32 i, n;
 
3523
    JSXML *attr, *kid;
 
3524
 
 
3525
    if (xml->xml_class == JSXML_CLASS_ELEMENT &&
 
3526
        OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) {
 
3527
        for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
 
3528
            attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
 
3529
            if (attr && MatchAttrName(nameqn, attr)) {
 
3530
                if (!Append(cx, list, attr))
 
3531
                    return JS_FALSE;
 
3532
            }
 
3533
        }
 
3534
    }
 
3535
 
 
3536
    for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
3537
        kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
3538
        if (!kid)
 
3539
            continue;
 
3540
        if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass &&
 
3541
            MatchElemName(nameqn, kid)) {
 
3542
            if (!Append(cx, list, kid))
 
3543
                return JS_FALSE;
 
3544
        }
 
3545
        if (!DescendantsHelper(cx, kid, nameqn, list))
 
3546
            return JS_FALSE;
 
3547
    }
 
3548
    return JS_TRUE;
 
3549
}
 
3550
 
 
3551
static JSXML *
 
3552
Descendants(JSContext *cx, JSXML *xml, jsval id)
 
3553
{
 
3554
    jsid funid;
 
3555
    JSXMLQName *nameqn;
 
3556
    JSObject *listobj;
 
3557
    JSXML *list, *kid;
 
3558
    uint32 i, n;
 
3559
    JSBool ok;
 
3560
 
 
3561
    nameqn = ToXMLName(cx, id, &funid);
 
3562
    if (!nameqn)
 
3563
        return NULL;
 
3564
 
 
3565
    listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
3566
    if (!listobj)
 
3567
        return NULL;
 
3568
    list = (JSXML *) JS_GetPrivate(cx, listobj);
 
3569
    if (funid)
 
3570
        return list;
 
3571
 
 
3572
    /*
 
3573
     * Protect nameqn's object and strings from GC by linking list to it
 
3574
     * temporarily.  The cx->newborn[GCX_OBJECT] GC root protects listobj,
 
3575
     * which protects list.  Any other object allocations occuring beneath
 
3576
     * DescendantsHelper use local roots.
 
3577
     */
 
3578
    list->name = nameqn;
 
3579
    if (!js_EnterLocalRootScope(cx))
 
3580
        return NULL;
 
3581
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
3582
        ok = JS_TRUE;
 
3583
        for (i = 0, n = xml->xml_kids.length; i < n; i++) {
 
3584
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
3585
            if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
3586
                ok = DescendantsHelper(cx, kid, nameqn, list);
 
3587
                if (!ok)
 
3588
                    break;
 
3589
            }
 
3590
        }
 
3591
    } else {
 
3592
        ok = DescendantsHelper(cx, xml, nameqn, list);
 
3593
    }
 
3594
    js_LeaveLocalRootScopeWithResult(cx, (jsval) list);
 
3595
    if (!ok)
 
3596
        return NULL;
 
3597
    list->name = NULL;
 
3598
    return list;
 
3599
}
 
3600
 
 
3601
static JSBool
 
3602
xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
 
3603
 
 
3604
/* Recursive (JSXML *) parameterized version of Equals. */
 
3605
static JSBool
 
3606
XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
 
3607
{
 
3608
    JSXMLQName *qn, *vqn;
 
3609
    uint32 i, j, n;
 
3610
    JSXMLArrayCursor cursor, vcursor;
 
3611
    JSXML *kid, *vkid, *attr, *vattr;
 
3612
    JSBool ok;
 
3613
    JSObject *xobj, *vobj;
 
3614
 
 
3615
retry:
 
3616
    if (xml->xml_class != vxml->xml_class) {
 
3617
        if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
 
3618
            xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
3619
            if (xml)
 
3620
                goto retry;
 
3621
        }
 
3622
        if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
 
3623
            vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
 
3624
            if (vxml)
 
3625
                goto retry;
 
3626
        }
 
3627
        *bp = JS_FALSE;
 
3628
        return JS_TRUE;
 
3629
    }
 
3630
 
 
3631
    qn = xml->name;
 
3632
    vqn = vxml->name;
 
3633
    if (qn) {
 
3634
        *bp = vqn &&
 
3635
              js_EqualStrings(qn->localName, vqn->localName) &&
 
3636
              js_EqualStrings(qn->uri, vqn->uri);
 
3637
    } else {
 
3638
        *bp = vqn == NULL;
 
3639
    }
 
3640
    if (!*bp)
 
3641
        return JS_TRUE;
 
3642
 
 
3643
    if (JSXML_HAS_VALUE(xml)) {
 
3644
        *bp = js_EqualStrings(xml->xml_value, vxml->xml_value);
 
3645
    } else if (xml->xml_kids.length != vxml->xml_kids.length) {
 
3646
        *bp = JS_FALSE;
 
3647
    } else {
 
3648
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
3649
        XMLArrayCursorInit(&vcursor, &vxml->xml_kids);
 
3650
        for (;;) {
 
3651
            kid = (JSXML *) XMLArrayCursorNext(&cursor);
 
3652
            vkid = (JSXML *) XMLArrayCursorNext(&vcursor);
 
3653
            if (!kid || !vkid) {
 
3654
                *bp = !kid && !vkid;
 
3655
                ok = JS_TRUE;
 
3656
                break;
 
3657
            }
 
3658
            xobj = js_GetXMLObject(cx, kid);
 
3659
            vobj = js_GetXMLObject(cx, vkid);
 
3660
            ok = xobj && vobj &&
 
3661
                 xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp);
 
3662
            if (!ok || !*bp)
 
3663
                break;
 
3664
        }
 
3665
        XMLArrayCursorFinish(&vcursor);
 
3666
        XMLArrayCursorFinish(&cursor);
 
3667
        if (!ok)
 
3668
            return JS_FALSE;
 
3669
 
 
3670
        if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
 
3671
            n = xml->xml_attrs.length;
 
3672
            if (n != vxml->xml_attrs.length)
 
3673
                *bp = JS_FALSE;
 
3674
            for (i = 0; *bp && i < n; i++) {
 
3675
                attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
 
3676
                if (!attr)
 
3677
                    continue;
 
3678
                j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
 
3679
                if (j == XML_NOT_FOUND) {
 
3680
                    *bp = JS_FALSE;
 
3681
                    break;
 
3682
                }
 
3683
                vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
 
3684
                if (!vattr)
 
3685
                    continue;
 
3686
                *bp = js_EqualStrings(attr->xml_value, vattr->xml_value);
 
3687
            }
 
3688
        }
 
3689
    }
 
3690
 
 
3691
    return JS_TRUE;
 
3692
}
 
3693
 
 
3694
/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
 
3695
static JSBool
 
3696
Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
 
3697
{
 
3698
    JSObject *vobj;
 
3699
    JSXML *vxml;
 
3700
 
 
3701
    if (JSVAL_IS_PRIMITIVE(v)) {
 
3702
        *bp = JS_FALSE;
 
3703
        if (xml->xml_class == JSXML_CLASS_LIST) {
 
3704
            if (xml->xml_kids.length == 1) {
 
3705
                vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
3706
                if (!vxml)
 
3707
                    return JS_TRUE;
 
3708
                vobj = js_GetXMLObject(cx, vxml);
 
3709
                if (!vobj)
 
3710
                    return JS_FALSE;
 
3711
                return js_XMLObjectOps.equality(cx, vobj, v, bp);
 
3712
            }
 
3713
            if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
 
3714
                *bp = JS_TRUE;
 
3715
        }
 
3716
    } else {
 
3717
        vobj = JSVAL_TO_OBJECT(v);
 
3718
        if (!OBJECT_IS_XML(cx, vobj)) {
 
3719
            *bp = JS_FALSE;
 
3720
        } else {
 
3721
            vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
3722
            if (!XMLEquals(cx, xml, vxml, bp))
 
3723
                return JS_FALSE;
 
3724
        }
 
3725
    }
 
3726
    return JS_TRUE;
 
3727
}
 
3728
 
 
3729
static JSBool
 
3730
CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
 
3731
{
 
3732
    JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
 
3733
 
 
3734
    do {
 
3735
        if (xml == kid) {
 
3736
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3737
                                 JSMSG_CYCLIC_VALUE, js_XML_str);
 
3738
            return JS_FALSE;
 
3739
        }
 
3740
    } while ((xml = xml->parent) != NULL);
 
3741
 
 
3742
    return JS_TRUE;
 
3743
}
 
3744
 
 
3745
/* ECMA-357 9.1.1.11 XML [[Insert]]. */
 
3746
static JSBool
 
3747
Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
 
3748
{
 
3749
    uint32 j, n;
 
3750
    JSXML *vxml, *kid;
 
3751
    JSObject *vobj;
 
3752
    JSString *str;
 
3753
 
 
3754
    if (!JSXML_HAS_KIDS(xml))
 
3755
        return JS_TRUE;
 
3756
 
 
3757
    n = 1;
 
3758
    vxml = NULL;
 
3759
    if (!JSVAL_IS_PRIMITIVE(v)) {
 
3760
        vobj = JSVAL_TO_OBJECT(v);
 
3761
        if (OBJECT_IS_XML(cx, vobj)) {
 
3762
            vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
3763
            if (vxml->xml_class == JSXML_CLASS_LIST) {
 
3764
                n = vxml->xml_kids.length;
 
3765
                if (n == 0)
 
3766
                    return JS_TRUE;
 
3767
                for (j = 0; j < n; j++) {
 
3768
                    kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
 
3769
                    if (!kid)
 
3770
                        continue;
 
3771
                    if (!CheckCycle(cx, xml, kid))
 
3772
                        return JS_FALSE;
 
3773
                }
 
3774
            } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
 
3775
                /* OPTION: enforce that descendants have superset namespaces. */
 
3776
                if (!CheckCycle(cx, xml, vxml))
 
3777
                    return JS_FALSE;
 
3778
            }
 
3779
        }
 
3780
    }
 
3781
    if (!vxml) {
 
3782
        str = js_ValueToString(cx, v);
 
3783
        if (!str)
 
3784
            return JS_FALSE;
 
3785
 
 
3786
        vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
 
3787
        if (!vxml)
 
3788
            return JS_FALSE;
 
3789
        vxml->xml_value = str;
 
3790
    }
 
3791
 
 
3792
    if (i > xml->xml_kids.length)
 
3793
        i = xml->xml_kids.length;
 
3794
 
 
3795
    if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
 
3796
        return JS_FALSE;
 
3797
 
 
3798
    if (vxml->xml_class == JSXML_CLASS_LIST) {
 
3799
        for (j = 0; j < n; j++) {
 
3800
            kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
 
3801
            if (!kid)
 
3802
                continue;
 
3803
            kid->parent = xml;
 
3804
            XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
 
3805
 
 
3806
            /* OPTION: enforce that descendants have superset namespaces. */
 
3807
        }
 
3808
    } else {
 
3809
        vxml->parent = xml;
 
3810
        XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
 
3811
    }
 
3812
    return JS_TRUE;
 
3813
}
 
3814
 
 
3815
static JSBool
 
3816
IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp)
 
3817
{
 
3818
    JSString *str;
 
3819
 
 
3820
    if (index <= JSVAL_INT_MAX) {
 
3821
        *idvp = INT_TO_JSVAL(index);
 
3822
    } else {
 
3823
        str = js_NumberToString(cx, (jsdouble) index);
 
3824
        if (!str)
 
3825
            return JS_FALSE;
 
3826
        *idvp = STRING_TO_JSVAL(str);
 
3827
    }
 
3828
    return JS_TRUE;
 
3829
}
 
3830
 
 
3831
/* ECMA-357 9.1.1.12 XML [[Replace]]. */
 
3832
static JSBool
 
3833
Replace(JSContext *cx, JSXML *xml, jsval id, jsval v)
 
3834
{
 
3835
    uint32 i, n;
 
3836
    JSXML *vxml, *kid;
 
3837
    JSObject *vobj;
 
3838
    jsval junk;
 
3839
    JSString *str;
 
3840
 
 
3841
    if (!JSXML_HAS_KIDS(xml))
 
3842
        return JS_TRUE;
 
3843
 
 
3844
    if (!js_IdIsIndex(id, &i)) {
 
3845
        ReportBadXMLName(cx, id);
 
3846
        return JS_FALSE;
 
3847
    }
 
3848
 
 
3849
    /*
 
3850
     * 9.1.1.12
 
3851
     * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
 
3852
     * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
 
3853
     */
 
3854
    n = xml->xml_kids.length;
 
3855
    if (i >= n) {
 
3856
        if (!IndexToIdVal(cx, n, &id))
 
3857
            return JS_FALSE;
 
3858
        i = n;
 
3859
    }
 
3860
 
 
3861
    vxml = NULL;
 
3862
    if (!JSVAL_IS_PRIMITIVE(v)) {
 
3863
        vobj = JSVAL_TO_OBJECT(v);
 
3864
        if (OBJECT_IS_XML(cx, vobj))
 
3865
            vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
3866
    }
 
3867
 
 
3868
    switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) {
 
3869
      case JSXML_CLASS_ELEMENT:
 
3870
        /* OPTION: enforce that descendants have superset namespaces. */
 
3871
        if (!CheckCycle(cx, xml, vxml))
 
3872
            return JS_FALSE;
 
3873
      case JSXML_CLASS_COMMENT:
 
3874
      case JSXML_CLASS_PROCESSING_INSTRUCTION:
 
3875
      case JSXML_CLASS_TEXT:
 
3876
        goto do_replace;
 
3877
 
 
3878
      case JSXML_CLASS_LIST:
 
3879
        if (i < n && !DeleteByIndex(cx, xml, id, &junk))
 
3880
            return JS_FALSE;
 
3881
        if (!Insert(cx, xml, i, v))
 
3882
            return JS_FALSE;
 
3883
        break;
 
3884
 
 
3885
      default:
 
3886
        str = js_ValueToString(cx, v);
 
3887
        if (!str)
 
3888
            return JS_FALSE;
 
3889
 
 
3890
        vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
 
3891
        if (!vxml)
 
3892
            return JS_FALSE;
 
3893
        vxml->xml_value = str;
 
3894
 
 
3895
      do_replace:
 
3896
        vxml->parent = xml;
 
3897
        if (i < n) {
 
3898
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
3899
            if (kid)
 
3900
                kid->parent = NULL;
 
3901
        }
 
3902
        if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
 
3903
            return JS_FALSE;
 
3904
        break;
 
3905
    }
 
3906
 
 
3907
    return JS_TRUE;
 
3908
}
 
3909
 
 
3910
/* Forward declared -- its implementation uses other statics that call it. */
 
3911
static JSBool
 
3912
ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
 
3913
 
 
3914
/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */
 
3915
static JSBool
 
3916
DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
3917
{
 
3918
    JSXML *xml, *kid, *parent;
 
3919
    JSBool isIndex;
 
3920
    JSXMLArray *array;
 
3921
    uint32 length, index, kidIndex, deleteCount;
 
3922
    JSXMLQName *nameqn;
 
3923
    jsid funid;
 
3924
    JSObject *nameobj, *kidobj;
 
3925
    JSXMLNameMatcher matcher;
 
3926
 
 
3927
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
3928
    isIndex = js_IdIsIndex(id, &index);
 
3929
    if (JSXML_HAS_KIDS(xml)) {
 
3930
        array = &xml->xml_kids;
 
3931
        length = array->length;
 
3932
    } else {
 
3933
        array = NULL;
 
3934
        length = 0;
 
3935
    }
 
3936
 
 
3937
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
3938
        /* ECMA-357 9.2.1.3. */
 
3939
        if (isIndex && index < length) {
 
3940
            kid = XMLARRAY_MEMBER(array, index, JSXML);
 
3941
            if (!kid)
 
3942
                goto out;
 
3943
            parent = kid->parent;
 
3944
            if (parent) {
 
3945
                JS_ASSERT(parent != xml);
 
3946
                JS_ASSERT(JSXML_HAS_KIDS(parent));
 
3947
 
 
3948
                if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
 
3949
                    nameqn = kid->name;
 
3950
                    nameobj = js_GetAttributeNameObject(cx, nameqn);
 
3951
                    if (!nameobj || !js_GetXMLObject(cx, parent))
 
3952
                        return JS_FALSE;
 
3953
 
 
3954
                    id = OBJECT_TO_JSVAL(nameobj);
 
3955
                    if (!DeleteProperty(cx, parent->object, id, vp))
 
3956
                        return JS_FALSE;
 
3957
                } else {
 
3958
                    kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
 
3959
                                                    NULL);
 
3960
                    JS_ASSERT(kidIndex != XML_NOT_FOUND);
 
3961
                    if (!IndexToIdVal(cx, kidIndex, &id))
 
3962
                        return JS_FALSE;
 
3963
                    if (!DeleteByIndex(cx, parent, id, vp))
 
3964
                        return JS_FALSE;
 
3965
                }
 
3966
            }
 
3967
 
 
3968
            XMLArrayDelete(cx, array, index, JS_TRUE);
 
3969
        } else {
 
3970
            for (index = 0; index < length; index++) {
 
3971
                kid = XMLARRAY_MEMBER(array, index, JSXML);
 
3972
                if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
3973
                    kidobj = js_GetXMLObject(cx, kid);
 
3974
                    if (!kidobj || !DeleteProperty(cx, kidobj, id, vp))
 
3975
                        return JS_FALSE;
 
3976
                }
 
3977
            }
 
3978
        }
 
3979
    } else {
 
3980
        /* ECMA-357 9.1.1.3. */
 
3981
        if (isIndex) {
 
3982
            /* See NOTE in spec: this variation is reserved for future use. */
 
3983
            ReportBadXMLName(cx, id);
 
3984
            return JS_FALSE;
 
3985
        }
 
3986
 
 
3987
        nameqn = ToXMLName(cx, id, &funid);
 
3988
        if (!nameqn)
 
3989
            return JS_FALSE;
 
3990
        if (funid)
 
3991
            goto out;
 
3992
        nameobj = nameqn->object;
 
3993
 
 
3994
        if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
 
3995
            if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
3996
                goto out;
 
3997
            array = &xml->xml_attrs;
 
3998
            length = array->length;
 
3999
            matcher = MatchAttrName;
 
4000
        } else {
 
4001
            matcher = MatchElemName;
 
4002
        }
 
4003
        if (length != 0) {
 
4004
            deleteCount = 0;
 
4005
            for (index = 0; index < length; index++) {
 
4006
                kid = XMLARRAY_MEMBER(array, index, JSXML);
 
4007
                if (kid && matcher(nameqn, kid)) {
 
4008
                    kid->parent = NULL;
 
4009
                    XMLArrayDelete(cx, array, index, JS_FALSE);
 
4010
                    ++deleteCount;
 
4011
                } else if (deleteCount != 0) {
 
4012
                    XMLARRAY_SET_MEMBER(array,
 
4013
                                        index - deleteCount,
 
4014
                                        array->vector[index]);
 
4015
                }
 
4016
            }
 
4017
            array->length -= deleteCount;
 
4018
        }
 
4019
    }
 
4020
 
 
4021
out:
 
4022
    *vp = JSVAL_TRUE;
 
4023
    return JS_TRUE;
 
4024
}
 
4025
 
 
4026
static JSBool
 
4027
SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
 
4028
{
 
4029
    JSXMLArray *nsarray;
 
4030
    uint32 i, n;
 
4031
    JSXMLNamespace *ns;
 
4032
 
 
4033
    nsarray = &xml->xml_namespaces;
 
4034
    while ((xml = xml->parent) != NULL) {
 
4035
        for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
 
4036
            ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
 
4037
            if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
 
4038
                if (!XMLARRAY_APPEND(cx, nsarray, ns))
 
4039
                    return JS_FALSE;
 
4040
            }
 
4041
        }
 
4042
    }
 
4043
    return JS_TRUE;
 
4044
}
 
4045
 
 
4046
static JSBool
 
4047
GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn,
 
4048
                 JSBool attributes, JSXML *list)
 
4049
{
 
4050
    JSXMLArray *array;
 
4051
    JSXMLNameMatcher matcher;
 
4052
    JSXMLArrayCursor cursor;
 
4053
    JSXML *kid;
 
4054
    JSBool ok;
 
4055
 
 
4056
    if (!JSXML_HAS_KIDS(xml))
 
4057
        return JS_TRUE;
 
4058
 
 
4059
    if (attributes) {
 
4060
        array = &xml->xml_attrs;
 
4061
        matcher = MatchAttrName;
 
4062
    } else {
 
4063
        array = &xml->xml_kids;
 
4064
        matcher = MatchElemName;
 
4065
    }
 
4066
 
 
4067
    XMLArrayCursorInit(&cursor, array);
 
4068
    while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
4069
        if (matcher(nameqn, kid)) {
 
4070
            if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
4071
                ok = SyncInScopeNamespaces(cx, kid);
 
4072
                if (!ok)
 
4073
                    goto out;
 
4074
            }
 
4075
            ok = Append(cx, list, kid);
 
4076
            if (!ok)
 
4077
                goto out;
 
4078
        }
 
4079
    }
 
4080
    ok = JS_TRUE;
 
4081
 
 
4082
  out:
 
4083
    XMLArrayCursorFinish(&cursor);
 
4084
    return ok;
 
4085
}
 
4086
 
 
4087
/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
 
4088
static JSBool
 
4089
GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
4090
{
 
4091
    JSXML *xml, *list, *kid;
 
4092
    uint32 index;
 
4093
    JSObject *kidobj, *listobj;
 
4094
    JSXMLQName *nameqn;
 
4095
    jsid funid;
 
4096
    jsval roots[2];
 
4097
    JSTempValueRooter tvr;
 
4098
    JSBool attributes;
 
4099
    JSXMLArrayCursor cursor;
 
4100
 
 
4101
    xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
 
4102
    if (!xml)
 
4103
        return JS_TRUE;
 
4104
 
 
4105
    if (js_IdIsIndex(id, &index)) {
 
4106
        if (xml->xml_class != JSXML_CLASS_LIST) {
 
4107
            *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
 
4108
        } else {
 
4109
            /*
 
4110
             * ECMA-357 9.2.1.1 starts here.
 
4111
             *
 
4112
             * Erratum: 9.2 is not completely clear that indexed properties
 
4113
             * correspond to kids, but that's what it seems to say, and it's
 
4114
             * what any sane user would want.
 
4115
             */
 
4116
            if (index < xml->xml_kids.length) {
 
4117
                kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
 
4118
                if (!kid) {
 
4119
                    *vp = JSVAL_VOID;
 
4120
                    return JS_TRUE;
 
4121
                }
 
4122
                kidobj = js_GetXMLObject(cx, kid);
 
4123
                if (!kidobj)
 
4124
                    return JS_FALSE;
 
4125
 
 
4126
                *vp = OBJECT_TO_JSVAL(kidobj);
 
4127
            } else {
 
4128
                *vp = JSVAL_VOID;
 
4129
            }
 
4130
        }
 
4131
        return JS_TRUE;
 
4132
    }
 
4133
 
 
4134
    /*
 
4135
     * ECMA-357 9.2.1.1/9.1.1.1 qname case.
 
4136
     */
 
4137
    nameqn = ToXMLName(cx, id, &funid);
 
4138
    if (!nameqn)
 
4139
        return JS_FALSE;
 
4140
    if (funid)
 
4141
        return js_GetXMLFunction(cx, obj, funid, vp);
 
4142
 
 
4143
    roots[0] = OBJECT_TO_JSVAL(nameqn->object);
 
4144
    JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr);
 
4145
 
 
4146
    listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
4147
    if (listobj) {
 
4148
        roots[1] = OBJECT_TO_JSVAL(listobj);
 
4149
        tvr.count++;
 
4150
 
 
4151
        list = (JSXML *) JS_GetPrivate(cx, listobj);
 
4152
        attributes = (OBJ_GET_CLASS(cx, nameqn->object) ==
 
4153
                      &js_AttributeNameClass);
 
4154
 
 
4155
        if (xml->xml_class == JSXML_CLASS_LIST) {
 
4156
            XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
4157
            while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
4158
                if (kid->xml_class == JSXML_CLASS_ELEMENT &&
 
4159
                    !GetNamedProperty(cx, kid, nameqn, attributes, list)) {
 
4160
                    listobj = NULL;
 
4161
                    break;
 
4162
                }
 
4163
            }
 
4164
            XMLArrayCursorFinish(&cursor);
 
4165
        } else {
 
4166
            if (!GetNamedProperty(cx, xml, nameqn, attributes, list))
 
4167
                listobj = NULL;
 
4168
        }
 
4169
 
 
4170
        /*
 
4171
         * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given
 
4172
         * list's [[TargetProperty]] to the property that is being appended.
 
4173
         * This means that any use of the internal [[Get]] property returns
 
4174
         * a list which, when used by e.g. [[Insert]] duplicates the last
 
4175
         * element matched by id.
 
4176
         * See bug 336921.
 
4177
         */
 
4178
        list->xml_target = xml;
 
4179
        list->xml_targetprop = nameqn;
 
4180
        *vp = OBJECT_TO_JSVAL(listobj);
 
4181
    }
 
4182
 
 
4183
    JS_POP_TEMP_ROOT(cx, &tvr);
 
4184
    return listobj != NULL;
 
4185
}
 
4186
 
 
4187
static JSXML *
 
4188
CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
 
4189
{
 
4190
    JS_ASSERT(xml->object != obj);
 
4191
 
 
4192
    xml = DeepCopy(cx, xml, obj, 0);
 
4193
    if (!xml)
 
4194
        return NULL;
 
4195
 
 
4196
    JS_ASSERT(xml->object == obj);
 
4197
    return xml;
 
4198
}
 
4199
 
 
4200
#define CHECK_COPY_ON_WRITE(cx,xml,obj)                                       \
 
4201
    (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
 
4202
 
 
4203
static JSString *
 
4204
KidToString(JSContext *cx, JSXML *xml, uint32 index)
 
4205
{
 
4206
    JSXML *kid;
 
4207
    JSObject *kidobj;
 
4208
 
 
4209
    kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
 
4210
    if (!kid)
 
4211
        return cx->runtime->emptyString;
 
4212
    kidobj = js_GetXMLObject(cx, kid);
 
4213
    if (!kidobj)
 
4214
        return NULL;
 
4215
    return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj));
 
4216
}
 
4217
 
 
4218
/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
 
4219
static JSBool
 
4220
PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
4221
{
 
4222
    JSBool ok, primitiveAssign;
 
4223
    enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
 
4224
    jsval roots[3];
 
4225
    JSTempValueRooter tvr;
 
4226
    JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
 
4227
    JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
 
4228
    JSXMLQName *targetprop, *nameqn, *attrqn;
 
4229
    uint32 index, i, j, k, n, q;
 
4230
    jsval attrval, nsval, junk;
 
4231
    jsid funid;
 
4232
    JSString *left, *right, *space;
 
4233
    JSXMLNamespace *ns;
 
4234
 
 
4235
    xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
 
4236
    if (!xml)
 
4237
        return JS_TRUE;
 
4238
 
 
4239
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
4240
    if (!xml)
 
4241
        return JS_FALSE;
 
4242
 
 
4243
    /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
 
4244
    vxml = NULL;
 
4245
    if (!JSVAL_IS_PRIMITIVE(*vp)) {
 
4246
        vobj = JSVAL_TO_OBJECT(*vp);
 
4247
        if (OBJECT_IS_XML(cx, vobj))
 
4248
            vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
4249
    }
 
4250
 
 
4251
    /* Control flow after here must exit via label out. */
 
4252
    ok = js_EnterLocalRootScope(cx);
 
4253
    if (!ok)
 
4254
        return JS_FALSE;
 
4255
    roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
 
4256
    roots[ID_ROOT] = id;
 
4257
    roots[VAL_ROOT] = *vp;
 
4258
    JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr);
 
4259
 
 
4260
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
4261
        /* ECMA-357 9.2.1.2. */
 
4262
        if (js_IdIsIndex(id, &index)) {
 
4263
            /* Step 1 sets i to the property index. */
 
4264
            i = index;
 
4265
 
 
4266
            /* 2(a-b). */
 
4267
            if (xml->xml_target) {
 
4268
                ok = ResolveValue(cx, xml->xml_target, &rxml);
 
4269
                if (!ok)
 
4270
                    goto out;
 
4271
                if (!rxml)
 
4272
                    goto out;
 
4273
                JS_ASSERT(rxml->object);
 
4274
            } else {
 
4275
                rxml = NULL;
 
4276
            }
 
4277
 
 
4278
            /* 2(c). */
 
4279
            if (index >= xml->xml_kids.length) {
 
4280
                /* 2(c)(i). */
 
4281
                if (rxml) {
 
4282
                    if (rxml->xml_class == JSXML_CLASS_LIST) {
 
4283
                        if (rxml->xml_kids.length != 1)
 
4284
                            goto out;
 
4285
                        rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
 
4286
                        if (!rxml)
 
4287
                            goto out;
 
4288
                        ok = js_GetXMLObject(cx, rxml) != NULL;
 
4289
                        if (!ok)
 
4290
                            goto out;
 
4291
                    }
 
4292
 
 
4293
                    /*
 
4294
                     * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
 
4295
                     * _y.[[Parent]] = r_ where _r_ is the result of
 
4296
                     * [[ResolveValue]] called on _x.[[TargetObject]] in
 
4297
                     * 2(a)(i).  This can result in text parenting text:
 
4298
                     *
 
4299
                     *    var MYXML = new XML();
 
4300
                     *    MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
 
4301
                     *
 
4302
                     * (testcase from Werner Sharp <wsharp@macromedia.com>).
 
4303
                     *
 
4304
                     * To match insertChildAfter, insertChildBefore,
 
4305
                     * prependChild, and setChildren, we should silently
 
4306
                     * do nothing in this case.
 
4307
                     */
 
4308
                    if (!JSXML_HAS_KIDS(rxml))
 
4309
                        goto out;
 
4310
                }
 
4311
 
 
4312
                /* 2(c)(ii) is distributed below as several js_NewXML calls. */
 
4313
                targetprop = xml->xml_targetprop;
 
4314
                if (!targetprop || IS_STAR(targetprop->localName)) {
 
4315
                    /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
 
4316
                    kid = js_NewXML(cx, JSXML_CLASS_TEXT);
 
4317
                    if (!kid)
 
4318
                        goto bad;
 
4319
                } else {
 
4320
                    nameobj = js_GetXMLQNameObject(cx, targetprop);
 
4321
                    if (!nameobj)
 
4322
                        goto bad;
 
4323
                    if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
 
4324
                        /*
 
4325
                         * 2(c)(iii)(1-3).
 
4326
                         * Note that rxml can't be null here, because target
 
4327
                         * and targetprop are non-null.
 
4328
                         */
 
4329
                        ok = GetProperty(cx, rxml->object, id, &attrval);
 
4330
                        if (!ok)
 
4331
                            goto out;
 
4332
                        if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
 
4333
                            goto out;
 
4334
                        attrobj = JSVAL_TO_OBJECT(attrval);
 
4335
                        attr = (JSXML *) JS_GetPrivate(cx, attrobj);
 
4336
                        if (JSXML_LENGTH(attr) != 0)
 
4337
                            goto out;
 
4338
 
 
4339
                        kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
 
4340
                    } else {
 
4341
                        /* 2(c)(v). */
 
4342
                        kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
 
4343
                    }
 
4344
                    if (!kid)
 
4345
                        goto bad;
 
4346
 
 
4347
                    /* An important bit of 2(c)(ii). */
 
4348
                    kid->name = targetprop;
 
4349
                }
 
4350
 
 
4351
                /* Final important bit of 2(c)(ii). */
 
4352
                kid->parent = rxml;
 
4353
 
 
4354
                /* 2(c)(vi-vii). */
 
4355
                i = xml->xml_kids.length;
 
4356
                if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
 
4357
                    /*
 
4358
                     * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
 
4359
                     * y.[[Parent]] is here called kid->parent, which we know
 
4360
                     * from 2(c)(ii) is _r_, here called rxml.  So let's just
 
4361
                     * test that!  Erratum, the spec should be simpler here.
 
4362
                     */
 
4363
                    if (rxml) {
 
4364
                        JS_ASSERT(JSXML_HAS_KIDS(rxml));
 
4365
                        n = rxml->xml_kids.length;
 
4366
                        j = n - 1;
 
4367
                        if (n != 0 && i != 0) {
 
4368
                            for (n = j, j = 0; j < n; j++) {
 
4369
                                if (rxml->xml_kids.vector[j] ==
 
4370
                                    xml->xml_kids.vector[i-1]) {
 
4371
                                    break;
 
4372
                                }
 
4373
                            }
 
4374
                        }
 
4375
 
 
4376
                        kidobj = js_GetXMLObject(cx, kid);
 
4377
                        if (!kidobj)
 
4378
                            goto bad;
 
4379
                        ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
 
4380
                        if (!ok)
 
4381
                            goto out;
 
4382
                    }
 
4383
 
 
4384
                    /*
 
4385
                     * 2(c)(vii)(2-3).
 
4386
                     * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
 
4387
                     * typo for [[TargetProperty]].
 
4388
                     */
 
4389
                    if (vxml) {
 
4390
                        kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
 
4391
                                    ? vxml->xml_targetprop
 
4392
                                    : vxml->name;
 
4393
                    }
 
4394
                }
 
4395
 
 
4396
                /* 2(c)(viii). */
 
4397
                ok = Append(cx, xml, kid);
 
4398
                if (!ok)
 
4399
                    goto out;
 
4400
            }
 
4401
 
 
4402
            /* 2(d). */
 
4403
            if (!vxml ||
 
4404
                vxml->xml_class == JSXML_CLASS_TEXT ||
 
4405
                vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
 
4406
                ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
 
4407
                if (!ok)
 
4408
                    goto out;
 
4409
                roots[VAL_ROOT] = *vp;
 
4410
            }
 
4411
 
 
4412
            /* 2(e). */
 
4413
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
4414
            if (!kid)
 
4415
                goto out;
 
4416
            parent = kid->parent;
 
4417
            if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
 
4418
                nameobj = js_GetAttributeNameObject(cx, kid->name);
 
4419
                if (!nameobj)
 
4420
                    goto bad;
 
4421
                id = OBJECT_TO_JSVAL(nameobj);
 
4422
 
 
4423
                if (parent) {
 
4424
                    /* 2(e)(i). */
 
4425
                    parentobj = js_GetXMLObject(cx, parent);
 
4426
                    if (!parentobj)
 
4427
                        goto bad;
 
4428
                    ok = PutProperty(cx, parentobj, id, vp);
 
4429
                    if (!ok)
 
4430
                        goto out;
 
4431
 
 
4432
                    /* 2(e)(ii). */
 
4433
                    ok = GetProperty(cx, parentobj, id, vp);
 
4434
                    if (!ok)
 
4435
                        goto out;
 
4436
                    attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
 
4437
 
 
4438
                    /* 2(e)(iii). */
 
4439
                    xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
 
4440
                }
 
4441
            }
 
4442
 
 
4443
            /* 2(f). */
 
4444
            else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
 
4445
                /* 2(f)(i) Create a shallow copy _c_ of _V_. */
 
4446
                copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
4447
                if (!copyobj)
 
4448
                    goto bad;
 
4449
                copy = (JSXML *) JS_GetPrivate(cx, copyobj);
 
4450
                n = vxml->xml_kids.length;
 
4451
                ok = XMLArraySetCapacity(cx, &copy->xml_kids, n);
 
4452
                if (!ok)
 
4453
                    goto out;
 
4454
                for (k = 0; k < n; k++) {
 
4455
                    kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML);
 
4456
                    XMLARRAY_SET_MEMBER(&copy->xml_kids, k, kid2);
 
4457
                }
 
4458
 
 
4459
                JS_ASSERT(parent != xml);
 
4460
                if (parent) {
 
4461
                    q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
 
4462
                    JS_ASSERT(q != XML_NOT_FOUND);
 
4463
 
 
4464
                    ok = IndexToIdVal(cx, q, &id);
 
4465
                    if (!ok)
 
4466
                        goto out;
 
4467
                    ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj));
 
4468
                    if (!ok)
 
4469
                        goto out;
 
4470
 
 
4471
#ifdef DEBUG
 
4472
                    /* Erratum: this loop in the spec is useless. */
 
4473
                    for (j = 0, n = copy->xml_kids.length; j < n; j++) {
 
4474
                        kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
 
4475
                        JS_ASSERT(XMLARRAY_MEMBER(&copy->xml_kids, j, JSXML)
 
4476
                                  == kid2);
 
4477
                    }
 
4478
#endif
 
4479
                }
 
4480
 
 
4481
                /*
 
4482
                 * 2(f)(iv-vi).
 
4483
                 * Erratum: notice the unhandled zero-length V basis case and
 
4484
                 * the off-by-one errors for the n != 0 cases in the spec.
 
4485
                 */
 
4486
                if (n == 0) {
 
4487
                    XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
 
4488
                } else {
 
4489
                    ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
 
4490
                    if (!ok)
 
4491
                        goto out;
 
4492
 
 
4493
                    for (j = 0; j < n; j++)
 
4494
                        xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
 
4495
                }
 
4496
            }
 
4497
 
 
4498
            /* 2(g). */
 
4499
            else if (vxml || JSXML_HAS_VALUE(kid)) {
 
4500
                if (parent) {
 
4501
                    q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
 
4502
                    JS_ASSERT(q != XML_NOT_FOUND);
 
4503
 
 
4504
                    ok = IndexToIdVal(cx, q, &id);
 
4505
                    if (!ok)
 
4506
                        goto out;
 
4507
                    ok = Replace(cx, parent, id, *vp);
 
4508
                    if (!ok)
 
4509
                        goto out;
 
4510
 
 
4511
                    vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
 
4512
                    if (!vxml)
 
4513
                        goto out;
 
4514
                    roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
 
4515
                }
 
4516
 
 
4517
                /*
 
4518
                 * 2(g)(iii).
 
4519
                 * Erratum: _V_ may not be of type XML, but all index-named
 
4520
                 * properties _x[i]_ in an XMLList _x_ must be of type XML,
 
4521
                 * according to 9.2.1.1 Overview and other places in the spec.
 
4522
                 *
 
4523
                 * Thanks to 2(d), we know _V_ (*vp here) is either a string
 
4524
                 * or an XML/XMLList object.  If *vp is a string, call ToXML
 
4525
                 * on it to satisfy the constraint.
 
4526
                 */
 
4527
                if (!vxml) {
 
4528
                    JS_ASSERT(JSVAL_IS_STRING(*vp));
 
4529
                    vobj = ToXML(cx, *vp);
 
4530
                    if (!vobj)
 
4531
                        goto bad;
 
4532
                    roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
 
4533
                    vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
4534
                }
 
4535
                XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
 
4536
            }
 
4537
 
 
4538
            /* 2(h). */
 
4539
            else {
 
4540
                kidobj = js_GetXMLObject(cx, kid);
 
4541
                if (!kidobj)
 
4542
                    goto bad;
 
4543
                id = ATOM_KEY(cx->runtime->atomState.starAtom);
 
4544
                ok = PutProperty(cx, kidobj, id, vp);
 
4545
                if (!ok)
 
4546
                    goto out;
 
4547
            }
 
4548
        } else {
 
4549
            /*
 
4550
             * 3.
 
4551
             * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
 
4552
             * or an r with r.[[Length]] != 1, throw TypeError.
 
4553
             */
 
4554
            n = JSXML_LENGTH(xml);
 
4555
            if (n > 1)
 
4556
                goto type_error;
 
4557
            if (n == 0) {
 
4558
                ok = ResolveValue(cx, xml, &rxml);
 
4559
                if (!ok)
 
4560
                    goto out;
 
4561
                if (!rxml || JSXML_LENGTH(rxml) != 1)
 
4562
                    goto type_error;
 
4563
                ok = Append(cx, xml, rxml);
 
4564
                if (!ok)
 
4565
                    goto out;
 
4566
            }
 
4567
            JS_ASSERT(JSXML_LENGTH(xml) == 1);
 
4568
            kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
4569
            if (!kid)
 
4570
                goto out;
 
4571
            kidobj = js_GetXMLObject(cx, kid);
 
4572
            if (!kidobj)
 
4573
                goto bad;
 
4574
            ok = PutProperty(cx, kidobj, id, vp);
 
4575
            if (!ok)
 
4576
                goto out;
 
4577
        }
 
4578
    } else {
 
4579
        /*
 
4580
         * ECMA-357 9.1.1.2.
 
4581
         * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
 
4582
         * effort in ToString or [[DeepCopy]].
 
4583
         */
 
4584
        if (js_IdIsIndex(id, &index)) {
 
4585
            /* See NOTE in spec: this variation is reserved for future use. */
 
4586
            ReportBadXMLName(cx, id);
 
4587
            goto bad;
 
4588
        }
 
4589
 
 
4590
        nameqn = ToXMLName(cx, id, &funid);
 
4591
        if (!nameqn)
 
4592
            goto bad;
 
4593
        if (funid) {
 
4594
            ok = js_SetProperty(cx, obj, funid, vp);
 
4595
            goto out;
 
4596
        }
 
4597
        nameobj = nameqn->object;
 
4598
 
 
4599
        if (JSXML_HAS_VALUE(xml))
 
4600
            goto out;
 
4601
 
 
4602
        if (!vxml ||
 
4603
            vxml->xml_class == JSXML_CLASS_TEXT ||
 
4604
            vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
 
4605
            ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
 
4606
            if (!ok)
 
4607
                goto out;
 
4608
        } else {
 
4609
            rxml = DeepCopyInLRS(cx, vxml, 0);
 
4610
            if (!rxml || !js_GetXMLObject(cx, rxml))
 
4611
                goto bad;
 
4612
            vxml = rxml;
 
4613
            *vp = OBJECT_TO_JSVAL(vxml->object);
 
4614
        }
 
4615
        roots[VAL_ROOT] = *vp;
 
4616
 
 
4617
        /*
 
4618
         * 6.
 
4619
         * Erratum: why is this done here, so early? use is way later....
 
4620
         */
 
4621
        ok = js_GetDefaultXMLNamespace(cx, &nsval);
 
4622
        if (!ok)
 
4623
            goto out;
 
4624
 
 
4625
        if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
 
4626
            /* 7(a). */
 
4627
            if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
 
4628
                goto out;
 
4629
 
 
4630
            /* 7(b-c). */
 
4631
            if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
 
4632
                n = vxml->xml_kids.length;
 
4633
                if (n == 0) {
 
4634
                    *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
 
4635
                } else {
 
4636
                    left = KidToString(cx, vxml, 0);
 
4637
                    if (!left)
 
4638
                        goto bad;
 
4639
 
 
4640
                    space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom);
 
4641
                    for (i = 1; i < n; i++) {
 
4642
                        left = js_ConcatStrings(cx, left, space);
 
4643
                        if (!left)
 
4644
                            goto bad;
 
4645
                        right = KidToString(cx, vxml, i);
 
4646
                        if (!right)
 
4647
                            goto bad;
 
4648
                        left = js_ConcatStrings(cx, left, right);
 
4649
                        if (!left)
 
4650
                            goto bad;
 
4651
                    }
 
4652
 
 
4653
                    roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
 
4654
                }
 
4655
            } else {
 
4656
                ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
 
4657
                if (!ok)
 
4658
                    goto out;
 
4659
                roots[VAL_ROOT] = *vp;
 
4660
            }
 
4661
 
 
4662
            /* 7(d-e). */
 
4663
            match = NULL;
 
4664
            for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
 
4665
                attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
 
4666
                if (!attr)
 
4667
                    continue;
 
4668
                attrqn = attr->name;
 
4669
                if (js_EqualStrings(attrqn->localName, nameqn->localName) &&
 
4670
                    (!nameqn->uri ||
 
4671
                     js_EqualStrings(attrqn->uri, nameqn->uri))) {
 
4672
                    if (!match) {
 
4673
                        match = attr;
 
4674
                    } else {
 
4675
                        nameobj = js_GetAttributeNameObject(cx, attrqn);
 
4676
                        if (!nameobj)
 
4677
                            goto bad;
 
4678
 
 
4679
                        id = OBJECT_TO_JSVAL(nameobj);
 
4680
                        ok = DeleteProperty(cx, obj, id, &junk);
 
4681
                        if (!ok)
 
4682
                            goto out;
 
4683
                        --i;
 
4684
                    }
 
4685
                }
 
4686
            }
 
4687
 
 
4688
            /* 7(f). */
 
4689
            attr = match;
 
4690
            if (!attr) {
 
4691
                /* 7(f)(i-ii). */
 
4692
                if (!nameqn->uri) {
 
4693
                    left = right = cx->runtime->emptyString;
 
4694
                } else {
 
4695
                    left = nameqn->uri;
 
4696
                    right = nameqn->prefix;
 
4697
                }
 
4698
                nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
 
4699
                if (!nameqn)
 
4700
                    goto bad;
 
4701
 
 
4702
                /* 7(f)(iii). */
 
4703
                attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
 
4704
                if (!attr)
 
4705
                    goto bad;
 
4706
                attr->parent = xml;
 
4707
                attr->name = nameqn;
 
4708
 
 
4709
                /* 7(f)(iv). */
 
4710
                ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
 
4711
                if (!ok)
 
4712
                    goto out;
 
4713
 
 
4714
                /* 7(f)(v-vi). */
 
4715
                ns = GetNamespace(cx, nameqn, NULL);
 
4716
                if (!ns)
 
4717
                    goto bad;
 
4718
                ok = AddInScopeNamespace(cx, xml, ns);
 
4719
                if (!ok)
 
4720
                    goto out;
 
4721
            }
 
4722
 
 
4723
            /* 7(g). */
 
4724
            attr->xml_value = JSVAL_TO_STRING(*vp);
 
4725
            goto out;
 
4726
        }
 
4727
 
 
4728
        /* 8-9. */
 
4729
        if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
 
4730
            !IS_STAR(nameqn->localName)) {
 
4731
            goto out;
 
4732
        }
 
4733
 
 
4734
        /* 10-11. */
 
4735
        id = JSVAL_VOID;
 
4736
        primitiveAssign = !vxml && !IS_STAR(nameqn->localName);
 
4737
 
 
4738
        /* 12. */
 
4739
        k = n = xml->xml_kids.length;
 
4740
        kid2 = NULL;
 
4741
        while (k != 0) {
 
4742
            --k;
 
4743
            kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
 
4744
            if (kid && MatchElemName(nameqn, kid)) {
 
4745
                if (!JSVAL_IS_VOID(id)) {
 
4746
                    ok = DeleteByIndex(cx, xml, id, &junk);
 
4747
                    if (!ok)
 
4748
                        goto out;
 
4749
                }
 
4750
                ok = IndexToIdVal(cx, k, &id);
 
4751
                if (!ok)
 
4752
                    goto out;
 
4753
                kid2 = kid;
 
4754
            }
 
4755
        }
 
4756
 
 
4757
        /*
 
4758
         * Erratum: ECMA-357 specified child insertion inconsistently:
 
4759
         * insertChildBefore and insertChildAfter insert an arbitrary XML
 
4760
         * instance, and therefore can create cycles, but appendChild as
 
4761
         * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
 
4762
         * its argument.  But the "Semantics" in 13.4.4.3 do not include
 
4763
         * any [[DeepCopy]] call.
 
4764
         *
 
4765
         * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
 
4766
         * required adding cycle detection, and allowing duplicate kids to
 
4767
         * be created (see comment 6 in the bug).  Allowing duplicate kid
 
4768
         * references means the loop above will delete all but the lowest
 
4769
         * indexed reference, and each [[DeleteByIndex]] nulls the kid's
 
4770
         * parent.  Thus the need to restore parent here.  This is covered
 
4771
         * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
 
4772
         */
 
4773
        if (kid2) {
 
4774
            JS_ASSERT(kid2->parent == xml || !kid2->parent);
 
4775
            if (!kid2->parent)
 
4776
                kid2->parent = xml;
 
4777
        }
 
4778
 
 
4779
        /* 13. */
 
4780
        if (JSVAL_IS_VOID(id)) {
 
4781
            /* 13(a). */
 
4782
            ok = IndexToIdVal(cx, n, &id);
 
4783
            if (!ok)
 
4784
                goto out;
 
4785
 
 
4786
            /* 13(b). */
 
4787
            if (primitiveAssign) {
 
4788
                if (!nameqn->uri) {
 
4789
                    ns = (JSXMLNamespace *)
 
4790
                         JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
 
4791
                    left = ns->uri;
 
4792
                    right = ns->prefix;
 
4793
                } else {
 
4794
                    left = nameqn->uri;
 
4795
                    right = nameqn->prefix;
 
4796
                }
 
4797
                nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
 
4798
                if (!nameqn)
 
4799
                    goto bad;
 
4800
 
 
4801
                /* 13(b)(iii). */
 
4802
                vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
 
4803
                if (!vobj)
 
4804
                    goto bad;
 
4805
                vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
4806
                vxml->parent = xml;
 
4807
                vxml->name = nameqn;
 
4808
 
 
4809
                /* 13(b)(iv-vi). */
 
4810
                ns = GetNamespace(cx, nameqn, NULL);
 
4811
                if (!ns)
 
4812
                    goto bad;
 
4813
                ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj));
 
4814
                if (!ok)
 
4815
                    goto out;
 
4816
                ok = AddInScopeNamespace(cx, vxml, ns);
 
4817
                if (!ok)
 
4818
                    goto out;
 
4819
            }
 
4820
        }
 
4821
 
 
4822
        /* 14. */
 
4823
        if (primitiveAssign) {
 
4824
            JSXMLArrayCursor cursor;
 
4825
 
 
4826
            js_IdIsIndex(id, &index);
 
4827
            XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
4828
            cursor.index = index;
 
4829
            kid = (JSXML *) XMLArrayCursorItem(&cursor);
 
4830
            if (JSXML_HAS_KIDS(kid)) {
 
4831
                XMLArrayFinish(cx, &kid->xml_kids);
 
4832
                ok = XMLArrayInit(cx, &kid->xml_kids, 1);
 
4833
            }
 
4834
 
 
4835
            /* 14(b-c). */
 
4836
            /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
 
4837
            if (ok) {
 
4838
                ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
 
4839
                if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) {
 
4840
                    roots[VAL_ROOT] = *vp;
 
4841
                    if ((JSXML *) XMLArrayCursorItem(&cursor) == kid)
 
4842
                        ok = Replace(cx, kid, JSVAL_ZERO, *vp);
 
4843
                }
 
4844
            }
 
4845
            XMLArrayCursorFinish(&cursor);
 
4846
        } else {
 
4847
            /* 15(a). */
 
4848
            ok = Replace(cx, xml, id, *vp);
 
4849
        }
 
4850
    }
 
4851
 
 
4852
out:
 
4853
    JS_POP_TEMP_ROOT(cx, &tvr);
 
4854
    js_LeaveLocalRootScope(cx);
 
4855
    return ok;
 
4856
 
 
4857
type_error:
 
4858
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
4859
                         JSMSG_BAD_XMLLIST_PUT,
 
4860
                         js_ValueToPrintableString(cx, id));
 
4861
bad:
 
4862
    ok = JS_FALSE;
 
4863
    goto out;
 
4864
}
 
4865
 
 
4866
/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
 
4867
static JSBool
 
4868
ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
 
4869
{
 
4870
    JSXML *target, *base;
 
4871
    JSXMLQName *targetprop;
 
4872
    JSObject *targetpropobj;
 
4873
    jsval id, tv;
 
4874
 
 
4875
    /* Our caller must be protecting newborn objects. */
 
4876
    JS_ASSERT(cx->localRootStack);
 
4877
 
 
4878
    if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
 
4879
        if (!js_GetXMLObject(cx, list))
 
4880
            return JS_FALSE;
 
4881
        *result = list;
 
4882
        return JS_TRUE;
 
4883
    }
 
4884
 
 
4885
    target = list->xml_target;
 
4886
    targetprop = list->xml_targetprop;
 
4887
    if (!target || !targetprop || IS_STAR(targetprop->localName)) {
 
4888
        *result = NULL;
 
4889
        return JS_TRUE;
 
4890
    }
 
4891
 
 
4892
    targetpropobj = js_GetXMLQNameObject(cx, targetprop);
 
4893
    if (!targetpropobj)
 
4894
        return JS_FALSE;
 
4895
    if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) {
 
4896
        *result = NULL;
 
4897
        return JS_TRUE;
 
4898
    }
 
4899
 
 
4900
    if (!ResolveValue(cx, target, &base))
 
4901
        return JS_FALSE;
 
4902
    if (!base) {
 
4903
        *result = NULL;
 
4904
        return JS_TRUE;
 
4905
    }
 
4906
    if (!js_GetXMLObject(cx, base))
 
4907
        return JS_FALSE;
 
4908
 
 
4909
    id = OBJECT_TO_JSVAL(targetpropobj);
 
4910
    if (!GetProperty(cx, base->object, id, &tv))
 
4911
        return JS_FALSE;
 
4912
    target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
 
4913
 
 
4914
    if (JSXML_LENGTH(target) == 0) {
 
4915
        if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
 
4916
            *result = NULL;
 
4917
            return JS_TRUE;
 
4918
        }
 
4919
        tv = STRING_TO_JSVAL(cx->runtime->emptyString);
 
4920
        if (!PutProperty(cx, base->object, id, &tv))
 
4921
            return JS_FALSE;
 
4922
        if (!GetProperty(cx, base->object, id, &tv))
 
4923
            return JS_FALSE;
 
4924
        target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
 
4925
    }
 
4926
 
 
4927
    *result = target;
 
4928
    return JS_TRUE;
 
4929
}
 
4930
 
 
4931
/*
 
4932
 * HasProperty must be able to return a found JSProperty and the object in
 
4933
 * which it was found, if id is of the form function::name.  For other ids,
 
4934
 * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp
 
4935
 * and null in *objp.
 
4936
 *
 
4937
 * DROP_PROPERTY helps HasProperty callers drop function properties without
 
4938
 * trying to drop the magic FOUND_XML_PROPERTY cookie.
 
4939
 */
 
4940
#define FOUND_XML_PROPERTY              ((JSProperty *) 1)
 
4941
#define DROP_PROPERTY(cx,pobj,prop)     (((prop) != FOUND_XML_PROPERTY)       \
 
4942
                                         ? OBJ_DROP_PROPERTY(cx, pobj, prop)  \
 
4943
                                         : (void) 0)
 
4944
 
 
4945
/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
 
4946
static JSBool
 
4947
HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp,
 
4948
            JSProperty **propp)
 
4949
{
 
4950
    JSXML *xml, *kid;
 
4951
    JSXMLArrayCursor cursor;
 
4952
    JSObject *kidobj;
 
4953
    JSXMLQName *qn;
 
4954
    jsid funid;
 
4955
    JSXMLArray *array;
 
4956
    JSXMLNameMatcher matcher;
 
4957
    uint32 i, n;
 
4958
 
 
4959
    *objp = NULL;
 
4960
    *propp = NULL;
 
4961
 
 
4962
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
4963
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
4964
        n = JSXML_LENGTH(xml);
 
4965
        if (js_IdIsIndex(id, &i)) {
 
4966
            if (i < n)
 
4967
                *propp = FOUND_XML_PROPERTY;
 
4968
            return JS_TRUE;
 
4969
        }
 
4970
 
 
4971
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
4972
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
4973
            if (kid->xml_class == JSXML_CLASS_ELEMENT) {
 
4974
                kidobj = js_GetXMLObject(cx, kid);
 
4975
                if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp))
 
4976
                    break;
 
4977
                if (*propp)
 
4978
                    break;
 
4979
            }
 
4980
        }
 
4981
        XMLArrayCursorFinish(&cursor);
 
4982
        if (kid)
 
4983
            return *propp != NULL;
 
4984
    } else {
 
4985
        if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) {
 
4986
            if (i == 0)
 
4987
                *propp = FOUND_XML_PROPERTY;
 
4988
            return JS_TRUE;
 
4989
        }
 
4990
 
 
4991
        qn = ToXMLName(cx, id, &funid);
 
4992
        if (!qn)
 
4993
            return JS_FALSE;
 
4994
        if (funid)
 
4995
            return js_LookupProperty(cx, obj, funid, objp, propp);
 
4996
 
 
4997
        if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
4998
            return JS_TRUE;
 
4999
 
 
5000
        if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) {
 
5001
            array = &xml->xml_attrs;
 
5002
            matcher = MatchAttrName;
 
5003
        } else {
 
5004
            array = &xml->xml_kids;
 
5005
            matcher = MatchElemName;
 
5006
        }
 
5007
        for (i = 0, n = array->length; i < n; i++) {
 
5008
            kid = XMLARRAY_MEMBER(array, i, JSXML);
 
5009
            if (kid && matcher(qn, kid)) {
 
5010
                *propp = FOUND_XML_PROPERTY;
 
5011
                return JS_TRUE;
 
5012
            }
 
5013
        }
 
5014
    }
 
5015
 
 
5016
    return JS_TRUE;
 
5017
}
 
5018
 
 
5019
static void
 
5020
xml_finalize(JSContext *cx, JSObject *obj)
 
5021
{
 
5022
    JSXML *xml;
 
5023
 
 
5024
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
5025
    if (!xml)
 
5026
        return;
 
5027
    if (xml->object == obj)
 
5028
        xml->object = NULL;
 
5029
    UNMETER(xml_stats.livexmlobj);
 
5030
}
 
5031
 
 
5032
static void
 
5033
xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len)
 
5034
{
 
5035
    uint32 i;
 
5036
    JSXML *elt;
 
5037
 
 
5038
    for (i = 0; i < len; i++) {
 
5039
        elt = vec[i];
 
5040
        {
 
5041
#ifdef GC_MARK_DEBUG
 
5042
            char buf[120];
 
5043
 
 
5044
            if (elt->xml_class == JSXML_CLASS_LIST) {
 
5045
                strcpy(buf, js_XMLList_str);
 
5046
            } else if (JSXML_HAS_NAME(elt)) {
 
5047
                JSXMLQName *qn = elt->name;
 
5048
 
 
5049
                JS_snprintf(buf, sizeof buf, "%s::%s",
 
5050
                            qn->uri ? JS_GetStringBytes(qn->uri) : "*",
 
5051
                            JS_GetStringBytes(qn->localName));
 
5052
            } else {
 
5053
                JSString *str = elt->xml_value;
 
5054
                size_t srclen = JSSTRING_LENGTH(str);
 
5055
                size_t dstlen = sizeof buf;
 
5056
 
 
5057
                if (srclen >= sizeof buf / 6)
 
5058
                    srclen = sizeof buf / 6 - 1;
 
5059
                js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen,
 
5060
                                         buf, &dstlen);
 
5061
            }
 
5062
#endif
 
5063
            GC_MARK(cx, elt, buf);
 
5064
        }
 
5065
    }
 
5066
}
 
5067
 
 
5068
/*
 
5069
 * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
 
5070
 * be native.  Therefore, xml_lookupProperty must return a valid JSProperty
 
5071
 * pointer parameter via *propp to signify "property found".  Since the only
 
5072
 * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
 
5073
 * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from
 
5074
 * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a
 
5075
 * JSScopeProperty here is when an unqualified name or XML name is being
 
5076
 * accessed or when "name in xml" is called.
 
5077
 *
 
5078
 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
 
5079
 * giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
 
5080
 *
 
5081
 * NB: xml_deleteProperty must take care to remove any property added here.
 
5082
 *
 
5083
 * FIXME This clashes with the function namespace implementation which also
 
5084
 * uses native properties. Effectively after xml_lookupProperty any property
 
5085
 * stored previously using assignments to xml.function::name will be removed.
 
5086
 * We partially workaround the problem in js_GetXMLFunction. There we take
 
5087
 * advantage of the fact that typically function:: is used to access the
 
5088
 * functions from XML.prototype. So when js_GetProperty returns a non-function
 
5089
 * property, we assume that it represents the result of GetProperty setter
 
5090
 * hiding the function and use an extra prototype chain lookup to recover it.
 
5091
 * For a proper solution see bug 355257.
 
5092
 */
 
5093
static JSBool
 
5094
xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
 
5095
                   JSProperty **propp)
 
5096
{
 
5097
    JSScopeProperty *sprop;
 
5098
 
 
5099
    if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp))
 
5100
        return JS_FALSE;
 
5101
 
 
5102
    if (*propp == FOUND_XML_PROPERTY) {
 
5103
        sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
 
5104
                                     SPROP_INVALID_SLOT, JSPROP_ENUMERATE,
 
5105
                                     0, 0);
 
5106
        if (!sprop)
 
5107
            return JS_FALSE;
 
5108
 
 
5109
        JS_LOCK_OBJ(cx, obj);
 
5110
        *objp = obj;
 
5111
        *propp = (JSProperty *) sprop;
 
5112
    }
 
5113
    return JS_TRUE;
 
5114
}
 
5115
 
 
5116
static JSBool
 
5117
xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
 
5118
                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
 
5119
                   JSProperty **propp)
 
5120
{
 
5121
    if (VALUE_IS_FUNCTION(cx, value) || getter || setter ||
 
5122
        (attrs & JSPROP_ENUMERATE) == 0 ||
 
5123
        (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
 
5124
        return js_DefineProperty(cx, obj, id, value, getter, setter, attrs,
 
5125
                                 propp);
 
5126
    }
 
5127
 
 
5128
    if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value))
 
5129
        return JS_FALSE;
 
5130
    if (propp)
 
5131
        *propp = NULL;
 
5132
    return JS_TRUE;
 
5133
}
 
5134
 
 
5135
static JSBool
 
5136
xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
5137
{
 
5138
    if (id == JS_DEFAULT_XML_NAMESPACE_ID) {
 
5139
        *vp = JSVAL_VOID;
 
5140
        return JS_TRUE;
 
5141
    }
 
5142
 
 
5143
    return GetProperty(cx, obj, ID_TO_VALUE(id), vp);
 
5144
}
 
5145
 
 
5146
static JSBool
 
5147
xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
5148
{
 
5149
    return PutProperty(cx, obj, ID_TO_VALUE(id), vp);
 
5150
}
 
5151
 
 
5152
static JSBool
 
5153
FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
5154
              JSBool *foundp)
 
5155
{
 
5156
    JSObject *pobj;
 
5157
 
 
5158
    if (prop) {
 
5159
        *foundp = JS_TRUE;
 
5160
    } else {
 
5161
        if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop))
 
5162
            return JS_FALSE;
 
5163
        if (prop)
 
5164
            DROP_PROPERTY(cx, pobj, prop);
 
5165
        *foundp = (prop != NULL);
 
5166
    }
 
5167
    return JS_TRUE;
 
5168
}
 
5169
 
 
5170
static JSBool
 
5171
xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
5172
                  uintN *attrsp)
 
5173
{
 
5174
    JSBool found;
 
5175
 
 
5176
    if (!FoundProperty(cx, obj, id, prop, &found))
 
5177
        return JS_FALSE;
 
5178
    *attrsp = found ? JSPROP_ENUMERATE : 0;
 
5179
    return JS_TRUE;
 
5180
}
 
5181
 
 
5182
static JSBool
 
5183
xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
5184
                  uintN *attrsp)
 
5185
{
 
5186
    JSBool found;
 
5187
 
 
5188
    if (!FoundProperty(cx, obj, id, prop, &found))
 
5189
        return JS_FALSE;
 
5190
    if (found) {
 
5191
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
5192
                             JSMSG_CANT_SET_XML_ATTRS);
 
5193
    }
 
5194
    return !found;
 
5195
}
 
5196
 
 
5197
static JSBool
 
5198
xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
 
5199
{
 
5200
    /*
 
5201
     * If this object has its own (mutable) scope, and if id isn't an index,
 
5202
     * then we may have added a property to the scope in xml_lookupProperty
 
5203
     * for it to return to mean "found" and to provide a handle for access
 
5204
     * operations to call the property's getter or setter.  The property also
 
5205
     * helps speed up unqualified accesses via the property cache, avoiding
 
5206
     * what amount to two HasProperty searches.
 
5207
     *
 
5208
     * But now it's time to remove any such property, to purge the property
 
5209
     * cache and remove the scope entry.
 
5210
     */
 
5211
    if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) {
 
5212
        if (!js_DeleteProperty(cx, obj, id, rval))
 
5213
            return JS_FALSE;
 
5214
    }
 
5215
 
 
5216
    return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval);
 
5217
}
 
5218
 
 
5219
static JSBool
 
5220
xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
 
5221
{
 
5222
    JSXML *xml;
 
5223
 
 
5224
    if (hint == JSTYPE_OBJECT) {
 
5225
        /* Called from for..in code in js_Interpret: return an XMLList. */
 
5226
        xml = (JSXML *) JS_GetPrivate(cx, obj);
 
5227
        if (xml->xml_class != JSXML_CLASS_LIST) {
 
5228
            obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj));
 
5229
            if (!obj)
 
5230
                return JS_FALSE;
 
5231
        }
 
5232
        *vp = OBJECT_TO_JSVAL(obj);
 
5233
        return JS_TRUE;
 
5234
    }
 
5235
 
 
5236
    return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp);
 
5237
}
 
5238
 
 
5239
static JSBool
 
5240
xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
 
5241
              jsval *statep, jsid *idp)
 
5242
{
 
5243
    JSXML *xml;
 
5244
    uint32 length, index;
 
5245
    JSXMLArrayCursor *cursor;
 
5246
 
 
5247
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
5248
    length = JSXML_LENGTH(xml);
 
5249
 
 
5250
    switch (enum_op) {
 
5251
      case JSENUMERATE_INIT:
 
5252
        if (length == 0) {
 
5253
            cursor = NULL;
 
5254
        } else {
 
5255
            cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
 
5256
            if (!cursor)
 
5257
                return JS_FALSE;
 
5258
            XMLArrayCursorInit(cursor, &xml->xml_kids);
 
5259
        }
 
5260
        *statep = PRIVATE_TO_JSVAL(cursor);
 
5261
        if (idp)
 
5262
            *idp = INT_TO_JSID(length);
 
5263
        break;
 
5264
 
 
5265
      case JSENUMERATE_NEXT:
 
5266
        cursor = JSVAL_TO_PRIVATE(*statep);
 
5267
        if (cursor && cursor->array && (index = cursor->index) < length) {
 
5268
            *idp = INT_TO_JSID(index);
 
5269
            cursor->index = index + 1;
 
5270
            break;
 
5271
        }
 
5272
        /* FALL THROUGH */
 
5273
 
 
5274
      case JSENUMERATE_DESTROY:
 
5275
        cursor = JSVAL_TO_PRIVATE(*statep);
 
5276
        if (cursor) {
 
5277
            XMLArrayCursorFinish(cursor);
 
5278
            JS_free(cx, cursor);
 
5279
        }
 
5280
        *statep = JSVAL_NULL;
 
5281
        break;
 
5282
    }
 
5283
    return JS_TRUE;
 
5284
}
 
5285
 
 
5286
static JSBool
 
5287
xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 
5288
{
 
5289
    return JS_TRUE;
 
5290
}
 
5291
 
 
5292
static uint32
 
5293
xml_mark(JSContext *cx, JSObject *obj, void *arg)
 
5294
{
 
5295
    JSXML *xml;
 
5296
 
 
5297
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
5298
    GC_MARK(cx, xml, "private");
 
5299
    return js_Mark(cx, obj, NULL);
 
5300
}
 
5301
 
 
5302
static void
 
5303
xml_clear(JSContext *cx, JSObject *obj)
 
5304
{
 
5305
}
 
5306
 
 
5307
static JSBool
 
5308
HasSimpleContent(JSXML *xml)
 
5309
{
 
5310
    JSXML *kid;
 
5311
    JSBool simple;
 
5312
    uint32 i, n;
 
5313
 
 
5314
again:
 
5315
    switch (xml->xml_class) {
 
5316
      case JSXML_CLASS_COMMENT:
 
5317
      case JSXML_CLASS_PROCESSING_INSTRUCTION:
 
5318
        return JS_FALSE;
 
5319
      case JSXML_CLASS_LIST:
 
5320
        if (xml->xml_kids.length == 0)
 
5321
            return JS_TRUE;
 
5322
        if (xml->xml_kids.length == 1) {
 
5323
            kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
5324
            if (kid) {
 
5325
                xml = kid;
 
5326
                goto again;
 
5327
            }
 
5328
        }
 
5329
        /* FALL THROUGH */
 
5330
      default:
 
5331
        simple = JS_TRUE;
 
5332
        for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
5333
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
5334
            if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
5335
                simple = JS_FALSE;
 
5336
                break;
 
5337
            }
 
5338
        }
 
5339
        return simple;
 
5340
    }
 
5341
}
 
5342
 
 
5343
/*
 
5344
 * 11.2.2.1 Step 3(d) onward.
 
5345
 */
 
5346
static JSObject *
 
5347
xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
5348
{
 
5349
    JSTempValueRooter tvr;
 
5350
 
 
5351
    JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL));
 
5352
 
 
5353
    /*
 
5354
     * As our callers have a bad habit of passing a pointer to an unrooted
 
5355
     * local value as vp, we use a proper root here.
 
5356
     */
 
5357
    JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
 
5358
    if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value))
 
5359
        obj = NULL;
 
5360
    *vp = tvr.u.value;
 
5361
    JS_POP_TEMP_ROOT(cx, &tvr);
 
5362
    return obj;
 
5363
}
 
5364
 
 
5365
static JSBool
 
5366
xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
5367
{
 
5368
    return js_SetProperty(cx, obj, id, vp);
 
5369
}
 
5370
 
 
5371
static JSBool
 
5372
xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
 
5373
                    jsval *statep, jsid *idp, jsval *vp)
 
5374
{
 
5375
    JSXML *xml, *kid;
 
5376
    uint32 length, index;
 
5377
    JSXMLArrayCursor *cursor;
 
5378
    JSObject *kidobj;
 
5379
 
 
5380
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
5381
    length = JSXML_LENGTH(xml);
 
5382
    JS_ASSERT(INT_FITS_IN_JSVAL(length));
 
5383
 
 
5384
    switch (enum_op) {
 
5385
      case JSENUMERATE_INIT:
 
5386
        if (length == 0) {
 
5387
            cursor = NULL;
 
5388
        } else {
 
5389
            cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
 
5390
            if (!cursor)
 
5391
                return JS_FALSE;
 
5392
            XMLArrayCursorInit(cursor, &xml->xml_kids);
 
5393
        }
 
5394
        *statep = PRIVATE_TO_JSVAL(cursor);
 
5395
        if (idp)
 
5396
            *idp = INT_TO_JSID(length);
 
5397
        if (vp)
 
5398
            *vp = JSVAL_VOID;
 
5399
        break;
 
5400
 
 
5401
      case JSENUMERATE_NEXT:
 
5402
        cursor = JSVAL_TO_PRIVATE(*statep);
 
5403
        if (cursor && cursor->array && (index = cursor->index) < length) {
 
5404
            while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) {
 
5405
                if (++index == length)
 
5406
                    goto destroy;
 
5407
            }
 
5408
            kidobj = js_GetXMLObject(cx, kid);
 
5409
            if (!kidobj)
 
5410
                return JS_FALSE;
 
5411
            JS_ASSERT(INT_FITS_IN_JSVAL(index));
 
5412
            *idp = INT_TO_JSID(index);
 
5413
            *vp = OBJECT_TO_JSVAL(kidobj);
 
5414
            cursor->index = index + 1;
 
5415
            break;
 
5416
        }
 
5417
        /* FALL THROUGH */
 
5418
 
 
5419
      case JSENUMERATE_DESTROY:
 
5420
        cursor = JSVAL_TO_PRIVATE(*statep);
 
5421
        if (cursor) {
 
5422
      destroy:
 
5423
            XMLArrayCursorFinish(cursor);
 
5424
            JS_free(cx, cursor);
 
5425
        }
 
5426
        *statep = JSVAL_NULL;
 
5427
        break;
 
5428
    }
 
5429
    return JS_TRUE;
 
5430
}
 
5431
 
 
5432
static JSBool
 
5433
xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 
5434
{
 
5435
    JSXML *xml, *vxml;
 
5436
    JSObject *vobj;
 
5437
    JSBool ok;
 
5438
    JSString *str, *vstr;
 
5439
    jsdouble d, d2;
 
5440
 
 
5441
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
5442
    vxml = NULL;
 
5443
    if (!JSVAL_IS_PRIMITIVE(v)) {
 
5444
        vobj = JSVAL_TO_OBJECT(v);
 
5445
        if (OBJECT_IS_XML(cx, vobj))
 
5446
            vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
5447
    }
 
5448
 
 
5449
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
5450
        ok = Equals(cx, xml, v, bp);
 
5451
    } else if (vxml) {
 
5452
        if (vxml->xml_class == JSXML_CLASS_LIST) {
 
5453
            ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
 
5454
        } else {
 
5455
            if (((xml->xml_class == JSXML_CLASS_TEXT ||
 
5456
                  xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
 
5457
                 HasSimpleContent(vxml)) ||
 
5458
                ((vxml->xml_class == JSXML_CLASS_TEXT ||
 
5459
                  vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
 
5460
                 HasSimpleContent(xml))) {
 
5461
                ok = js_EnterLocalRootScope(cx);
 
5462
                if (ok) {
 
5463
                    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
 
5464
                    vstr = js_ValueToString(cx, v);
 
5465
                    ok = str && vstr;
 
5466
                    if (ok)
 
5467
                        *bp = js_EqualStrings(str, vstr);
 
5468
                    js_LeaveLocalRootScope(cx);
 
5469
                }
 
5470
            } else {
 
5471
                ok = XMLEquals(cx, xml, vxml, bp);
 
5472
            }
 
5473
        }
 
5474
    } else {
 
5475
        ok = js_EnterLocalRootScope(cx);
 
5476
        if (ok) {
 
5477
            if (HasSimpleContent(xml)) {
 
5478
                str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
 
5479
                vstr = js_ValueToString(cx, v);
 
5480
                ok = str && vstr;
 
5481
                if (ok)
 
5482
                    *bp = js_EqualStrings(str, vstr);
 
5483
            } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
 
5484
                str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
 
5485
                if (!str) {
 
5486
                    ok = JS_FALSE;
 
5487
                } else if (JSVAL_IS_STRING(v)) {
 
5488
                    *bp = js_EqualStrings(str, JSVAL_TO_STRING(v));
 
5489
                } else {
 
5490
                    ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
 
5491
                    if (ok) {
 
5492
                        d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
 
5493
                                             : *JSVAL_TO_DOUBLE(v);
 
5494
                        *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
 
5495
                    }
 
5496
                }
 
5497
            } else {
 
5498
                *bp = JS_FALSE;
 
5499
            }
 
5500
            js_LeaveLocalRootScope(cx);
 
5501
        }
 
5502
    }
 
5503
    return ok;
 
5504
}
 
5505
 
 
5506
static JSBool
 
5507
xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp)
 
5508
{
 
5509
    JSBool ok;
 
5510
    JSObject *listobj, *robj;
 
5511
    JSXML *list, *lxml, *rxml;
 
5512
 
 
5513
    ok = js_EnterLocalRootScope(cx);
 
5514
    if (!ok)
 
5515
        return JS_FALSE;
 
5516
 
 
5517
    listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
5518
    if (!listobj) {
 
5519
        ok = JS_FALSE;
 
5520
        goto out;
 
5521
    }
 
5522
 
 
5523
    list = (JSXML *) JS_GetPrivate(cx, listobj);
 
5524
    lxml = (JSXML *) JS_GetPrivate(cx, obj);
 
5525
    ok = Append(cx, list, lxml);
 
5526
    if (!ok)
 
5527
        goto out;
 
5528
 
 
5529
    if (VALUE_IS_XML(cx, v)) {
 
5530
        rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
5531
    } else {
 
5532
        robj = ToXML(cx, v);
 
5533
        if (!robj) {
 
5534
            ok = JS_FALSE;
 
5535
            goto out;
 
5536
        }
 
5537
        rxml = (JSXML *) JS_GetPrivate(cx, robj);
 
5538
    }
 
5539
    ok = Append(cx, list, rxml);
 
5540
    if (!ok)
 
5541
        goto out;
 
5542
 
 
5543
    *vp = OBJECT_TO_JSVAL(listobj);
 
5544
out:
 
5545
    js_LeaveLocalRootScopeWithResult(cx, *vp);
 
5546
    return ok;
 
5547
}
 
5548
 
 
5549
/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */
 
5550
JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = {
 
5551
  { js_NewObjectMap,            js_DestroyObjectMap,
 
5552
    xml_lookupProperty,         xml_defineProperty,
 
5553
    xml_getProperty,            xml_setProperty,
 
5554
    xml_getAttributes,          xml_setAttributes,
 
5555
    xml_deleteProperty,         xml_defaultValue,
 
5556
    xml_enumerate,              js_CheckAccess,
 
5557
    NULL,                       NULL,
 
5558
    NULL,                       NULL,
 
5559
    NULL,                       xml_hasInstance,
 
5560
    js_SetProtoOrParent,        js_SetProtoOrParent,
 
5561
    xml_mark,                   xml_clear,
 
5562
    NULL,                       NULL },
 
5563
    xml_getMethod,              xml_setMethod,
 
5564
    xml_enumerateValues,        xml_equality,
 
5565
    xml_concatenate
 
5566
};
 
5567
 
 
5568
static JSObjectOps *
 
5569
xml_getObjectOps(JSContext *cx, JSClass *clasp)
 
5570
{
 
5571
    return &js_XMLObjectOps.base;
 
5572
}
 
5573
 
 
5574
JS_FRIEND_DATA(JSClass) js_XMLClass = {
 
5575
    js_XML_str,
 
5576
    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
 
5577
    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
 
5578
    JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    xml_finalize,
 
5579
    xml_getObjectOps,  NULL,              NULL,              NULL,
 
5580
    NULL,              NULL,              NULL,              NULL
 
5581
};
 
5582
 
 
5583
static JSObject *
 
5584
CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp,
 
5585
                        uintN argc, jsval *argv)
 
5586
{
 
5587
    JSObject *tmp;
 
5588
    jsval rval;
 
5589
 
 
5590
    while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
 
5591
        obj = tmp;
 
5592
    if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval))
 
5593
        return NULL;
 
5594
    JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval));
 
5595
    return JSVAL_TO_OBJECT(rval);
 
5596
}
 
5597
 
 
5598
static JSXML *
 
5599
StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv)
 
5600
{
 
5601
    JSXML *xml;
 
5602
    JSFunction *fun;
 
5603
 
 
5604
    JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2]));
 
5605
 
 
5606
    xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv);
 
5607
    if (!xml || xml->xml_class != JSXML_CLASS_LIST)
 
5608
        return xml;
 
5609
 
 
5610
    if (xml->xml_kids.length == 1) {
 
5611
        xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
5612
        if (xml) {
 
5613
            *objp = js_GetXMLObject(cx, xml);
 
5614
            if (!*objp)
 
5615
                return NULL;
 
5616
            argv[-1] = OBJECT_TO_JSVAL(*objp);
 
5617
            return xml;
 
5618
        }
 
5619
    }
 
5620
 
 
5621
    fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
 
5622
    if (fun) {
 
5623
        char numBuf[12];
 
5624
        JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
 
5625
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
5626
                             JSMSG_NON_LIST_XML_METHOD,
 
5627
                             JS_GetFunctionName(fun), numBuf);
 
5628
    }
 
5629
    return NULL;
 
5630
}
 
5631
 
 
5632
#define XML_METHOD_PROLOG                                                     \
 
5633
    JS_BEGIN_MACRO                                                            \
 
5634
        xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv);   \
 
5635
        if (!xml)                                                             \
 
5636
            return JS_FALSE;                                                  \
 
5637
    JS_END_MACRO
 
5638
 
 
5639
#define NON_LIST_XML_METHOD_PROLOG                                            \
 
5640
    JS_BEGIN_MACRO                                                            \
 
5641
        xml = StartNonListXMLMethod(cx, &obj, argv);                          \
 
5642
        if (!xml)                                                             \
 
5643
            return JS_FALSE;                                                  \
 
5644
        JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);                        \
 
5645
    JS_END_MACRO
 
5646
 
 
5647
static JSBool
 
5648
xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5649
                 jsval *rval)
 
5650
{
 
5651
    JSXML *xml;
 
5652
    JSObject *nsobj;
 
5653
    JSXMLNamespace *ns;
 
5654
 
 
5655
    NON_LIST_XML_METHOD_PROLOG;
 
5656
    if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
5657
        return JS_TRUE;
 
5658
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
5659
    if (!xml)
 
5660
        return JS_FALSE;
 
5661
 
 
5662
    nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv);
 
5663
    if (!nsobj)
 
5664
        return JS_FALSE;
 
5665
    argv[0] = OBJECT_TO_JSVAL(nsobj);
 
5666
 
 
5667
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
 
5668
    if (!AddInScopeNamespace(cx, xml, ns))
 
5669
        return JS_FALSE;
 
5670
    ns->declared = JS_TRUE;
 
5671
    *rval = OBJECT_TO_JSVAL(obj);
 
5672
    return JS_TRUE;
 
5673
}
 
5674
 
 
5675
static JSBool
 
5676
xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5677
                jsval *rval)
 
5678
{
 
5679
    JSXML *xml, *vxml;
 
5680
    jsval name, v;
 
5681
    JSObject *vobj;
 
5682
 
 
5683
    NON_LIST_XML_METHOD_PROLOG;
 
5684
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
5685
    if (!xml)
 
5686
        return JS_FALSE;
 
5687
 
 
5688
    if (!js_GetAnyName(cx, &name))
 
5689
        return JS_FALSE;
 
5690
 
 
5691
    if (!GetProperty(cx, obj, name, &v))
 
5692
        return JS_FALSE;
 
5693
 
 
5694
    JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
 
5695
    vobj = JSVAL_TO_OBJECT(v);
 
5696
    JS_ASSERT(OBJECT_IS_XML(cx, vobj));
 
5697
    vxml = (JSXML *) JS_GetPrivate(cx, vobj);
 
5698
    JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
 
5699
 
 
5700
    if (!IndexToIdVal(cx, vxml->xml_kids.length, &name))
 
5701
        return JS_FALSE;
 
5702
    if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0]))
 
5703
        return JS_FALSE;
 
5704
 
 
5705
    *rval = OBJECT_TO_JSVAL(obj);
 
5706
    return JS_TRUE;
 
5707
}
 
5708
 
 
5709
/* XML and XMLList */
 
5710
static JSBool
 
5711
xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5712
              jsval *rval)
 
5713
{
 
5714
    JSXMLQName *qn;
 
5715
 
 
5716
    qn = ToAttributeName(cx, argv[0]);
 
5717
    if (!qn)
 
5718
        return JS_FALSE;
 
5719
    argv[0] = OBJECT_TO_JSVAL(qn->object);      /* local root */
 
5720
    return GetProperty(cx, obj, argv[0], rval);
 
5721
}
 
5722
 
 
5723
/* XML and XMLList */
 
5724
static JSBool
 
5725
xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5726
               jsval *rval)
 
5727
{
 
5728
    jsval name;
 
5729
    JSXMLQName *qn;
 
5730
    JSTempValueRooter tvr;
 
5731
    JSBool ok;
 
5732
 
 
5733
    name = ATOM_KEY(cx->runtime->atomState.starAtom);
 
5734
    qn = ToAttributeName(cx, name);
 
5735
    if (!qn)
 
5736
        return JS_FALSE;
 
5737
    name = OBJECT_TO_JSVAL(qn->object);
 
5738
    JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr);
 
5739
    ok = GetProperty(cx, obj, name, rval);
 
5740
    JS_POP_TEMP_ROOT(cx, &tvr);
 
5741
    return ok;
 
5742
}
 
5743
 
 
5744
static JSXML *
 
5745
xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
 
5746
{
 
5747
    JSObject *listobj;
 
5748
    JSXML *list;
 
5749
 
 
5750
    listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
5751
    if (!listobj)
 
5752
        return NULL;
 
5753
 
 
5754
    *rval = OBJECT_TO_JSVAL(listobj);
 
5755
    list = (JSXML *) JS_GetPrivate(cx, listobj);
 
5756
    list->xml_target = xml;
 
5757
    return list;
 
5758
}
 
5759
 
 
5760
static JSBool
 
5761
xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
 
5762
                 jsval *rval)
 
5763
{
 
5764
    uint32 index;
 
5765
    JSXML *kid;
 
5766
    JSObject *kidobj;
 
5767
 
 
5768
    /* ECMA-357 13.4.4.6 */
 
5769
    JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
 
5770
 
 
5771
    if (js_IdIsIndex(name, &index)) {
 
5772
        if (index >= JSXML_LENGTH(xml)) {
 
5773
            *rval = JSVAL_VOID;
 
5774
        } else {
 
5775
            kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
 
5776
            if (!kid) {
 
5777
                *rval = JSVAL_VOID;
 
5778
            } else {
 
5779
                kidobj = js_GetXMLObject(cx, kid);
 
5780
                if (!kidobj)
 
5781
                    return JS_FALSE;
 
5782
                *rval = OBJECT_TO_JSVAL(kidobj);
 
5783
            }
 
5784
        }
 
5785
        return JS_TRUE;
 
5786
    }
 
5787
 
 
5788
    return GetProperty(cx, obj, name, rval);
 
5789
}
 
5790
 
 
5791
/* XML and XMLList */
 
5792
static JSBool
 
5793
xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
5794
{
 
5795
    JSXML *xml, *list, *kid, *vxml;
 
5796
    JSXMLArrayCursor cursor;
 
5797
    jsval name, v;
 
5798
    JSObject *kidobj;
 
5799
 
 
5800
    XML_METHOD_PROLOG;
 
5801
    name = argv[0];
 
5802
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
5803
        /* ECMA-357 13.5.4.4 */
 
5804
        list = xml_list_helper(cx, xml, rval);
 
5805
        if (!list)
 
5806
            return JS_FALSE;
 
5807
 
 
5808
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
5809
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
5810
            kidobj = js_GetXMLObject(cx, kid);
 
5811
            if (!kidobj)
 
5812
                break;
 
5813
            if (!xml_child_helper(cx, kidobj, kid, name, &v))
 
5814
                break;
 
5815
            if (JSVAL_IS_VOID(v)) {
 
5816
                /* The property didn't exist in this kid. */
 
5817
                continue;
 
5818
            }
 
5819
 
 
5820
            JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
 
5821
            vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
5822
            if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
 
5823
                !Append(cx, list, vxml)) {
 
5824
                break;
 
5825
            }
 
5826
        }
 
5827
        XMLArrayCursorFinish(&cursor);
 
5828
        return !kid;
 
5829
    }
 
5830
 
 
5831
    /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
 
5832
    if (!xml_child_helper(cx, obj, xml, name, rval))
 
5833
        return JS_FALSE;
 
5834
    if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval))
 
5835
        return JS_FALSE;
 
5836
    return JS_TRUE;
 
5837
}
 
5838
 
 
5839
static JSBool
 
5840
xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5841
               jsval *rval)
 
5842
{
 
5843
    JSXML *xml, *parent;
 
5844
    uint32 i, n;
 
5845
 
 
5846
    NON_LIST_XML_METHOD_PROLOG;
 
5847
    parent = xml->parent;
 
5848
    if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
 
5849
        *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
 
5850
        return JS_TRUE;
 
5851
    }
 
5852
    for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
 
5853
        if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
 
5854
            break;
 
5855
    }
 
5856
    JS_ASSERT(i < n);
 
5857
    return js_NewNumberValue(cx, i, rval);
 
5858
}
 
5859
 
 
5860
/* XML and XMLList */
 
5861
static JSBool
 
5862
xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5863
             jsval *rval)
 
5864
{
 
5865
    jsval name;
 
5866
 
 
5867
    name = ATOM_KEY(cx->runtime->atomState.starAtom);
 
5868
    return GetProperty(cx, obj, name, rval);
 
5869
}
 
5870
 
 
5871
/* XML and XMLList */
 
5872
static JSBool
 
5873
xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5874
             jsval *rval)
 
5875
{
 
5876
    JSXML *xml, *list, *kid, *vxml;
 
5877
    JSBool ok;
 
5878
    uint32 i, n;
 
5879
    JSObject *kidobj;
 
5880
    jsval v;
 
5881
 
 
5882
    XML_METHOD_PROLOG;
 
5883
    list = xml_list_helper(cx, xml, rval);
 
5884
    if (!list)
 
5885
        return JS_FALSE;
 
5886
 
 
5887
    ok = JS_TRUE;
 
5888
 
 
5889
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
5890
        /* 13.5.4.6 Step 2. */
 
5891
        for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
5892
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
5893
            if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
5894
                ok = js_EnterLocalRootScope(cx);
 
5895
                if (!ok)
 
5896
                    break;
 
5897
                kidobj = js_GetXMLObject(cx, kid);
 
5898
                if (kidobj) {
 
5899
                    ok = xml_comments(cx, kidobj, argc, argv, &v);
 
5900
                } else {
 
5901
                    ok = JS_FALSE;
 
5902
                    v = JSVAL_NULL;
 
5903
                }
 
5904
                js_LeaveLocalRootScopeWithResult(cx, v);
 
5905
                if (!ok)
 
5906
                    break;
 
5907
                vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
5908
                if (JSXML_LENGTH(vxml) != 0) {
 
5909
                    ok = Append(cx, list, vxml);
 
5910
                    if (!ok)
 
5911
                        break;
 
5912
                }
 
5913
            }
 
5914
        }
 
5915
    } else {
 
5916
        /* 13.4.4.9 Step 2. */
 
5917
        for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
5918
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
5919
            if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
 
5920
                ok = Append(cx, list, kid);
 
5921
                if (!ok)
 
5922
                    break;
 
5923
            }
 
5924
        }
 
5925
    }
 
5926
 
 
5927
    return ok;
 
5928
}
 
5929
 
 
5930
/* XML and XMLList */
 
5931
static JSBool
 
5932
xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5933
             jsval *rval)
 
5934
{
 
5935
    JSXML *xml, *kid;
 
5936
    jsval value;
 
5937
    JSBool eq;
 
5938
    JSXMLArrayCursor cursor;
 
5939
    JSObject *kidobj;
 
5940
 
 
5941
    XML_METHOD_PROLOG;
 
5942
    value = argv[0];
 
5943
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
5944
        eq = JS_FALSE;
 
5945
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
5946
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
5947
            kidobj = js_GetXMLObject(cx, kid);
 
5948
            if (!kidobj || !xml_equality(cx, kidobj, value, &eq))
 
5949
                break;
 
5950
            if (eq)
 
5951
                break;
 
5952
        }
 
5953
        XMLArrayCursorFinish(&cursor);
 
5954
        if (kid && !eq)
 
5955
            return JS_FALSE;
 
5956
    } else {
 
5957
        if (!xml_equality(cx, obj, value, &eq))
 
5958
            return JS_FALSE;
 
5959
    }
 
5960
    *rval = BOOLEAN_TO_JSVAL(eq);
 
5961
    return JS_TRUE;
 
5962
}
 
5963
 
 
5964
/* XML and XMLList */
 
5965
static JSBool
 
5966
xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
5967
{
 
5968
    JSXML *xml, *copy;
 
5969
 
 
5970
    XML_METHOD_PROLOG;
 
5971
    copy = DeepCopy(cx, xml, NULL, 0);
 
5972
    if (!copy)
 
5973
        return JS_FALSE;
 
5974
    *rval = OBJECT_TO_JSVAL(copy->object);
 
5975
    return JS_TRUE;
 
5976
}
 
5977
 
 
5978
/* XML and XMLList */
 
5979
static JSBool
 
5980
xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5981
                jsval *rval)
 
5982
{
 
5983
    JSXML *xml, *list;
 
5984
    jsval name;
 
5985
 
 
5986
    XML_METHOD_PROLOG;
 
5987
    name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
 
5988
    list = Descendants(cx, xml, name);
 
5989
    if (!list)
 
5990
        return JS_FALSE;
 
5991
    *rval = OBJECT_TO_JSVAL(list->object);
 
5992
    return JS_TRUE;
 
5993
}
 
5994
 
 
5995
/* XML and XMLList */
 
5996
static JSBool
 
5997
xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
5998
             jsval *rval)
 
5999
{
 
6000
    JSXML *xml, *list, *kid, *vxml;
 
6001
    jsval name, v;
 
6002
    JSXMLQName *nameqn;
 
6003
    jsid funid;
 
6004
    JSBool ok;
 
6005
    JSXMLArrayCursor cursor;
 
6006
    JSObject *kidobj;
 
6007
    uint32 i, n;
 
6008
 
 
6009
    XML_METHOD_PROLOG;
 
6010
    name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
 
6011
    nameqn = ToXMLName(cx, name, &funid);
 
6012
    if (!nameqn)
 
6013
        return JS_FALSE;
 
6014
    argv[0] = OBJECT_TO_JSVAL(nameqn->object);
 
6015
 
 
6016
    list = xml_list_helper(cx, xml, rval);
 
6017
    if (!list)
 
6018
        return JS_FALSE;
 
6019
    if (funid)
 
6020
        return JS_TRUE;
 
6021
 
 
6022
    list->xml_targetprop = nameqn;
 
6023
    ok = JS_TRUE;
 
6024
 
 
6025
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
6026
        /* 13.5.4.6 */
 
6027
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
6028
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
6029
            if (kid->xml_class == JSXML_CLASS_ELEMENT) {
 
6030
                ok = js_EnterLocalRootScope(cx);
 
6031
                if (!ok)
 
6032
                    break;
 
6033
                kidobj = js_GetXMLObject(cx, kid);
 
6034
                if (kidobj) {
 
6035
                    ok = xml_elements(cx, kidobj, argc, argv, &v);
 
6036
                } else {
 
6037
                    ok = JS_FALSE;
 
6038
                    v = JSVAL_NULL;
 
6039
                }
 
6040
                js_LeaveLocalRootScopeWithResult(cx, v);
 
6041
                if (!ok)
 
6042
                    break;
 
6043
                vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
6044
                if (JSXML_LENGTH(vxml) != 0) {
 
6045
                    ok = Append(cx, list, vxml);
 
6046
                    if (!ok)
 
6047
                        break;
 
6048
                }
 
6049
            }
 
6050
        }
 
6051
        XMLArrayCursorFinish(&cursor);
 
6052
    } else {
 
6053
        for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
6054
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
6055
            if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
 
6056
                MatchElemName(nameqn, kid)) {
 
6057
                ok = Append(cx, list, kid);
 
6058
                if (!ok)
 
6059
                    break;
 
6060
            }
 
6061
        }
 
6062
    }
 
6063
 
 
6064
    return ok;
 
6065
}
 
6066
 
 
6067
/* XML and XMLList */
 
6068
static JSBool
 
6069
xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6070
                   jsval *rval)
 
6071
{
 
6072
    jsval name;
 
6073
    JSObject *pobj;
 
6074
    JSProperty *prop;
 
6075
 
 
6076
    if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv))
 
6077
        return JS_FALSE;
 
6078
 
 
6079
    name = argv[0];
 
6080
    if (!HasProperty(cx, obj, name, &pobj, &prop))
 
6081
        return JS_FALSE;
 
6082
    if (!prop) {
 
6083
        return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv,
 
6084
                                       rval);
 
6085
    }
 
6086
    DROP_PROPERTY(cx, pobj, prop);
 
6087
    *rval = JSVAL_TRUE;
 
6088
    return JS_TRUE;
 
6089
}
 
6090
 
 
6091
/* XML and XMLList */
 
6092
static JSBool
 
6093
xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6094
                      jsval *rval)
 
6095
{
 
6096
    JSXML *xml, *kid;
 
6097
    JSObject *kidobj;
 
6098
    uint32 i, n;
 
6099
 
 
6100
    XML_METHOD_PROLOG;
 
6101
again:
 
6102
    switch (xml->xml_class) {
 
6103
      case JSXML_CLASS_ATTRIBUTE:
 
6104
      case JSXML_CLASS_COMMENT:
 
6105
      case JSXML_CLASS_PROCESSING_INSTRUCTION:
 
6106
      case JSXML_CLASS_TEXT:
 
6107
        *rval = JSVAL_FALSE;
 
6108
        break;
 
6109
      case JSXML_CLASS_LIST:
 
6110
        if (xml->xml_kids.length == 0) {
 
6111
            *rval = JSVAL_TRUE;
 
6112
        } else if (xml->xml_kids.length == 1) {
 
6113
            kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
6114
            if (kid) {
 
6115
                kidobj = js_GetXMLObject(cx, kid);
 
6116
                if (!kidobj)
 
6117
                    return JS_FALSE;
 
6118
                obj = kidobj;
 
6119
                xml = (JSXML *) JS_GetPrivate(cx, obj);
 
6120
                goto again;
 
6121
            }
 
6122
        }
 
6123
        /* FALL THROUGH */
 
6124
      default:
 
6125
        *rval = JSVAL_FALSE;
 
6126
        for (i = 0, n = xml->xml_kids.length; i < n; i++) {
 
6127
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
6128
            if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
6129
                *rval = JSVAL_TRUE;
 
6130
                break;
 
6131
            }
 
6132
        }
 
6133
        break;
 
6134
    }
 
6135
    return JS_TRUE;
 
6136
}
 
6137
 
 
6138
/* XML and XMLList */
 
6139
static JSBool
 
6140
xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6141
                     jsval *rval)
 
6142
{
 
6143
    JSXML *xml;
 
6144
 
 
6145
    XML_METHOD_PROLOG;
 
6146
    *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
 
6147
    return JS_TRUE;
 
6148
}
 
6149
 
 
6150
typedef struct JSTempRootedNSArray {
 
6151
    JSTempValueRooter   tvr;
 
6152
    JSXMLArray          array;
 
6153
    jsval               value;  /* extra root for temporaries */
 
6154
} JSTempRootedNSArray;
 
6155
 
 
6156
JS_STATIC_DLL_CALLBACK(void)
 
6157
mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr)
 
6158
{
 
6159
    JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr;
 
6160
 
 
6161
    namespace_mark_vector(cx,
 
6162
                          (JSXMLNamespace **)tmp->array.vector,
 
6163
                          tmp->array.length);
 
6164
    XMLArrayCursorMark(cx, tmp->array.cursors);
 
6165
    if (JSVAL_IS_GCTHING(tmp->value))
 
6166
        GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value");
 
6167
}
 
6168
 
 
6169
static void
 
6170
InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
 
6171
{
 
6172
    XMLArrayInit(cx, &tmp->array, 0);
 
6173
    tmp->value = JSVAL_NULL;
 
6174
    JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr);
 
6175
}
 
6176
 
 
6177
static void
 
6178
FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
 
6179
{
 
6180
    JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array);
 
6181
    JS_POP_TEMP_ROOT(cx, &tmp->tvr);
 
6182
    XMLArrayFinish(cx, &tmp->array);
 
6183
}
 
6184
 
 
6185
/*
 
6186
 * Populate a new JS array with elements of JSTempRootedNSArray.array and
 
6187
 * place the result into rval.  rval must point to a rooted location.
 
6188
 */
 
6189
static JSBool
 
6190
TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval)
 
6191
{
 
6192
    JSObject *arrayobj;
 
6193
    uint32 i, n;
 
6194
    JSXMLNamespace *ns;
 
6195
    JSObject *nsobj;
 
6196
 
 
6197
    arrayobj = js_NewArrayObject(cx, 0, NULL);
 
6198
    if (!arrayobj)
 
6199
        return JS_FALSE;
 
6200
    *rval = OBJECT_TO_JSVAL(arrayobj);
 
6201
    for (i = 0, n = tmp->array.length; i < n; i++) {
 
6202
        ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace);
 
6203
        if (!ns)
 
6204
            continue;
 
6205
        nsobj = js_GetXMLNamespaceObject(cx, ns);
 
6206
        if (!nsobj)
 
6207
            return JS_FALSE;
 
6208
        tmp->value = OBJECT_TO_JSVAL(nsobj);
 
6209
        if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value))
 
6210
            return JS_FALSE;
 
6211
    }
 
6212
    return JS_TRUE;
 
6213
}
 
6214
 
 
6215
static JSBool
 
6216
FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
 
6217
{
 
6218
    uint32 length, i, j, n;
 
6219
    JSXMLNamespace *ns, *ns2;
 
6220
 
 
6221
    length = nsarray->length;
 
6222
    do {
 
6223
        if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
6224
            continue;
 
6225
        for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
 
6226
            ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
 
6227
            if (!ns)
 
6228
                continue;
 
6229
 
 
6230
            for (j = 0; j < length; j++) {
 
6231
                ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace);
 
6232
                if (ns2 &&
 
6233
                    ((ns2->prefix && ns->prefix)
 
6234
                     ? js_EqualStrings(ns2->prefix, ns->prefix)
 
6235
                     : js_EqualStrings(ns2->uri, ns->uri))) {
 
6236
                    break;
 
6237
                }
 
6238
            }
 
6239
 
 
6240
            if (j == length) {
 
6241
                if (!XMLARRAY_APPEND(cx, nsarray, ns))
 
6242
                    return JS_FALSE;
 
6243
                ++length;
 
6244
            }
 
6245
        }
 
6246
    } while ((xml = xml->parent) != NULL);
 
6247
    JS_ASSERT(length == nsarray->length);
 
6248
 
 
6249
    return JS_TRUE;
 
6250
}
 
6251
 
 
6252
static JSBool
 
6253
xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6254
                      jsval *rval)
 
6255
{
 
6256
    JSXML *xml;
 
6257
    JSTempRootedNSArray namespaces;
 
6258
    JSBool ok;
 
6259
 
 
6260
    NON_LIST_XML_METHOD_PROLOG;
 
6261
 
 
6262
    InitTempNSArray(cx, &namespaces);
 
6263
    ok = FindInScopeNamespaces(cx, xml, &namespaces.array) &&
 
6264
         TempNSArrayToJSArray(cx, &namespaces, rval);
 
6265
    FinishTempNSArray(cx, &namespaces);
 
6266
    return ok;
 
6267
}
 
6268
 
 
6269
static JSBool
 
6270
xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6271
                     jsval *rval)
 
6272
{
 
6273
    JSXML *xml, *kid;
 
6274
    jsval arg;
 
6275
    uint32 i;
 
6276
 
 
6277
    NON_LIST_XML_METHOD_PROLOG;
 
6278
    if (!JSXML_HAS_KIDS(xml))
 
6279
        return JS_TRUE;
 
6280
 
 
6281
    arg = argv[0];
 
6282
    if (JSVAL_IS_NULL(arg)) {
 
6283
        kid = NULL;
 
6284
        i = 0;
 
6285
    } else {
 
6286
        if (!VALUE_IS_XML(cx, arg))
 
6287
            return JS_TRUE;
 
6288
        kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
 
6289
        i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
 
6290
        if (i == XML_NOT_FOUND)
 
6291
            return JS_TRUE;
 
6292
        ++i;
 
6293
    }
 
6294
 
 
6295
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6296
    if (!xml)
 
6297
        return JS_FALSE;
 
6298
    if (!Insert(cx, xml, i, argv[1]))
 
6299
        return JS_FALSE;
 
6300
    *rval = OBJECT_TO_JSVAL(obj);
 
6301
    return JS_TRUE;
 
6302
}
 
6303
 
 
6304
static JSBool
 
6305
xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6306
                      jsval *rval)
 
6307
{
 
6308
    JSXML *xml, *kid;
 
6309
    jsval arg;
 
6310
    uint32 i;
 
6311
 
 
6312
    NON_LIST_XML_METHOD_PROLOG;
 
6313
    if (!JSXML_HAS_KIDS(xml))
 
6314
        return JS_TRUE;
 
6315
 
 
6316
    arg = argv[0];
 
6317
    if (JSVAL_IS_NULL(arg)) {
 
6318
        kid = NULL;
 
6319
        i = xml->xml_kids.length;
 
6320
    } else {
 
6321
        if (!VALUE_IS_XML(cx, arg))
 
6322
            return JS_TRUE;
 
6323
        kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
 
6324
        i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
 
6325
        if (i == XML_NOT_FOUND)
 
6326
            return JS_TRUE;
 
6327
    }
 
6328
 
 
6329
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6330
    if (!xml)
 
6331
        return JS_FALSE;
 
6332
    if (!Insert(cx, xml, i, argv[1]))
 
6333
        return JS_FALSE;
 
6334
    *rval = OBJECT_TO_JSVAL(obj);
 
6335
    return JS_TRUE;
 
6336
}
 
6337
 
 
6338
/* XML and XMLList */
 
6339
static JSBool
 
6340
xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
6341
{
 
6342
    JSXML *xml;
 
6343
 
 
6344
    XML_METHOD_PROLOG;
 
6345
    if (xml->xml_class != JSXML_CLASS_LIST) {
 
6346
        *rval = JSVAL_ONE;
 
6347
    } else {
 
6348
        if (!js_NewNumberValue(cx, xml->xml_kids.length, rval))
 
6349
            return JS_FALSE;
 
6350
    }
 
6351
    return JS_TRUE;
 
6352
}
 
6353
 
 
6354
static JSBool
 
6355
xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6356
              jsval *rval)
 
6357
{
 
6358
    JSXML *xml;
 
6359
 
 
6360
    NON_LIST_XML_METHOD_PROLOG;
 
6361
    *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL;
 
6362
    return JS_TRUE;
 
6363
}
 
6364
 
 
6365
static JSBool
 
6366
xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
6367
{
 
6368
    JSXML *xml;
 
6369
    JSObject *nameobj;
 
6370
 
 
6371
    NON_LIST_XML_METHOD_PROLOG;
 
6372
    if (!xml->name) {
 
6373
        *rval = JSVAL_NULL;
 
6374
    } else {
 
6375
        nameobj = js_GetXMLQNameObject(cx, xml->name);
 
6376
        if (!nameobj)
 
6377
            return JS_FALSE;
 
6378
        *rval = OBJECT_TO_JSVAL(nameobj);
 
6379
    }
 
6380
    return JS_TRUE;
 
6381
}
 
6382
 
 
6383
static JSBool
 
6384
xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6385
              jsval *rval)
 
6386
{
 
6387
    JSXML *xml;
 
6388
    JSString *prefix;
 
6389
    JSTempRootedNSArray inScopeNSes;
 
6390
    JSBool ok;
 
6391
    jsuint i, length;
 
6392
    JSXMLNamespace *ns;
 
6393
    JSObject *nsobj;
 
6394
 
 
6395
    NON_LIST_XML_METHOD_PROLOG;
 
6396
    if (argc == 0 && !JSXML_HAS_NAME(xml)) {
 
6397
        *rval = JSVAL_NULL;
 
6398
        return JS_TRUE;
 
6399
    }
 
6400
 
 
6401
    if (argc == 0) {
 
6402
        prefix = NULL;
 
6403
    } else {
 
6404
        prefix = js_ValueToString(cx, argv[0]);
 
6405
        if (!prefix)
 
6406
            return JS_FALSE;
 
6407
        argv[0] = STRING_TO_JSVAL(prefix);      /* local root */
 
6408
    }
 
6409
 
 
6410
    /* After this point the control must flow through label out. */
 
6411
    InitTempNSArray(cx, &inScopeNSes);
 
6412
    ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array);
 
6413
    if (!ok)
 
6414
        goto out;
 
6415
 
 
6416
    if (!prefix) {
 
6417
        ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
 
6418
        if (!ns) {
 
6419
            ok = JS_FALSE;
 
6420
            goto out;
 
6421
        }
 
6422
    } else {
 
6423
        ns = NULL;
 
6424
        for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
 
6425
            ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace);
 
6426
            if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix))
 
6427
                break;
 
6428
            ns = NULL;
 
6429
        }
 
6430
    }
 
6431
 
 
6432
    if (!ns) {
 
6433
        *rval = JSVAL_VOID;
 
6434
    } else {
 
6435
        nsobj = js_GetXMLNamespaceObject(cx, ns);
 
6436
        if (!nsobj) {
 
6437
            ok = JS_FALSE;
 
6438
            goto out;
 
6439
        }
 
6440
        *rval = OBJECT_TO_JSVAL(nsobj);
 
6441
    }
 
6442
 
 
6443
  out:
 
6444
    FinishTempNSArray(cx, &inScopeNSes);
 
6445
    return JS_TRUE;
 
6446
}
 
6447
 
 
6448
static JSBool
 
6449
xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6450
                          jsval *rval)
 
6451
{
 
6452
    JSXML *xml, *yml;
 
6453
    JSBool ok;
 
6454
    JSTempRootedNSArray ancestors, declared;
 
6455
    uint32 i, n;
 
6456
    JSXMLNamespace *ns;
 
6457
 
 
6458
    NON_LIST_XML_METHOD_PROLOG;
 
6459
    if (JSXML_HAS_VALUE(xml))
 
6460
        return JS_TRUE;
 
6461
 
 
6462
    /* From here, control flow must goto out to finish these arrays. */
 
6463
    ok = JS_TRUE;
 
6464
    InitTempNSArray(cx, &ancestors);
 
6465
    InitTempNSArray(cx, &declared);
 
6466
    yml = xml;
 
6467
 
 
6468
    while ((yml = yml->parent) != NULL) {
 
6469
        JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
 
6470
        for (i = 0, n = yml->xml_namespaces.length; i < n; i++) {
 
6471
            ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace);
 
6472
            if (ns &&
 
6473
                !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
 
6474
                ok = XMLARRAY_APPEND(cx, &ancestors.array, ns);
 
6475
                if (!ok)
 
6476
                    goto out;
 
6477
            }
 
6478
        }
 
6479
    }
 
6480
 
 
6481
    for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
 
6482
        ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
 
6483
        if (!ns)
 
6484
            continue;
 
6485
        if (!ns->declared)
 
6486
            continue;
 
6487
        if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
 
6488
            ok = XMLARRAY_APPEND(cx, &declared.array, ns);
 
6489
            if (!ok)
 
6490
                goto out;
 
6491
        }
 
6492
    }
 
6493
 
 
6494
    ok = TempNSArrayToJSArray(cx, &declared, rval);
 
6495
 
 
6496
out:
 
6497
    /* Finishing must be in reverse order of initialization to follow LIFO. */
 
6498
    FinishTempNSArray(cx, &declared);
 
6499
    FinishTempNSArray(cx, &ancestors);
 
6500
    return ok;
 
6501
}
 
6502
 
 
6503
static const char js_attribute_str[] = "attribute";
 
6504
static const char js_text_str[]      = "text";
 
6505
 
 
6506
/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */
 
6507
const char *js_xml_class_str[] = {
 
6508
    "list",
 
6509
    "element",
 
6510
    js_attribute_str,
 
6511
    "processing-instruction",
 
6512
    js_text_str,
 
6513
    "comment"
 
6514
};
 
6515
 
 
6516
static JSBool
 
6517
xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6518
             jsval *rval)
 
6519
{
 
6520
    JSXML *xml;
 
6521
    JSString *str;
 
6522
 
 
6523
    NON_LIST_XML_METHOD_PROLOG;
 
6524
    str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
 
6525
    if (!str)
 
6526
        return JS_FALSE;
 
6527
    *rval = STRING_TO_JSVAL(str);
 
6528
    return JS_TRUE;
 
6529
}
 
6530
 
 
6531
static JSBool
 
6532
NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id)
 
6533
{
 
6534
    jsval junk;
 
6535
 
 
6536
    if (xml->xml_class == JSXML_CLASS_LIST)
 
6537
        return DeleteProperty(cx, obj, id, &junk);
 
6538
    return DeleteByIndex(cx, xml, id, &junk);
 
6539
}
 
6540
 
 
6541
/*
 
6542
 * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace
 
6543
 * text between tags to be removed by normalize.
 
6544
 */
 
6545
static JSBool
 
6546
IsXMLSpace(JSString *str)
 
6547
{
 
6548
    const jschar *cp, *end;
 
6549
 
 
6550
    cp = JSSTRING_CHARS(str);
 
6551
    end = cp + JSSTRING_LENGTH(str);
 
6552
    while (cp < end) {
 
6553
        if (!JS_ISXMLSPACE(*cp))
 
6554
            return JS_FALSE;
 
6555
        ++cp;
 
6556
    }
 
6557
    return JS_TRUE;
 
6558
}
 
6559
 
 
6560
/* XML and XMLList */
 
6561
static JSBool
 
6562
xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6563
              jsval *rval)
 
6564
{
 
6565
    JSXML *xml, *kid, *kid2;
 
6566
    uint32 i, n;
 
6567
    JSObject *kidobj;
 
6568
    JSString *str;
 
6569
    jsval junk;
 
6570
 
 
6571
    XML_METHOD_PROLOG;
 
6572
    *rval = OBJECT_TO_JSVAL(obj);
 
6573
    if (!JSXML_HAS_KIDS(xml))
 
6574
        return JS_TRUE;
 
6575
 
 
6576
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6577
    if (!xml)
 
6578
        return JS_FALSE;
 
6579
 
 
6580
    for (i = 0, n = xml->xml_kids.length; i < n; i++) {
 
6581
        kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
6582
        if (!kid)
 
6583
            continue;
 
6584
        if (kid->xml_class == JSXML_CLASS_ELEMENT) {
 
6585
            kidobj = js_GetXMLObject(cx, kid);
 
6586
            if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk))
 
6587
                return JS_FALSE;
 
6588
        } else if (kid->xml_class == JSXML_CLASS_TEXT) {
 
6589
            while (i + 1 < n &&
 
6590
                   (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
 
6591
                   kid2->xml_class == JSXML_CLASS_TEXT) {
 
6592
                str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
 
6593
                if (!str)
 
6594
                    return JS_FALSE;
 
6595
                if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1)))
 
6596
                    return JS_FALSE;
 
6597
                n = xml->xml_kids.length;
 
6598
                kid->xml_value = str;
 
6599
            }
 
6600
            if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) {
 
6601
                if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i)))
 
6602
                    return JS_FALSE;
 
6603
                n = xml->xml_kids.length;
 
6604
                --i;
 
6605
            }
 
6606
        }
 
6607
    }
 
6608
 
 
6609
    return JS_TRUE;
 
6610
}
 
6611
 
 
6612
/* XML and XMLList */
 
6613
static JSBool
 
6614
xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
6615
{
 
6616
    JSXML *xml, *parent, *kid;
 
6617
    uint32 i, n;
 
6618
    JSObject *parentobj;
 
6619
 
 
6620
    XML_METHOD_PROLOG;
 
6621
    parent = xml->parent;
 
6622
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
6623
        *rval = JSVAL_VOID;
 
6624
        n = xml->xml_kids.length;
 
6625
        if (n == 0)
 
6626
            return JS_TRUE;
 
6627
 
 
6628
        kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
 
6629
        if (!kid)
 
6630
            return JS_TRUE;
 
6631
        parent = kid->parent;
 
6632
        for (i = 1; i < n; i++) {
 
6633
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
6634
            if (kid && kid->parent != parent)
 
6635
                return JS_TRUE;
 
6636
        }
 
6637
    }
 
6638
 
 
6639
    if (!parent) {
 
6640
        *rval = JSVAL_NULL;
 
6641
        return JS_TRUE;
 
6642
    }
 
6643
 
 
6644
    parentobj = js_GetXMLObject(cx, parent);
 
6645
    if (!parentobj)
 
6646
        return JS_FALSE;
 
6647
    *rval = OBJECT_TO_JSVAL(parentobj);
 
6648
    return JS_TRUE;
 
6649
}
 
6650
 
 
6651
/* XML and XMLList */
 
6652
static JSBool
 
6653
xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc,
 
6654
                           jsval *argv, jsval *rval)
 
6655
{
 
6656
    JSXML *xml, *list, *kid, *vxml;
 
6657
    jsval name, v;
 
6658
    JSXMLQName *nameqn;
 
6659
    jsid funid;
 
6660
    JSBool ok;
 
6661
    JSXMLArrayCursor cursor;
 
6662
    JSObject *kidobj;
 
6663
    uint32 i, n;
 
6664
 
 
6665
    XML_METHOD_PROLOG;
 
6666
    name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
 
6667
    nameqn = ToXMLName(cx, name, &funid);
 
6668
    if (!nameqn)
 
6669
        return JS_FALSE;
 
6670
    argv[0] = OBJECT_TO_JSVAL(nameqn->object);
 
6671
 
 
6672
    list = xml_list_helper(cx, xml, rval);
 
6673
    if (!list)
 
6674
        return JS_FALSE;
 
6675
    if (funid)
 
6676
        return JS_TRUE;
 
6677
 
 
6678
    list->xml_targetprop = nameqn;
 
6679
    ok = JS_TRUE;
 
6680
 
 
6681
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
6682
        /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
 
6683
        XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
6684
        while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
6685
            if (kid->xml_class == JSXML_CLASS_ELEMENT) {
 
6686
                ok = js_EnterLocalRootScope(cx);
 
6687
                if (!ok)
 
6688
                    break;
 
6689
                kidobj = js_GetXMLObject(cx, kid);
 
6690
                if (kidobj) {
 
6691
                    ok = xml_processingInstructions(cx, kidobj, argc, argv, &v);
 
6692
                } else {
 
6693
                    ok = JS_FALSE;
 
6694
                    v = JSVAL_NULL;
 
6695
                }
 
6696
                js_LeaveLocalRootScopeWithResult(cx, v);
 
6697
                if (!ok)
 
6698
                    break;
 
6699
                vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
6700
                if (JSXML_LENGTH(vxml) != 0) {
 
6701
                    ok = Append(cx, list, vxml);
 
6702
                    if (!ok)
 
6703
                        break;
 
6704
                }
 
6705
            }
 
6706
        }
 
6707
        XMLArrayCursorFinish(&cursor);
 
6708
    } else {
 
6709
        /* 13.4.4.28 Step 4. */
 
6710
        for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
6711
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
6712
            if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
 
6713
                (IS_STAR(nameqn->localName) ||
 
6714
                 js_EqualStrings(nameqn->localName, kid->name->localName))) {
 
6715
                ok = Append(cx, list, kid);
 
6716
                if (!ok)
 
6717
                    break;
 
6718
            }
 
6719
        }
 
6720
    }
 
6721
 
 
6722
    return ok;
 
6723
}
 
6724
 
 
6725
static JSBool
 
6726
xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6727
                 jsval *rval)
 
6728
{
 
6729
    JSXML *xml;
 
6730
 
 
6731
    NON_LIST_XML_METHOD_PROLOG;
 
6732
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6733
    if (!xml)
 
6734
        return JS_FALSE;
 
6735
    *rval = OBJECT_TO_JSVAL(obj);
 
6736
    return Insert(cx, xml, 0, argv[0]);
 
6737
}
 
6738
 
 
6739
/* XML and XMLList */
 
6740
static JSBool
 
6741
xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6742
                         jsval *rval)
 
6743
{
 
6744
    JSXML *xml;
 
6745
    jsval name;
 
6746
    uint32 index;
 
6747
 
 
6748
    XML_METHOD_PROLOG;
 
6749
    name = argv[0];
 
6750
    *rval = JSVAL_FALSE;
 
6751
    if (js_IdIsIndex(name, &index)) {
 
6752
        if (xml->xml_class == JSXML_CLASS_LIST) {
 
6753
            /* 13.5.4.18. */
 
6754
            *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
 
6755
        } else {
 
6756
            /* 13.4.4.30. */
 
6757
            *rval = BOOLEAN_TO_JSVAL(index == 0);
 
6758
        }
 
6759
    }
 
6760
    return JS_TRUE;
 
6761
}
 
6762
 
 
6763
static JSBool
 
6764
namespace_full_match(const void *a, const void *b)
 
6765
{
 
6766
    const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
 
6767
    const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
 
6768
 
 
6769
    if (nsa->prefix && nsb->prefix &&
 
6770
        !js_EqualStrings(nsa->prefix, nsb->prefix)) {
 
6771
        return JS_FALSE;
 
6772
    }
 
6773
    return js_EqualStrings(nsa->uri, nsb->uri);
 
6774
}
 
6775
 
 
6776
static JSBool
 
6777
xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
 
6778
{
 
6779
    JSXMLNamespace *thisns, *attrns;
 
6780
    uint32 i, n;
 
6781
    JSXML *attr, *kid;
 
6782
 
 
6783
    thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
 
6784
    JS_ASSERT(thisns);
 
6785
    if (thisns == ns)
 
6786
        return JS_TRUE;
 
6787
 
 
6788
    for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
 
6789
        attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
 
6790
        if (!attr)
 
6791
            continue;
 
6792
        attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
 
6793
        JS_ASSERT(attrns);
 
6794
        if (attrns == ns)
 
6795
            return JS_TRUE;
 
6796
    }
 
6797
 
 
6798
    i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
 
6799
    if (i != XML_NOT_FOUND)
 
6800
        XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
 
6801
 
 
6802
    for (i = 0, n = xml->xml_kids.length; i < n; i++) {
 
6803
        kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
6804
        if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
6805
            if (!xml_removeNamespace_helper(cx, kid, ns))
 
6806
                return JS_FALSE;
 
6807
        }
 
6808
    }
 
6809
    return JS_TRUE;
 
6810
}
 
6811
 
 
6812
static JSBool
 
6813
xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6814
                    jsval *rval)
 
6815
{
 
6816
    JSXML *xml;
 
6817
    JSObject *nsobj;
 
6818
    JSXMLNamespace *ns;
 
6819
 
 
6820
    NON_LIST_XML_METHOD_PROLOG;
 
6821
    *rval = OBJECT_TO_JSVAL(obj);
 
6822
    if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
6823
        return JS_TRUE;
 
6824
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6825
    if (!xml)
 
6826
        return JS_FALSE;
 
6827
 
 
6828
    nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv);
 
6829
    if (!nsobj)
 
6830
        return JS_FALSE;
 
6831
    argv[0] = OBJECT_TO_JSVAL(nsobj);
 
6832
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
 
6833
 
 
6834
    /* NOTE: remove ns from each ancestor if not used by that ancestor. */
 
6835
    return xml_removeNamespace_helper(cx, xml, ns);
 
6836
}
 
6837
 
 
6838
static JSBool
 
6839
xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
6840
{
 
6841
    JSXML *xml, *vxml, *kid;
 
6842
    jsval name, value, id, junk;
 
6843
    uint32 index;
 
6844
    JSObject *nameobj;
 
6845
    JSXMLQName *nameqn;
 
6846
 
 
6847
    NON_LIST_XML_METHOD_PROLOG;
 
6848
    *rval = OBJECT_TO_JSVAL(obj);
 
6849
    if (xml->xml_class != JSXML_CLASS_ELEMENT)
 
6850
        return JS_TRUE;
 
6851
 
 
6852
    value = argv[1];
 
6853
    vxml = VALUE_IS_XML(cx, value)
 
6854
           ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value))
 
6855
           : NULL;
 
6856
    if (!vxml) {
 
6857
        if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1]))
 
6858
            return JS_FALSE;
 
6859
        value = argv[1];
 
6860
    } else {
 
6861
        vxml = DeepCopy(cx, vxml, NULL, 0);
 
6862
        if (!vxml)
 
6863
            return JS_FALSE;
 
6864
        value = argv[1] = OBJECT_TO_JSVAL(vxml->object);
 
6865
    }
 
6866
 
 
6867
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6868
    if (!xml)
 
6869
        return JS_FALSE;
 
6870
 
 
6871
    name = argv[0];
 
6872
    if (js_IdIsIndex(name, &index))
 
6873
        return Replace(cx, xml, name, value);
 
6874
 
 
6875
    /* Call function QName per spec, not ToXMLName, to avoid attribute names. */
 
6876
    nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name);
 
6877
    if (!nameobj)
 
6878
        return JS_FALSE;
 
6879
    argv[0] = OBJECT_TO_JSVAL(nameobj);
 
6880
    nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
 
6881
 
 
6882
    id = JSVAL_VOID;
 
6883
    index = xml->xml_kids.length;
 
6884
    while (index != 0) {
 
6885
        --index;
 
6886
        kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
 
6887
        if (kid && MatchElemName(nameqn, kid)) {
 
6888
            if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk))
 
6889
                return JS_FALSE;
 
6890
            if (!IndexToIdVal(cx, index, &id))
 
6891
                return JS_FALSE;
 
6892
        }
 
6893
    }
 
6894
    if (JSVAL_IS_VOID(id))
 
6895
        return JS_TRUE;
 
6896
    return Replace(cx, xml, id, value);
 
6897
}
 
6898
 
 
6899
static JSBool
 
6900
xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6901
                jsval *rval)
 
6902
{
 
6903
    if (!StartNonListXMLMethod(cx, &obj, argv))
 
6904
        return JS_FALSE;
 
6905
 
 
6906
    if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom),
 
6907
                     &argv[0])) {
 
6908
        return JS_FALSE;
 
6909
    }
 
6910
 
 
6911
    *rval = OBJECT_TO_JSVAL(obj);
 
6912
    return JS_TRUE;
 
6913
}
 
6914
 
 
6915
static JSBool
 
6916
xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
6917
                 jsval *rval)
 
6918
{
 
6919
    JSXML *xml;
 
6920
    jsval name;
 
6921
    JSXMLQName *nameqn;
 
6922
    JSString *namestr;
 
6923
 
 
6924
    NON_LIST_XML_METHOD_PROLOG;
 
6925
    if (!JSXML_HAS_NAME(xml))
 
6926
        return JS_TRUE;
 
6927
 
 
6928
    name = argv[0];
 
6929
    if (!JSVAL_IS_PRIMITIVE(name) &&
 
6930
        OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) {
 
6931
        nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name));
 
6932
        namestr = nameqn->localName;
 
6933
    } else {
 
6934
        if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0]))
 
6935
            return JS_FALSE;
 
6936
        name = argv[0];
 
6937
        namestr = JSVAL_TO_STRING(name);
 
6938
    }
 
6939
 
 
6940
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6941
    if (!xml)
 
6942
        return JS_FALSE;
 
6943
    xml->name->localName = namestr;
 
6944
    return JS_TRUE;
 
6945
}
 
6946
 
 
6947
static JSBool
 
6948
xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
6949
{
 
6950
    JSXML *xml, *nsowner;
 
6951
    jsval name;
 
6952
    JSXMLQName *nameqn;
 
6953
    JSObject *nameobj;
 
6954
    JSXMLArray *nsarray;
 
6955
    uint32 i, n;
 
6956
    JSXMLNamespace *ns;
 
6957
 
 
6958
    NON_LIST_XML_METHOD_PROLOG;
 
6959
    if (!JSXML_HAS_NAME(xml))
 
6960
        return JS_TRUE;
 
6961
 
 
6962
    name = argv[0];
 
6963
    if (!JSVAL_IS_PRIMITIVE(name) &&
 
6964
        OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base &&
 
6965
        !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)))
 
6966
         ->uri) {
 
6967
        name = argv[0] = STRING_TO_JSVAL(nameqn->localName);
 
6968
    }
 
6969
 
 
6970
    nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name);
 
6971
    if (!nameobj)
 
6972
        return JS_FALSE;
 
6973
    nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
 
6974
 
 
6975
    /* ECMA-357 13.4.4.35 Step 4. */
 
6976
    if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
 
6977
        nameqn->uri = cx->runtime->emptyString;
 
6978
 
 
6979
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
6980
    if (!xml)
 
6981
        return JS_FALSE;
 
6982
    xml->name = nameqn;
 
6983
 
 
6984
    /*
 
6985
     * Erratum: nothing in 13.4.4.35 talks about making the name match the
 
6986
     * in-scope namespaces, either by finding an in-scope namespace with a
 
6987
     * matching uri and setting the new name's prefix to that namespace's
 
6988
     * prefix, or by extending the in-scope namespaces for xml (which are in
 
6989
     * xml->parent if xml is an attribute or a PI).
 
6990
     */
 
6991
    if (xml->xml_class == JSXML_CLASS_ELEMENT) {
 
6992
        nsowner = xml;
 
6993
    } else {
 
6994
        if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
 
6995
            return JS_TRUE;
 
6996
        nsowner = xml->parent;
 
6997
    }
 
6998
 
 
6999
    if (nameqn->prefix) {
 
7000
        /*
 
7001
         * The name being set has a prefix, which originally came from some
 
7002
         * namespace object (which may be the null namespace, where both the
 
7003
         * prefix and uri are the empty string).  We must go through a full
 
7004
         * GetNamespace in case that namespace is in-scope in nsowner.
 
7005
         *
 
7006
         * If we find such an in-scope namespace, we return true right away,
 
7007
         * in this block.  Otherwise, we fall through to the final return of
 
7008
         * AddInScopeNamespace(cx, nsowner, ns).
 
7009
         */
 
7010
        ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
 
7011
        if (!ns)
 
7012
            return JS_FALSE;
 
7013
 
 
7014
        /* XXXbe have to test membership to see whether GetNamespace added */
 
7015
        if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
 
7016
            return JS_TRUE;
 
7017
    } else {
 
7018
        /*
 
7019
         * At this point, we know nameqn->prefix is null, so nameqn->uri can't
 
7020
         * be the empty string (the null namespace always uses the empty string
 
7021
         * for both prefix and uri).
 
7022
         *
 
7023
         * This means we must inline GetNamespace and specialize it to match
 
7024
         * uri only, never prefix.  If we find a namespace with nameqn's uri
 
7025
         * already in nsowner->xml_namespaces, then all that we need do is set
 
7026
         * nameqn->prefix to that namespace's prefix.
 
7027
         *
 
7028
         * If no such namespace exists, we can create one without going through
 
7029
         * the constructor, because we know nameqn->uri is non-empty (so prefix
 
7030
         * does not need to be converted from null to empty by QName).
 
7031
         */
 
7032
        JS_ASSERT(!IS_EMPTY(nameqn->uri));
 
7033
 
 
7034
        nsarray = &nsowner->xml_namespaces;
 
7035
        for (i = 0, n = nsarray->length; i < n; i++) {
 
7036
            ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace);
 
7037
            if (ns && js_EqualStrings(ns->uri, nameqn->uri)) {
 
7038
                nameqn->prefix = ns->prefix;
 
7039
                return JS_TRUE;
 
7040
            }
 
7041
        }
 
7042
 
 
7043
        ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE);
 
7044
        if (!ns)
 
7045
            return JS_FALSE;
 
7046
    }
 
7047
 
 
7048
    return AddInScopeNamespace(cx, nsowner, ns);
 
7049
}
 
7050
 
 
7051
static JSBool
 
7052
xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
7053
                 jsval *rval)
 
7054
{
 
7055
    JSXML *xml, *nsowner;
 
7056
    JSObject *nsobj, *qnobj;
 
7057
    JSXMLNamespace *ns;
 
7058
    jsval qnargv[2];
 
7059
 
 
7060
    NON_LIST_XML_METHOD_PROLOG;
 
7061
    if (!JSXML_HAS_NAME(xml))
 
7062
        return JS_TRUE;
 
7063
 
 
7064
    xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
 
7065
    if (!xml || !js_GetXMLQNameObject(cx, xml->name))
 
7066
        return JS_FALSE;
 
7067
 
 
7068
    nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv);
 
7069
    if (!nsobj)
 
7070
        return JS_FALSE;
 
7071
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
 
7072
    ns->declared = JS_TRUE;
 
7073
 
 
7074
    qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj);
 
7075
    qnargv[1] = OBJECT_TO_JSVAL(xml->name->object);
 
7076
    qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv);
 
7077
    if (!qnobj)
 
7078
        return JS_FALSE;
 
7079
 
 
7080
    xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj);
 
7081
 
 
7082
    /*
 
7083
     * Erratum: the spec fails to update the governing in-scope namespaces.
 
7084
     * See the erratum noted in xml_setName, above.
 
7085
     */
 
7086
    if (xml->xml_class == JSXML_CLASS_ELEMENT) {
 
7087
        nsowner = xml;
 
7088
    } else {
 
7089
        if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
 
7090
            return JS_TRUE;
 
7091
        nsowner = xml->parent;
 
7092
    }
 
7093
    return AddInScopeNamespace(cx, nsowner, ns);
 
7094
}
 
7095
 
 
7096
/* XML and XMLList */
 
7097
static JSBool
 
7098
xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
7099
{
 
7100
    JSXML *xml, *list, *kid, *vxml;
 
7101
    uint32 i, n;
 
7102
    JSBool ok;
 
7103
    JSObject *kidobj;
 
7104
    jsval v;
 
7105
 
 
7106
    XML_METHOD_PROLOG;
 
7107
    list = xml_list_helper(cx, xml, rval);
 
7108
    if (!list)
 
7109
        return JS_FALSE;
 
7110
 
 
7111
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
7112
        ok = JS_TRUE;
 
7113
        for (i = 0, n = xml->xml_kids.length; i < n; i++) {
 
7114
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
7115
            if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
 
7116
                ok = js_EnterLocalRootScope(cx);
 
7117
                if (!ok)
 
7118
                    break;
 
7119
                kidobj = js_GetXMLObject(cx, kid);
 
7120
                if (kidobj) {
 
7121
                    ok = xml_text(cx, kidobj, argc, argv, &v);
 
7122
                } else {
 
7123
                    ok = JS_FALSE;
 
7124
                    v = JSVAL_NULL;
 
7125
                }
 
7126
                js_LeaveLocalRootScopeWithResult(cx, v);
 
7127
                if (!ok)
 
7128
                    return JS_FALSE;
 
7129
                vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
 
7130
                if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
 
7131
                    return JS_FALSE;
 
7132
            }
 
7133
        }
 
7134
    } else {
 
7135
        for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
 
7136
            kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
 
7137
            if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
 
7138
                if (!Append(cx, list, kid))
 
7139
                    return JS_FALSE;
 
7140
            }
 
7141
        }
 
7142
    }
 
7143
    return JS_TRUE;
 
7144
}
 
7145
 
 
7146
/* XML and XMLList */
 
7147
static JSBool
 
7148
xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
7149
                jsval *rval)
 
7150
{
 
7151
    JSString *str;
 
7152
 
 
7153
    str = ToXMLString(cx, OBJECT_TO_JSVAL(obj));
 
7154
    if (!str)
 
7155
        return JS_FALSE;
 
7156
    *rval = STRING_TO_JSVAL(str);
 
7157
    return JS_TRUE;
 
7158
}
 
7159
 
 
7160
/* XML and XMLList */
 
7161
static JSString *
 
7162
xml_toString_helper(JSContext *cx, JSXML *xml)
 
7163
{
 
7164
    JSString *str, *kidstr;
 
7165
    JSXML *kid;
 
7166
    JSXMLArrayCursor cursor;
 
7167
 
 
7168
    if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
 
7169
        xml->xml_class == JSXML_CLASS_TEXT) {
 
7170
        return xml->xml_value;
 
7171
    }
 
7172
 
 
7173
    if (!HasSimpleContent(xml))
 
7174
        return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object));
 
7175
 
 
7176
    str = cx->runtime->emptyString;
 
7177
    js_EnterLocalRootScope(cx);
 
7178
    XMLArrayCursorInit(&cursor, &xml->xml_kids);
 
7179
    while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
7180
        if (kid->xml_class != JSXML_CLASS_COMMENT &&
 
7181
            kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
 
7182
            kidstr = xml_toString_helper(cx, kid);
 
7183
            if (!kidstr) {
 
7184
                str = NULL;
 
7185
                break;
 
7186
            }
 
7187
            str = js_ConcatStrings(cx, str, kidstr);
 
7188
            if (!str)
 
7189
                break;
 
7190
        }
 
7191
    }
 
7192
    XMLArrayCursorFinish(&cursor);
 
7193
    js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
 
7194
    return str;
 
7195
}
 
7196
 
 
7197
static JSBool
 
7198
xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
7199
             jsval *rval)
 
7200
{
 
7201
    JSXML *xml;
 
7202
    JSString *str;
 
7203
 
 
7204
    XML_METHOD_PROLOG;
 
7205
    str = xml_toString_helper(cx, xml);
 
7206
    if (!str)
 
7207
        return JS_FALSE;
 
7208
    *rval = STRING_TO_JSVAL(str);
 
7209
    return JS_TRUE;
 
7210
}
 
7211
 
 
7212
/* XML and XMLList */
 
7213
static JSBool
 
7214
xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
7215
{
 
7216
    *rval = OBJECT_TO_JSVAL(obj);
 
7217
    return JS_TRUE;
 
7218
}
 
7219
 
 
7220
static JSFunctionSpec xml_methods[] = {
 
7221
    {"addNamespace",          xml_addNamespace,          1,0,0},
 
7222
    {"appendChild",           xml_appendChild,           1,0,0},
 
7223
    {js_attribute_str,        xml_attribute,             1,0,0},
 
7224
    {"attributes",            xml_attributes,            0,0,0},
 
7225
    {"child",                 xml_child,                 1,0,0},
 
7226
    {"childIndex",            xml_childIndex,            0,0,0},
 
7227
    {"children",              xml_children,              0,0,0},
 
7228
    {"comments",              xml_comments,              0,0,0},
 
7229
    {"contains",              xml_contains,              1,0,0},
 
7230
    {"copy",                  xml_copy,                  0,0,0},
 
7231
    {"descendants",           xml_descendants,           1,0,0},
 
7232
    {"elements",              xml_elements,              1,0,0},
 
7233
    {"hasOwnProperty",        xml_hasOwnProperty,        1,0,0},
 
7234
    {"hasComplexContent",     xml_hasComplexContent,     1,0,0},
 
7235
    {"hasSimpleContent",      xml_hasSimpleContent,      1,0,0},
 
7236
    {"inScopeNamespaces",     xml_inScopeNamespaces,     0,0,0},
 
7237
    {"insertChildAfter",      xml_insertChildAfter,      2,0,0},
 
7238
    {"insertChildBefore",     xml_insertChildBefore,     2,0,0},
 
7239
    {js_length_str,           xml_length,                0,0,0},
 
7240
    {js_localName_str,        xml_localName,             0,0,0},
 
7241
    {js_name_str,             xml_name,                  0,0,0},
 
7242
    {js_namespace_str,        xml_namespace,             1,0,0},
 
7243
    {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0},
 
7244
    {"nodeKind",              xml_nodeKind,              0,0,0},
 
7245
    {"normalize",             xml_normalize,             0,0,0},
 
7246
    {js_xml_parent_str,       xml_parent,                0,0,0},
 
7247
    {"processingInstructions",xml_processingInstructions,1,0,0},
 
7248
    {"prependChild",          xml_prependChild,          1,0,0},
 
7249
    {"propertyIsEnumerable",  xml_propertyIsEnumerable,  1,0,0},
 
7250
    {"removeNamespace",       xml_removeNamespace,       1,0,0},
 
7251
    {"replace",               xml_replace,               2,0,0},
 
7252
    {"setChildren",           xml_setChildren,           1,0,0},
 
7253
    {"setLocalName",          xml_setLocalName,          1,0,0},
 
7254
    {"setName",               xml_setName,               1,0,0},
 
7255
    {"setNamespace",          xml_setNamespace,          1,0,0},
 
7256
    {js_text_str,             xml_text,                  0,0,0},
 
7257
    {js_toString_str,         xml_toString,              0,0,0},
 
7258
    {js_toXMLString_str,      xml_toXMLString,           0,0,0},
 
7259
    {js_toSource_str,         xml_toXMLString,           0,0,0},
 
7260
    {js_valueOf_str,          xml_valueOf,               0,0,0},
 
7261
    {0,0,0,0,0}
 
7262
};
 
7263
 
 
7264
static JSBool
 
7265
CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
 
7266
{
 
7267
    int i;
 
7268
    const char *name;
 
7269
    jsval v;
 
7270
 
 
7271
    for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
 
7272
        name = xml_static_props[i].name;
 
7273
        if (!JS_GetProperty(cx, from, name, &v))
 
7274
            return JS_FALSE;
 
7275
        if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v))
 
7276
            return JS_FALSE;
 
7277
    }
 
7278
 
 
7279
    name = xml_static_props[i].name;
 
7280
    if (!JS_GetProperty(cx, from, name, &v))
 
7281
        return JS_FALSE;
 
7282
    if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v))
 
7283
        return JS_FALSE;
 
7284
    return JS_TRUE;
 
7285
}
 
7286
 
 
7287
static JSBool
 
7288
SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
 
7289
{
 
7290
    int i;
 
7291
    jsval v;
 
7292
 
 
7293
    for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
 
7294
        v = JSVAL_TRUE;
 
7295
        if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
 
7296
            return JS_FALSE;
 
7297
    }
 
7298
    v = INT_TO_JSVAL(2);
 
7299
    return JS_SetProperty(cx, obj, xml_static_props[i].name, &v);
 
7300
}
 
7301
 
 
7302
static JSBool
 
7303
xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
7304
{
 
7305
    JSObject *settings;
 
7306
 
 
7307
    settings = JS_NewObject(cx, NULL, NULL, NULL);
 
7308
    if (!settings)
 
7309
        return JS_FALSE;
 
7310
    *rval = OBJECT_TO_JSVAL(settings);
 
7311
    return CopyXMLSettings(cx, obj, settings);
 
7312
}
 
7313
 
 
7314
static JSBool
 
7315
xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
7316
                jsval *rval)
 
7317
{
 
7318
    jsval v;
 
7319
    JSBool ok;
 
7320
    JSObject *settings;
 
7321
 
 
7322
    v = argv[0];
 
7323
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
 
7324
        cx->xmlSettingFlags = 0;
 
7325
        ok = SetDefaultXMLSettings(cx, obj);
 
7326
    } else {
 
7327
        if (JSVAL_IS_PRIMITIVE(v))
 
7328
            return JS_TRUE;
 
7329
        settings = JSVAL_TO_OBJECT(v);
 
7330
        cx->xmlSettingFlags = 0;
 
7331
        ok = CopyXMLSettings(cx, settings, obj);
 
7332
    }
 
7333
    if (ok)
 
7334
        cx->xmlSettingFlags |= XSF_CACHE_VALID;
 
7335
    return ok;
 
7336
}
 
7337
 
 
7338
static JSBool
 
7339
xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
7340
                    jsval *rval)
 
7341
{
 
7342
    JSObject *settings;
 
7343
 
 
7344
    settings = JS_NewObject(cx, NULL, NULL, NULL);
 
7345
    if (!settings)
 
7346
        return JS_FALSE;
 
7347
    *rval = OBJECT_TO_JSVAL(settings);
 
7348
    return SetDefaultXMLSettings(cx, settings);
 
7349
}
 
7350
 
 
7351
static JSFunctionSpec xml_static_methods[] = {
 
7352
    {"settings",         xml_settings,          0,0,0},
 
7353
    {"setSettings",      xml_setSettings,       1,0,0},
 
7354
    {"defaultSettings",  xml_defaultSettings,   0,0,0},
 
7355
    {0,0,0,0,0}
 
7356
};
 
7357
 
 
7358
static JSBool
 
7359
XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
7360
{
 
7361
    jsval v;
 
7362
    JSXML *xml, *copy;
 
7363
    JSObject *xobj, *vobj;
 
7364
    JSClass *clasp;
 
7365
 
 
7366
    v = argv[0];
 
7367
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
 
7368
        v = STRING_TO_JSVAL(cx->runtime->emptyString);
 
7369
 
 
7370
    xobj = ToXML(cx, v);
 
7371
    if (!xobj)
 
7372
        return JS_FALSE;
 
7373
    *rval = OBJECT_TO_JSVAL(xobj);
 
7374
    xml = (JSXML *) JS_GetPrivate(cx, xobj);
 
7375
 
 
7376
    if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) {
 
7377
        vobj = JSVAL_TO_OBJECT(v);
 
7378
        clasp = OBJ_GET_CLASS(cx, vobj);
 
7379
        if (clasp == &js_XMLClass ||
 
7380
            (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
 
7381
            /* No need to lock obj, it's newly constructed and thread local. */
 
7382
            copy = DeepCopy(cx, xml, obj, 0);
 
7383
            if (!copy)
 
7384
                return JS_FALSE;
 
7385
            JS_ASSERT(copy->object == obj);
 
7386
            *rval = OBJECT_TO_JSVAL(obj);
 
7387
            return JS_TRUE;
 
7388
        }
 
7389
    }
 
7390
    return JS_TRUE;
 
7391
}
 
7392
 
 
7393
static JSBool
 
7394
XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
7395
{
 
7396
    jsval v;
 
7397
    JSObject *vobj, *listobj;
 
7398
    JSXML *xml, *list;
 
7399
 
 
7400
    v = argv[0];
 
7401
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
 
7402
        v = STRING_TO_JSVAL(cx->runtime->emptyString);
 
7403
 
 
7404
    if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) {
 
7405
        vobj = JSVAL_TO_OBJECT(v);
 
7406
        if (OBJECT_IS_XML(cx, vobj)) {
 
7407
            xml = (JSXML *) JS_GetPrivate(cx, vobj);
 
7408
            if (xml->xml_class == JSXML_CLASS_LIST) {
 
7409
                listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
7410
                if (!listobj)
 
7411
                    return JS_FALSE;
 
7412
                *rval = OBJECT_TO_JSVAL(listobj);
 
7413
 
 
7414
                list = (JSXML *) JS_GetPrivate(cx, listobj);
 
7415
                if (!Append(cx, list, xml))
 
7416
                    return JS_FALSE;
 
7417
                return JS_TRUE;
 
7418
            }
 
7419
        }
 
7420
    }
 
7421
 
 
7422
    /* Toggle on XML support since the script has explicitly requested it. */
 
7423
    listobj = ToXMLList(cx, v);
 
7424
    if (!listobj)
 
7425
        return JS_FALSE;
 
7426
 
 
7427
    *rval = OBJECT_TO_JSVAL(listobj);
 
7428
    return JS_TRUE;
 
7429
}
 
7430
 
 
7431
#define JSXML_LIST_SIZE     (offsetof(JSXML, u) + sizeof(struct JSXMLListVar))
 
7432
#define JSXML_ELEMENT_SIZE  (offsetof(JSXML, u) + sizeof(struct JSXMLVar))
 
7433
#define JSXML_LEAF_SIZE     (offsetof(JSXML, u) + sizeof(JSString *))
 
7434
 
 
7435
static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = {
 
7436
    JSXML_LIST_SIZE,        /* JSXML_CLASS_LIST */
 
7437
    JSXML_ELEMENT_SIZE,     /* JSXML_CLASS_ELEMENT */
 
7438
    JSXML_LEAF_SIZE,        /* JSXML_CLASS_ATTRIBUTE */
 
7439
    JSXML_LEAF_SIZE,        /* JSXML_CLASS_PROCESSING_INSTRUCTION */
 
7440
    JSXML_LEAF_SIZE,        /* JSXML_CLASS_TEXT */
 
7441
    JSXML_LEAF_SIZE         /* JSXML_CLASS_COMMENT */
 
7442
};
 
7443
 
 
7444
#ifdef DEBUG_notme
 
7445
JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
 
7446
uint32  xml_serial;
 
7447
#endif
 
7448
 
 
7449
JSXML *
 
7450
js_NewXML(JSContext *cx, JSXMLClass xml_class)
 
7451
{
 
7452
    JSXML *xml;
 
7453
 
 
7454
    xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]);
 
7455
    if (!xml)
 
7456
        return NULL;
 
7457
 
 
7458
    xml->object = NULL;
 
7459
    xml->domnode = NULL;
 
7460
    xml->parent = NULL;
 
7461
    xml->name = NULL;
 
7462
    xml->xml_class = xml_class;
 
7463
    xml->xml_flags = 0;
 
7464
    if (JSXML_CLASS_HAS_VALUE(xml_class)) {
 
7465
        xml->xml_value = cx->runtime->emptyString;
 
7466
    } else {
 
7467
        XMLArrayInit(cx, &xml->xml_kids, 0);
 
7468
        if (xml_class == JSXML_CLASS_LIST) {
 
7469
            xml->xml_target = NULL;
 
7470
            xml->xml_targetprop = NULL;
 
7471
        } else {
 
7472
            XMLArrayInit(cx, &xml->xml_namespaces, 0);
 
7473
            XMLArrayInit(cx, &xml->xml_attrs, 0);
 
7474
        }
 
7475
    }
 
7476
 
 
7477
#ifdef DEBUG_notme
 
7478
    JS_APPEND_LINK(&xml->links, &xml_leaks);
 
7479
    xml->serial = xml_serial++;
 
7480
#endif
 
7481
    METER(xml_stats.xml);
 
7482
    METER(xml_stats.livexml);
 
7483
    return xml;
 
7484
}
 
7485
 
 
7486
void
 
7487
js_MarkXML(JSContext *cx, JSXML *xml)
 
7488
{
 
7489
    GC_MARK(cx, xml->object, "object");
 
7490
    GC_MARK(cx, xml->name, "name");
 
7491
    GC_MARK(cx, xml->parent, "xml_parent");
 
7492
 
 
7493
    if (JSXML_HAS_VALUE(xml)) {
 
7494
        GC_MARK(cx, xml->xml_value, "value");
 
7495
        return;
 
7496
    }
 
7497
 
 
7498
    xml_mark_vector(cx,
 
7499
                    (JSXML **) xml->xml_kids.vector,
 
7500
                    xml->xml_kids.length);
 
7501
    XMLArrayCursorMark(cx, xml->xml_kids.cursors);
 
7502
    XMLArrayTrim(&xml->xml_kids);
 
7503
 
 
7504
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
7505
        if (xml->xml_target)
 
7506
            GC_MARK(cx, xml->xml_target, "target");
 
7507
        if (xml->xml_targetprop)
 
7508
            GC_MARK(cx, xml->xml_targetprop, "targetprop");
 
7509
    } else {
 
7510
        namespace_mark_vector(cx,
 
7511
                              (JSXMLNamespace **) xml->xml_namespaces.vector,
 
7512
                              xml->xml_namespaces.length);
 
7513
        XMLArrayCursorMark(cx, xml->xml_namespaces.cursors);
 
7514
        XMLArrayTrim(&xml->xml_namespaces);
 
7515
 
 
7516
        xml_mark_vector(cx,
 
7517
                        (JSXML **) xml->xml_attrs.vector,
 
7518
                        xml->xml_attrs.length);
 
7519
        XMLArrayCursorMark(cx, xml->xml_attrs.cursors);
 
7520
        XMLArrayTrim(&xml->xml_attrs);
 
7521
    }
 
7522
}
 
7523
 
 
7524
void
 
7525
js_FinalizeXML(JSContext *cx, JSXML *xml)
 
7526
{
 
7527
    if (JSXML_HAS_KIDS(xml)) {
 
7528
        XMLArrayFinish(cx, &xml->xml_kids);
 
7529
        if (xml->xml_class == JSXML_CLASS_ELEMENT) {
 
7530
            XMLArrayFinish(cx, &xml->xml_namespaces);
 
7531
            XMLArrayFinish(cx, &xml->xml_attrs);
 
7532
        }
 
7533
    }
 
7534
 
 
7535
#ifdef DEBUG_notme
 
7536
    JS_REMOVE_LINK(&xml->links);
 
7537
#endif
 
7538
 
 
7539
    UNMETER(xml_stats.livexml);
 
7540
}
 
7541
 
 
7542
JSObject *
 
7543
js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn)
 
7544
{
 
7545
    jsval nsval;
 
7546
    JSXMLNamespace *ns;
 
7547
    JSXMLArray nsarray;
 
7548
    JSXML *xml;
 
7549
 
 
7550
    if (!js_GetDefaultXMLNamespace(cx, &nsval))
 
7551
        return NULL;
 
7552
    JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
 
7553
    ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
 
7554
 
 
7555
    if (!XMLArrayInit(cx, &nsarray, 1))
 
7556
        return NULL;
 
7557
 
 
7558
    XMLARRAY_APPEND(cx, &nsarray, ns);
 
7559
    xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT);
 
7560
    XMLArrayFinish(cx, &nsarray);
 
7561
    if (!xml)
 
7562
        return NULL;
 
7563
 
 
7564
    return xml->object;
 
7565
}
 
7566
 
 
7567
JSObject *
 
7568
js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
 
7569
{
 
7570
    JSXML *xml;
 
7571
    JSObject *obj;
 
7572
    JSTempValueRooter tvr;
 
7573
 
 
7574
    xml = js_NewXML(cx, xml_class);
 
7575
    if (!xml)
 
7576
        return NULL;
 
7577
    JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr);
 
7578
    obj = js_GetXMLObject(cx, xml);
 
7579
    JS_POP_TEMP_ROOT(cx, &tvr);
 
7580
    return obj;
 
7581
}
 
7582
 
 
7583
static JSObject *
 
7584
NewXMLObject(JSContext *cx, JSXML *xml)
 
7585
{
 
7586
    JSObject *obj;
 
7587
 
 
7588
    obj = js_NewObject(cx, &js_XMLClass, NULL, NULL);
 
7589
    if (!obj || !JS_SetPrivate(cx, obj, xml)) {
 
7590
        cx->weakRoots.newborn[GCX_OBJECT] = NULL;
 
7591
        return NULL;
 
7592
    }
 
7593
    METER(xml_stats.xmlobj);
 
7594
    METER(xml_stats.livexmlobj);
 
7595
    return obj;
 
7596
}
 
7597
 
 
7598
JSObject *
 
7599
js_GetXMLObject(JSContext *cx, JSXML *xml)
 
7600
{
 
7601
    JSObject *obj;
 
7602
 
 
7603
    obj = xml->object;
 
7604
    if (obj) {
 
7605
        JS_ASSERT(JS_GetPrivate(cx, obj) == xml);
 
7606
        return obj;
 
7607
    }
 
7608
 
 
7609
    /*
 
7610
     * A JSXML cannot be shared among threads unless it has an object.
 
7611
     * A JSXML cannot be given an object unless:
 
7612
     * (a) it has no parent; or
 
7613
     * (b) its parent has no object (therefore is thread-private); or
 
7614
     * (c) its parent's object is locked.
 
7615
     *
 
7616
     * Once given an object, a JSXML is immutable.
 
7617
     */
 
7618
    JS_ASSERT(!xml->parent ||
 
7619
              !xml->parent->object ||
 
7620
              JS_IS_OBJ_LOCKED(cx, xml->parent->object));
 
7621
 
 
7622
    obj = NewXMLObject(cx, xml);
 
7623
    if (!obj)
 
7624
        return NULL;
 
7625
    xml->object = obj;
 
7626
    return obj;
 
7627
}
 
7628
 
 
7629
JSObject *
 
7630
js_InitNamespaceClass(JSContext *cx, JSObject *obj)
 
7631
{
 
7632
    return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2,
 
7633
                        namespace_props, namespace_methods, NULL, NULL);
 
7634
}
 
7635
 
 
7636
JSObject *
 
7637
js_InitQNameClass(JSContext *cx, JSObject *obj)
 
7638
{
 
7639
    return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2,
 
7640
                        qname_props, qname_methods, NULL, NULL);
 
7641
}
 
7642
 
 
7643
JSObject *
 
7644
js_InitAttributeNameClass(JSContext *cx, JSObject *obj)
 
7645
{
 
7646
    return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2,
 
7647
                        qname_props, qname_methods, NULL, NULL);
 
7648
}
 
7649
 
 
7650
JSObject *
 
7651
js_InitAnyNameClass(JSContext *cx, JSObject *obj)
 
7652
{
 
7653
    jsval v;
 
7654
 
 
7655
    if (!js_GetAnyName(cx, &v))
 
7656
        return NULL;
 
7657
    return JSVAL_TO_OBJECT(v);
 
7658
}
 
7659
 
 
7660
JSObject *
 
7661
js_InitXMLClass(JSContext *cx, JSObject *obj)
 
7662
{
 
7663
    JSObject *proto, *pobj, *ctor;
 
7664
    JSFunction *fun;
 
7665
    JSXML *xml;
 
7666
    JSProperty *prop;
 
7667
    JSScopeProperty *sprop;
 
7668
    jsval cval, argv[1], junk;
 
7669
 
 
7670
    /* Define the isXMLName function. */
 
7671
    if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
 
7672
        return NULL;
 
7673
 
 
7674
    /* Define the XML class constructor and prototype. */
 
7675
    proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
 
7676
                         NULL, xml_methods,
 
7677
                         xml_static_props, xml_static_methods);
 
7678
    if (!proto)
 
7679
        return NULL;
 
7680
 
 
7681
    xml = js_NewXML(cx, JSXML_CLASS_TEXT);
 
7682
    if (!xml || !JS_SetPrivate(cx, proto, xml))
 
7683
        return NULL;
 
7684
    xml->object = proto;
 
7685
    METER(xml_stats.xmlobj);
 
7686
    METER(xml_stats.livexmlobj);
 
7687
 
 
7688
    /*
 
7689
     * Prepare to set default settings on the XML constructor we just made.
 
7690
     * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY,
 
7691
     * which is xml_getProperty, which creates a new XMLList every time!  We
 
7692
     * must instead call js_LookupProperty directly.
 
7693
     */
 
7694
    if (!js_LookupProperty(cx, proto,
 
7695
                           ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
 
7696
                           &pobj, &prop)) {
 
7697
        return NULL;
 
7698
    }
 
7699
    JS_ASSERT(prop);
 
7700
    sprop = (JSScopeProperty *) prop;
 
7701
    JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
 
7702
    cval = OBJ_GET_SLOT(cx, pobj, sprop->slot);
 
7703
    OBJ_DROP_PROPERTY(cx, pobj, prop);
 
7704
    JS_ASSERT(VALUE_IS_FUNCTION(cx, cval));
 
7705
 
 
7706
    /* Set default settings. */
 
7707
    ctor = JSVAL_TO_OBJECT(cval);
 
7708
    argv[0] = JSVAL_VOID;
 
7709
    if (!xml_setSettings(cx, ctor, 1, argv, &junk))
 
7710
        return NULL;
 
7711
 
 
7712
    /* Define the XMLList function and give it the same prototype as XML. */
 
7713
    fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0);
 
7714
    if (!fun)
 
7715
        return NULL;
 
7716
    if (!js_SetClassPrototype(cx, fun->object, proto,
 
7717
                              JSPROP_READONLY | JSPROP_PERMANENT)) {
 
7718
        return NULL;
 
7719
    }
 
7720
    return proto;
 
7721
}
 
7722
 
 
7723
JSObject *
 
7724
js_InitXMLClasses(JSContext *cx, JSObject *obj)
 
7725
{
 
7726
    if (!js_InitNamespaceClass(cx, obj))
 
7727
        return NULL;
 
7728
    if (!js_InitQNameClass(cx, obj))
 
7729
        return NULL;
 
7730
    if (!js_InitAttributeNameClass(cx, obj))
 
7731
        return NULL;
 
7732
    if (!js_InitAnyNameClass(cx, obj))
 
7733
        return NULL;
 
7734
    return js_InitXMLClass(cx, obj);
 
7735
}
 
7736
 
 
7737
JSBool
 
7738
js_GetFunctionNamespace(JSContext *cx, jsval *vp)
 
7739
{
 
7740
    JSRuntime *rt;
 
7741
    JSObject *obj;
 
7742
    JSAtom *atom;
 
7743
    JSString *prefix, *uri;
 
7744
 
 
7745
    /* An invalid URI, for internal use only, guaranteed not to collide. */
 
7746
    static const char anti_uri[] = "@mozilla.org/js/function";
 
7747
 
 
7748
    /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
 
7749
    rt = cx->runtime;
 
7750
    obj = rt->functionNamespaceObject;
 
7751
    if (!obj) {
 
7752
        JS_LOCK_GC(rt);
 
7753
        obj = rt->functionNamespaceObject;
 
7754
        if (!obj) {
 
7755
            JS_UNLOCK_GC(rt);
 
7756
            atom = js_Atomize(cx, js_function_str, 8, 0);
 
7757
            JS_ASSERT(atom);
 
7758
            prefix = ATOM_TO_STRING(atom);
 
7759
 
 
7760
            /*
 
7761
             * Note that any race to atomize anti_uri here is resolved by
 
7762
             * the atom table code, such that at most one atom for anti_uri
 
7763
             * is created.  We store in rt->atomState.lazy unconditionally,
 
7764
             * since we are guaranteed to overwrite either null or the same
 
7765
             * atom pointer.
 
7766
             */
 
7767
            atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED);
 
7768
            if (!atom)
 
7769
                return JS_FALSE;
 
7770
            rt->atomState.lazy.functionNamespaceURIAtom = atom;
 
7771
 
 
7772
            uri = ATOM_TO_STRING(atom);
 
7773
            obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE);
 
7774
            if (!obj)
 
7775
                return JS_FALSE;
 
7776
 
 
7777
            /*
 
7778
             * Avoid entraining any in-scope Object.prototype.  The loss of
 
7779
             * Namespace.prototype is not detectable, as there is no way to
 
7780
             * refer to this instance in scripts.  When used to qualify method
 
7781
             * names, its prefix and uri references are copied to the QName.
 
7782
             */
 
7783
            OBJ_SET_PROTO(cx, obj, NULL);
 
7784
            OBJ_SET_PARENT(cx, obj, NULL);
 
7785
 
 
7786
            JS_LOCK_GC(rt);
 
7787
            if (!rt->functionNamespaceObject)
 
7788
                rt->functionNamespaceObject = obj;
 
7789
            else
 
7790
                obj = rt->functionNamespaceObject;
 
7791
        }
 
7792
        JS_UNLOCK_GC(rt);
 
7793
    }
 
7794
    *vp = OBJECT_TO_JSVAL(obj);
 
7795
    return JS_TRUE;
 
7796
}
 
7797
 
 
7798
/*
 
7799
 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
 
7800
 * Namespace.  Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
 
7801
 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a
 
7802
 * lightweight function activation).  There's no requirement that fp->varobj
 
7803
 * lie directly on fp->scopeChain, although it should be reachable using the
 
7804
 * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h).
 
7805
 *
 
7806
 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
 
7807
 * creates a default namespace via 'new Namespace()'.  In contrast, Set uses
 
7808
 * its v argument as the uri of a new Namespace, with "" as the prefix.  See
 
7809
 * ECMA-357 12.1 and 12.1.1.  Note that if Set is called with a Namespace n,
 
7810
 * the default XML namespace will be set to ("", n.uri).  So the uri string
 
7811
 * is really the only usefully stored value of the default namespace.
 
7812
 */
 
7813
JSBool
 
7814
js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
 
7815
{
 
7816
    JSStackFrame *fp;
 
7817
    JSObject *nsobj, *obj, *tmp;
 
7818
    jsval v;
 
7819
 
 
7820
    fp = cx->fp;
 
7821
    nsobj = fp->xmlNamespace;
 
7822
    if (nsobj) {
 
7823
        *vp = OBJECT_TO_JSVAL(nsobj);
 
7824
        return JS_TRUE;
 
7825
    }
 
7826
 
 
7827
    obj = NULL;
 
7828
    for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) {
 
7829
        obj = tmp;
 
7830
        if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v))
 
7831
            return JS_FALSE;
 
7832
        if (!JSVAL_IS_PRIMITIVE(v)) {
 
7833
            fp->xmlNamespace = JSVAL_TO_OBJECT(v);
 
7834
            *vp = v;
 
7835
            return JS_TRUE;
 
7836
        }
 
7837
    }
 
7838
 
 
7839
    nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL);
 
7840
    if (!nsobj)
 
7841
        return JS_FALSE;
 
7842
    v = OBJECT_TO_JSVAL(nsobj);
 
7843
    if (obj &&
 
7844
        !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v,
 
7845
                             JS_PropertyStub, JS_PropertyStub,
 
7846
                             JSPROP_PERMANENT, NULL)) {
 
7847
        return JS_FALSE;
 
7848
    }
 
7849
    fp->xmlNamespace = nsobj;
 
7850
    *vp = v;
 
7851
    return JS_TRUE;
 
7852
}
 
7853
 
 
7854
JSBool
 
7855
js_SetDefaultXMLNamespace(JSContext *cx, jsval v)
 
7856
{
 
7857
    jsval argv[2];
 
7858
    JSObject *nsobj, *varobj;
 
7859
    JSStackFrame *fp;
 
7860
 
 
7861
    argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString);
 
7862
    argv[1] = v;
 
7863
    nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
 
7864
                               2, argv);
 
7865
    if (!nsobj)
 
7866
        return JS_FALSE;
 
7867
    v = OBJECT_TO_JSVAL(nsobj);
 
7868
 
 
7869
    fp = cx->fp;
 
7870
    varobj = fp->varobj;
 
7871
    if (varobj) {
 
7872
        if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v,
 
7873
                                 JS_PropertyStub, JS_PropertyStub,
 
7874
                                 JSPROP_PERMANENT, NULL)) {
 
7875
            return JS_FALSE;
 
7876
        }
 
7877
    } else {
 
7878
        JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags));
 
7879
    }
 
7880
    fp->xmlNamespace = JSVAL_TO_OBJECT(v);
 
7881
    return JS_TRUE;
 
7882
}
 
7883
 
 
7884
JSBool
 
7885
js_ToAttributeName(JSContext *cx, jsval *vp)
 
7886
{
 
7887
    JSXMLQName *qn;
 
7888
 
 
7889
    qn = ToAttributeName(cx, *vp);
 
7890
    if (!qn)
 
7891
        return JS_FALSE;
 
7892
    *vp = OBJECT_TO_JSVAL(qn->object);
 
7893
    return JS_TRUE;
 
7894
}
 
7895
 
 
7896
JSString *
 
7897
js_EscapeAttributeValue(JSContext *cx, JSString *str)
 
7898
{
 
7899
    return EscapeAttributeValue(cx, NULL, str);
 
7900
}
 
7901
 
 
7902
JSString *
 
7903
js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
 
7904
{
 
7905
    size_t len, len2, newlen;
 
7906
    jschar *chars;
 
7907
 
 
7908
    if (JSSTRING_IS_DEPENDENT(str) ||
 
7909
        !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) {
 
7910
        str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
 
7911
                                0);
 
7912
        if (!str)
 
7913
            return NULL;
 
7914
    }
 
7915
 
 
7916
    len = str->length;
 
7917
    len2 = JSSTRING_LENGTH(str2);
 
7918
    newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
 
7919
    chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar));
 
7920
    if (!chars)
 
7921
        return NULL;
 
7922
 
 
7923
    /*
 
7924
     * Reallocating str (because we know it has no other references) requires
 
7925
     * purging any deflated string cached for it.
 
7926
     */
 
7927
    js_PurgeDeflatedStringCache(cx->runtime, str);
 
7928
 
 
7929
    str->chars = chars;
 
7930
    str->length = newlen;
 
7931
    chars += len;
 
7932
    if (isName) {
 
7933
        *chars++ = ' ';
 
7934
        js_strncpy(chars, JSSTRING_CHARS(str2), len2);
 
7935
        chars += len2;
 
7936
    } else {
 
7937
        *chars++ = '=';
 
7938
        *chars++ = '"';
 
7939
        js_strncpy(chars, JSSTRING_CHARS(str2), len2);
 
7940
        chars += len2;
 
7941
        *chars++ = '"';
 
7942
    }
 
7943
    *chars = 0;
 
7944
    return str;
 
7945
}
 
7946
 
 
7947
JSString *
 
7948
js_EscapeElementValue(JSContext *cx, JSString *str)
 
7949
{
 
7950
    return EscapeElementValue(cx, NULL, str);
 
7951
}
 
7952
 
 
7953
JSString *
 
7954
js_ValueToXMLString(JSContext *cx, jsval v)
 
7955
{
 
7956
    return ToXMLString(cx, v);
 
7957
}
 
7958
 
 
7959
static JSBool
 
7960
anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
7961
                 jsval *rval)
 
7962
{
 
7963
    *rval = ATOM_KEY(cx->runtime->atomState.starAtom);
 
7964
    return JS_TRUE;
 
7965
}
 
7966
 
 
7967
JSBool
 
7968
js_GetAnyName(JSContext *cx, jsval *vp)
 
7969
{
 
7970
    JSRuntime *rt;
 
7971
    JSObject *obj;
 
7972
    JSXMLQName *qn;
 
7973
    JSBool ok;
 
7974
 
 
7975
    /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
 
7976
    rt = cx->runtime;
 
7977
    obj = rt->anynameObject;
 
7978
    if (!obj) {
 
7979
        JS_LOCK_GC(rt);
 
7980
        obj = rt->anynameObject;
 
7981
        if (!obj) {
 
7982
            JS_UNLOCK_GC(rt);
 
7983
 
 
7984
            /*
 
7985
             * Protect multiple newborns created below, in the do-while(0)
 
7986
             * loop used to ensure that we leave this local root scope.
 
7987
             */
 
7988
            ok = js_EnterLocalRootScope(cx);
 
7989
            if (!ok)
 
7990
                return JS_FALSE;
 
7991
 
 
7992
            do {
 
7993
                qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString,
 
7994
                                    ATOM_TO_STRING(rt->atomState.starAtom));
 
7995
                if (!qn) {
 
7996
                    ok = JS_FALSE;
 
7997
                    break;
 
7998
                }
 
7999
 
 
8000
                obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL);
 
8001
                if (!obj || !JS_SetPrivate(cx, obj, qn)) {
 
8002
                    cx->weakRoots.newborn[GCX_OBJECT] = NULL;
 
8003
                    ok = JS_FALSE;
 
8004
                    break;
 
8005
                }
 
8006
                qn->object = obj;
 
8007
                METER(xml_stats.qnameobj);
 
8008
                METER(xml_stats.liveqnameobj);
 
8009
 
 
8010
                /*
 
8011
                 * Avoid entraining any Object.prototype found via cx's scope
 
8012
                 * chain or global object.  This loses the default toString,
 
8013
                 * but no big deal: we want to customize toString anyway for
 
8014
                 * clearer diagnostics.
 
8015
                 */
 
8016
                if (!JS_DefineFunction(cx, obj, js_toString_str,
 
8017
                                       anyname_toString, 0, 0)) {
 
8018
                    ok = JS_FALSE;
 
8019
                    break;
 
8020
                }
 
8021
                OBJ_SET_PROTO(cx, obj, NULL);
 
8022
                JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
 
8023
            } while (0);
 
8024
 
 
8025
            js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj));
 
8026
            if (!ok)
 
8027
                return JS_FALSE;
 
8028
 
 
8029
            JS_LOCK_GC(rt);
 
8030
            if (!rt->anynameObject)
 
8031
                rt->anynameObject = obj;
 
8032
            else
 
8033
                obj = rt->anynameObject;
 
8034
        }
 
8035
        JS_UNLOCK_GC(rt);
 
8036
    }
 
8037
    *vp = OBJECT_TO_JSVAL(obj);
 
8038
    return JS_TRUE;
 
8039
}
 
8040
 
 
8041
JSBool
 
8042
js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep)
 
8043
{
 
8044
    JSXMLQName *qn;
 
8045
    jsid funid, id;
 
8046
    JSObject *obj, *pobj, *lastobj;
 
8047
    JSProperty *prop;
 
8048
    const char *printable;
 
8049
 
 
8050
    qn = ToXMLName(cx, name, &funid);
 
8051
    if (!qn)
 
8052
        return JS_FALSE;
 
8053
    id = OBJECT_TO_JSID(qn->object);
 
8054
 
 
8055
    obj = cx->fp->scopeChain;
 
8056
    do {
 
8057
        if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
 
8058
            return JS_FALSE;
 
8059
        if (prop) {
 
8060
            OBJ_DROP_PROPERTY(cx, pobj, prop);
 
8061
 
 
8062
            /*
 
8063
             * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML
 
8064
             * object to carry scope chain linkage in js_FilterXMLList.
 
8065
             */
 
8066
            pobj = OBJ_THIS_OBJECT(cx, obj);
 
8067
            if (OBJECT_IS_XML(cx, pobj)) {
 
8068
                *objp = pobj;
 
8069
                *namep = ID_TO_VALUE(id);
 
8070
                return JS_TRUE;
 
8071
            }
 
8072
        }
 
8073
 
 
8074
        lastobj = obj;
 
8075
    } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
 
8076
 
 
8077
    printable = js_ValueToPrintableString(cx, name);
 
8078
    if (printable) {
 
8079
        JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
 
8080
                                     js_GetErrorMessage, NULL,
 
8081
                                     JSMSG_UNDEFINED_XML_NAME, printable);
 
8082
    }
 
8083
    return JS_FALSE;
 
8084
}
 
8085
 
 
8086
JSBool
 
8087
js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp)
 
8088
{
 
8089
    return GetProperty(cx, obj, name, vp);
 
8090
}
 
8091
 
 
8092
JSBool
 
8093
js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
8094
{
 
8095
    JSObject *target;
 
8096
    JSXML *xml;
 
8097
    JSTempValueRooter tvr;
 
8098
    JSBool ok;
 
8099
 
 
8100
    JS_ASSERT(OBJECT_IS_XML(cx, obj));
 
8101
 
 
8102
    /* After this point, control must flow through label out: to exit. */
 
8103
    JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
 
8104
 
 
8105
    /*
 
8106
     * See comments before xml_lookupProperty about the need for the proto
 
8107
     * chain lookup.
 
8108
     */
 
8109
    target = obj;
 
8110
    for (;;) {
 
8111
        ok = js_GetProperty(cx, target, id, vp);
 
8112
        if (!ok)
 
8113
            goto out;
 
8114
        if (VALUE_IS_FUNCTION(cx, *vp)) {
 
8115
            ok = JS_TRUE;
 
8116
            goto out;
 
8117
        }
 
8118
        target = OBJ_GET_PROTO(cx, target);
 
8119
        if (target == NULL)
 
8120
            break;
 
8121
        tvr.u.object = target;
 
8122
    }
 
8123
 
 
8124
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
8125
    if (HasSimpleContent(xml)) {
 
8126
        /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
 
8127
        ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String),
 
8128
                                  &tvr.u.object);
 
8129
        if (!ok)
 
8130
            goto out;
 
8131
        JS_ASSERT(tvr.u.object);
 
8132
        ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp);
 
8133
    }
 
8134
 
 
8135
  out:
 
8136
    JS_POP_TEMP_ROOT(cx, &tvr);
 
8137
    return ok;
 
8138
}
 
8139
 
 
8140
JSBool
 
8141
js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp)
 
8142
{
 
8143
    return PutProperty(cx, obj, name, vp);
 
8144
}
 
8145
 
 
8146
static JSXML *
 
8147
GetPrivate(JSContext *cx, JSObject *obj, const char *method)
 
8148
{
 
8149
    JSXML *xml;
 
8150
 
 
8151
    xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
 
8152
    if (!xml) {
 
8153
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
8154
                             JSMSG_INCOMPATIBLE_METHOD,
 
8155
                             js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name);
 
8156
    }
 
8157
    return xml;
 
8158
}
 
8159
 
 
8160
JSBool
 
8161
js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
8162
{
 
8163
    JSXML *xml, *list;
 
8164
 
 
8165
    xml = GetPrivate(cx, obj, "descendants internal method");
 
8166
    if (!xml)
 
8167
        return JS_FALSE;
 
8168
 
 
8169
    list = Descendants(cx, xml, id);
 
8170
    if (!list)
 
8171
        return JS_FALSE;
 
8172
    *vp = OBJECT_TO_JSVAL(list->object);
 
8173
    return JS_TRUE;
 
8174
}
 
8175
 
 
8176
JSBool
 
8177
js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
 
8178
{
 
8179
    JSXML *list;
 
8180
    uint32 n;
 
8181
    jsval junk;
 
8182
 
 
8183
    list = (JSXML *) JS_GetPrivate(cx, listobj);
 
8184
    for (n = list->xml_kids.length; n != 0; --n) {
 
8185
        if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk))
 
8186
            return JS_FALSE;
 
8187
    }
 
8188
    return JS_TRUE;
 
8189
}
 
8190
 
 
8191
JSBool
 
8192
js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp)
 
8193
{
 
8194
    JSBool ok, match;
 
8195
    JSStackFrame *fp;
 
8196
    uint32 flags;
 
8197
    JSObject *scobj, *listobj, *resobj, *withobj, *kidobj;
 
8198
    JSXML *xml, *list, *result, *kid;
 
8199
    JSXMLArrayCursor cursor;
 
8200
 
 
8201
    ok = js_EnterLocalRootScope(cx);
 
8202
    if (!ok)
 
8203
        return JS_FALSE;
 
8204
 
 
8205
    /* All control flow after this point must exit via label out or bad. */
 
8206
    *vp = JSVAL_NULL;
 
8207
    fp = cx->fp;
 
8208
    flags = fp->flags;
 
8209
    fp->flags = flags | JSFRAME_FILTERING;
 
8210
    scobj = js_GetScopeChain(cx, fp);
 
8211
    withobj = NULL;
 
8212
    if (!scobj)
 
8213
        goto bad;
 
8214
    xml = GetPrivate(cx, obj, "filtering predicate operator");
 
8215
    if (!xml)
 
8216
        goto bad;
 
8217
 
 
8218
    if (xml->xml_class == JSXML_CLASS_LIST) {
 
8219
        list = xml;
 
8220
    } else {
 
8221
        listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
8222
        if (!listobj)
 
8223
            goto bad;
 
8224
        list = (JSXML *) JS_GetPrivate(cx, listobj);
 
8225
        ok = Append(cx, list, xml);
 
8226
        if (!ok)
 
8227
            goto out;
 
8228
    }
 
8229
 
 
8230
    resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
 
8231
    if (!resobj)
 
8232
        goto bad;
 
8233
    result = (JSXML *) JS_GetPrivate(cx, resobj);
 
8234
 
 
8235
    /* Hoist the scope chain update out of the loop over kids. */
 
8236
    withobj = js_NewWithObject(cx, NULL, scobj, -1);
 
8237
    if (!withobj)
 
8238
        goto bad;
 
8239
    fp->scopeChain = withobj;
 
8240
 
 
8241
    XMLArrayCursorInit(&cursor, &list->xml_kids);
 
8242
    while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
 
8243
        kidobj = js_GetXMLObject(cx, kid);
 
8244
        if (!kidobj)
 
8245
            break;
 
8246
        OBJ_SET_PROTO(cx, withobj, kidobj);
 
8247
        ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match);
 
8248
        if (ok && match)
 
8249
            ok = Append(cx, result, kid);
 
8250
        if (!ok)
 
8251
            break;
 
8252
    }
 
8253
    XMLArrayCursorFinish(&cursor);
 
8254
    if (!ok)
 
8255
        goto out;
 
8256
    if (kid)
 
8257
        goto bad;
 
8258
 
 
8259
    *vp = OBJECT_TO_JSVAL(resobj);
 
8260
 
 
8261
out:
 
8262
    fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS);
 
8263
    if (withobj) {
 
8264
        fp->scopeChain = scobj;
 
8265
        JS_SetPrivate(cx, withobj, NULL);
 
8266
    }
 
8267
    js_LeaveLocalRootScopeWithResult(cx, *vp);
 
8268
    return ok;
 
8269
bad:
 
8270
    ok = JS_FALSE;
 
8271
    goto out;
 
8272
}
 
8273
 
 
8274
JSObject *
 
8275
js_ValueToXMLObject(JSContext *cx, jsval v)
 
8276
{
 
8277
    return ToXML(cx, v);
 
8278
}
 
8279
 
 
8280
JSObject *
 
8281
js_ValueToXMLListObject(JSContext *cx, jsval v)
 
8282
{
 
8283
    return ToXMLList(cx, v);
 
8284
}
 
8285
 
 
8286
JSObject *
 
8287
js_CloneXMLObject(JSContext *cx, JSObject *obj)
 
8288
{
 
8289
    uintN flags;
 
8290
    JSXML *xml;
 
8291
 
 
8292
    if (!GetXMLSettingFlags(cx, &flags))
 
8293
        return NULL;
 
8294
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
8295
    if (flags & (XSF_IGNORE_COMMENTS |
 
8296
                 XSF_IGNORE_PROCESSING_INSTRUCTIONS |
 
8297
                 XSF_IGNORE_WHITESPACE)) {
 
8298
        xml = DeepCopy(cx, xml, NULL, flags);
 
8299
        if (!xml)
 
8300
            return NULL;
 
8301
        return xml->object;
 
8302
    }
 
8303
    return NewXMLObject(cx, xml);
 
8304
}
 
8305
 
 
8306
JSObject *
 
8307
js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
 
8308
                       JSString *value)
 
8309
{
 
8310
    uintN flags;
 
8311
    JSObject *obj;
 
8312
    JSXML *xml;
 
8313
    JSXMLQName *qn;
 
8314
 
 
8315
    if (!GetXMLSettingFlags(cx, &flags))
 
8316
        return NULL;
 
8317
 
 
8318
    if ((xml_class == JSXML_CLASS_COMMENT &&
 
8319
         (flags & XSF_IGNORE_COMMENTS)) ||
 
8320
        (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
 
8321
         (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
 
8322
        return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
 
8323
    }
 
8324
 
 
8325
    obj = js_NewXMLObject(cx, xml_class);
 
8326
    if (!obj)
 
8327
        return NULL;
 
8328
    xml = (JSXML *) JS_GetPrivate(cx, obj);
 
8329
    if (name) {
 
8330
        qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name);
 
8331
        if (!qn)
 
8332
            return NULL;
 
8333
        xml->name = qn;
 
8334
    }
 
8335
    xml->xml_value = value;
 
8336
    return obj;
 
8337
}
 
8338
 
 
8339
JSString *
 
8340
js_MakeXMLCDATAString(JSContext *cx, JSString *str)
 
8341
{
 
8342
    return MakeXMLCDATAString(cx, NULL, str);
 
8343
}
 
8344
 
 
8345
JSString *
 
8346
js_MakeXMLCommentString(JSContext *cx, JSString *str)
 
8347
{
 
8348
    return MakeXMLCommentString(cx, NULL, str);
 
8349
}
 
8350
 
 
8351
JSString *
 
8352
js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
 
8353
{
 
8354
    return MakeXMLPIString(cx, NULL, name, str);
 
8355
}
 
8356
 
 
8357
#endif /* JS_HAS_XML_SUPPORT */