1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
* vim: set ts=4 sw=4 et tw=78:
4
* ***** BEGIN LICENSE BLOCK *****
5
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17
* The Original Code is SpiderMonkey E4X code, released August, 2004.
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.
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.
38
* ***** END LICENSE BLOCK ***** */
43
#if JS_HAS_XML_SUPPORT
72
#include <string.h> /* for #ifdef DEBUG memset calls */
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
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"
98
jsrefcount liveqnameobj;
100
jsrefcount namespaceobj;
101
jsrefcount livenamespace;
102
jsrefcount livenamespaceobj;
106
jsrefcount livexmlobj;
109
#define METER(x) JS_ATOMIC_INCREMENT(&(x))
110
#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
112
#define METER(x) /* nothing */
113
#define UNMETER(x) /* nothing */
117
* Random utilities and global functions.
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";
127
const char js_amp_entity_str[] = "&";
128
const char js_gt_entity_str[] = ">";
129
const char js_lt_entity_str[] = "<";
130
const char js_quot_entity_str[] = """;
132
#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
133
#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
136
xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
139
*rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
144
* Namespace class and library functions.
146
enum namespace_tinyid {
147
NAMESPACE_PREFIX = -1,
152
namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
156
if (!JSVAL_IS_INT(id))
159
ns = (JSXMLNamespace *)
160
JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL);
164
switch (JSVAL_TO_INT(id)) {
165
case NAMESPACE_PREFIX:
166
*vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID;
169
*vp = STRING_TO_JSVAL(ns->uri);
176
namespace_finalize(JSContext *cx, JSObject *obj)
181
ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
184
JS_ASSERT(ns->object == obj);
186
UNMETER(xml_stats.livenamespaceobj);
189
if (rt->functionNamespaceObject == obj)
190
rt->functionNamespaceObject = NULL;
194
namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len)
199
for (i = 0; i < len; i++) {
205
JS_snprintf(buf, sizeof buf, "%s=%s",
206
ns->prefix ? JS_GetStringBytes(ns->prefix) : "",
207
JS_GetStringBytes(ns->uri));
209
GC_MARK(cx, ns, buf);
215
namespace_mark(JSContext *cx, JSObject *obj, void *arg)
219
ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
220
GC_MARK(cx, ns, "private");
225
namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
227
JSXMLNamespace *ns, *ns2;
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) {
236
ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2);
237
*bp = js_EqualStrings(ns->uri, ns2->uri);
242
JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
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
254
#define NAMESPACE_ATTRS \
255
(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
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},
264
namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
269
ns = (JSXMLNamespace *)
270
JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv);
274
*rval = STRING_TO_JSVAL(ns->uri);
278
static JSFunctionSpec namespace_methods[] = {
279
{js_toString_str, namespace_toString, 0,0,0},
284
js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri,
289
ns = (JSXMLNamespace *)
290
js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace));
296
ns->declared = declared;
297
METER(xml_stats.namespace);
298
METER(xml_stats.livenamespace);
303
js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
305
GC_MARK(cx, ns->object, "object");
306
GC_MARK(cx, ns->prefix, "prefix");
307
GC_MARK(cx, ns->uri, "uri");
311
js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
313
UNMETER(xml_stats.livenamespace);
317
js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri,
322
ns = js_NewXMLNamespace(cx, prefix, uri, declared);
325
return js_GetXMLNamespaceObject(cx, ns);
329
js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns)
335
JS_ASSERT(JS_GetPrivate(cx, obj) == ns);
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;
344
METER(xml_stats.namespaceobj);
345
METER(xml_stats.livenamespaceobj);
350
* QName class and library functions.
358
qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
362
if (!JSVAL_IS_INT(id))
366
JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL);
370
switch (JSVAL_TO_INT(id)) {
372
*vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL;
374
case QNAME_LOCALNAME:
375
*vp = STRING_TO_JSVAL(qn->localName);
382
qname_finalize(JSContext *cx, JSObject *obj)
386
qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
389
JS_ASSERT(qn->object == obj);
391
UNMETER(xml_stats.liveqnameobj);
395
anyname_finalize(JSContext* cx, JSObject* obj)
399
/* Make sure the next call to js_GetAnyName doesn't try to use obj. */
401
if (rt->anynameObject == obj)
402
rt->anynameObject = NULL;
404
qname_finalize(cx, obj);
408
qname_mark(JSContext *cx, JSObject *obj, void *arg)
412
qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
413
GC_MARK(cx, qn, "private");
418
qname_identity(JSXMLQName *qna, JSXMLQName *qnb)
420
if (!qna->uri ^ !qnb->uri)
422
if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri))
424
return js_EqualStrings(qna->localName, qnb->localName);
428
qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
430
JSXMLQName *qn, *qn2;
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) {
439
qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2);
440
*bp = qname_identity(qn, qn2);
445
JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
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
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.
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
473
JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
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
483
#define QNAME_ATTRS \
484
(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
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},
493
qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
498
JSString *str, *qualstr;
502
clasp = OBJ_GET_CLASS(cx, obj);
503
if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) {
504
qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
507
JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv);
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;
519
qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
520
str = js_ConcatStrings(cx, qn->uri, qualstr);
524
str = js_ConcatStrings(cx, str, qn->localName);
528
if (str && clasp == &js_AttributeNameClass) {
529
length = JSSTRING_LENGTH(str);
530
chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
534
js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
536
str = js_NewString(cx, chars, length, 0);
543
*rval = STRING_TO_JSVAL(str);
547
static JSFunctionSpec qname_methods[] = {
548
{js_toString_str, qname_toString, 0,0,0},
553
js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix,
558
qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName));
564
qn->localName = localName;
565
METER(xml_stats.qname);
566
METER(xml_stats.liveqname);
571
js_MarkXMLQName(JSContext *cx, JSXMLQName *qn)
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");
580
js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn)
582
UNMETER(xml_stats.liveqname);
586
js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix,
591
qn = js_NewXMLQName(cx, uri, prefix, localName);
594
return js_GetXMLQNameObject(cx, qn);
598
js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn)
604
JS_ASSERT(JS_GetPrivate(cx, obj) == qn);
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;
613
METER(xml_stats.qnameobj);
614
METER(xml_stats.liveqnameobj);
619
js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn)
625
if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass)
627
qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
632
obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL);
633
if (!obj || !JS_SetPrivate(cx, obj, qn)) {
634
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
639
METER(xml_stats.qnameobj);
640
METER(xml_stats.liveqnameobj);
645
js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
651
* The _QualifiedIdentifier : PropertySelector :: PropertySelector_
652
* production, step 2.
654
if (!JSVAL_IS_PRIMITIVE(nsval) &&
655
OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
661
return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
665
IsXMLName(const jschar *cp, size_t n)
671
if (n != 0 && JS_ISXMLNSSTART(*cp)) {
683
js_IsXMLName(JSContext *cx, jsval v)
688
JSErrorReporter older;
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.
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;
704
older = JS_SetErrorReporter(cx, NULL);
705
name = js_ValueToString(cx, v);
706
JS_SetErrorReporter(cx, older);
708
JS_ClearPendingException(cx);
713
return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
717
Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
719
jsval urival, prefixval;
721
JSBool isNamespace, isQName;
723
JSString *empty, *prefix;
724
JSXMLNamespace *ns, *ns2;
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);
735
#ifdef __GNUC__ /* suppress bogus gcc warnings */
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. */
747
/* Create and return a new QName object exactly as if constructed. */
748
obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
751
*rval = OBJECT_TO_JSVAL(obj);
753
METER(xml_stats.namespaceobj);
754
METER(xml_stats.livenamespaceobj);
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
761
empty = cx->runtime->emptyString;
762
ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE);
765
if (!JS_SetPrivate(cx, obj, ns))
771
ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj);
773
ns->prefix = ns2->prefix;
774
} else if (isQName &&
775
(qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
777
ns->prefix = qn->prefix;
779
ns->uri = js_ValueToString(cx, urival);
783
/* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
784
if (!IS_EMPTY(ns->uri))
787
} else if (argc == 2) {
789
(qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
792
ns->uri = js_ValueToString(cx, urival);
798
if (IS_EMPTY(ns->uri)) {
799
if (!JSVAL_IS_VOID(prefixval)) {
800
prefix = js_ValueToString(cx, prefixval);
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)));
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. */
815
prefix = js_ValueToString(cx, prefixval);
826
QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
828
jsval nameval, nsval;
829
JSBool isQName, isNamespace;
831
JSString *uri, *prefix, *name;
836
nameval = argv[argc > 1];
838
!JSVAL_IS_PRIMITIVE(nameval) &&
839
OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
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. */
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).
854
obj = js_NewObject(cx,
855
JS_ValueToFunction(cx, argv[-2])->clasp,
859
*rval = OBJECT_TO_JSVAL(obj);
861
METER(xml_stats.qnameobj);
862
METER(xml_stats.liveqnameobj);
865
/* If namespace is not specified and name is a QName, clone it. */
866
qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval));
870
name = qn->localName;
874
/* Namespace and qname were passed -- use the qname's localName. */
875
nameval = STRING_TO_JSVAL(qn->localName);
879
name = cx->runtime->emptyString;
881
name = js_ValueToString(cx, nameval);
885
/* Use argv[1] as a local root for name, even if it was not passed. */
886
argv[1] = STRING_TO_JSVAL(name);
890
if (argc == 1 || JSVAL_IS_VOID(nsval)) {
894
if (!js_GetDefaultXMLNamespace(cx, &nsval))
899
if (JSVAL_IS_NULL(nsval)) {
900
/* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
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
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);
917
#ifdef __GNUC__ /* suppress bogus gcc warnings */
922
ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
925
} else if (isQName &&
926
(qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) {
930
uri = js_ValueToString(cx, nsval);
933
argv[0] = STRING_TO_JSVAL(uri); /* local root */
935
/* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
936
prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
941
qn = js_NewXMLQName(cx, uri, prefix, name);
944
if (!JS_SetPrivate(cx, obj, qn))
951
AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
955
* Since js_AttributeNameClass was initialized, obj will have that as its
956
* class, not js_QNameClass.
958
return QName(cx, obj, argc, argv, rval);
962
* XMLArray library functions.
965
namespace_identity(const void *a, const void *b)
967
const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
968
const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
970
if (nsa->prefix && nsb->prefix) {
971
if (!js_EqualStrings(nsa->prefix, nsb->prefix))
974
if (nsa->prefix || nsb->prefix)
977
return js_EqualStrings(nsa->uri, nsb->uri);
981
attr_identity(const void *a, const void *b)
983
const JSXML *xmla = (const JSXML *) a;
984
const JSXML *xmlb = (const JSXML *) b;
986
return qname_identity(xmla->name, xmlb->name);
990
XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
992
JSXMLArrayCursor *next;
994
cursor->array = array;
996
next = cursor->next = array->cursors;
998
next->prevp = &cursor->next;
999
cursor->prevp = &array->cursors;
1000
array->cursors = cursor;
1001
cursor->root = NULL;
1005
XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
1007
JSXMLArrayCursor *next;
1011
next = cursor->next;
1013
next->prevp = cursor->prevp;
1014
*cursor->prevp = next;
1015
cursor->array = NULL;
1019
XMLArrayCursorNext(JSXMLArrayCursor *cursor)
1023
array = cursor->array;
1024
if (!array || cursor->index >= array->length)
1026
return cursor->root = array->vector[cursor->index++];
1030
XMLArrayCursorItem(JSXMLArrayCursor *cursor)
1034
array = cursor->array;
1035
if (!array || cursor->index >= array->length)
1037
return cursor->root = array->vector[cursor->index];
1041
XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor)
1044
GC_MARK(cx, cursor->root, "cursor->root");
1045
cursor = cursor->next;
1049
/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */
1051
XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
1055
if (capacity == 0) {
1056
/* We could let realloc(p, 0) free this, but purify gets confused. */
1058
free(array->vector);
1061
if ((size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1062
!(vector = (void **)
1063
realloc(array->vector, capacity * sizeof(void *)))) {
1065
JS_ReportOutOfMemory(cx);
1069
array->capacity = JSXML_PRESET_CAPACITY | capacity;
1070
array->vector = vector;
1075
XMLArrayTrim(JSXMLArray *array)
1077
if (array->capacity & JSXML_PRESET_CAPACITY)
1079
if (array->length < array->capacity)
1080
XMLArraySetCapacity(NULL, array, array->length);
1084
XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
1086
array->length = array->capacity = 0;
1087
array->vector = NULL;
1088
array->cursors = NULL;
1089
return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
1093
XMLArrayFinish(JSContext *cx, JSXMLArray *array)
1095
JSXMLArrayCursor *cursor;
1097
JS_free(cx, array->vector);
1099
while ((cursor = array->cursors) != NULL)
1100
XMLArrayCursorFinish(cursor);
1103
memset(array, 0xd5, sizeof *array);
1107
#define XML_NOT_FOUND ((uint32) -1)
1110
XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
1115
/* The identity op must not reallocate array->vector. */
1116
vector = array->vector;
1118
for (i = 0, n = array->length; i < n; i++) {
1119
if (identity(vector[i], elt))
1123
for (i = 0, n = array->length; i < n; i++) {
1124
if (vector[i] == elt)
1128
return XML_NOT_FOUND;
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.
1136
#define LINEAR_THRESHOLD 256
1137
#define LINEAR_INCREMENT 32
1140
XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
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);
1153
JS_CEILING_LOG2(log2, capacity);
1154
capacity = JS_BIT(log2);
1156
if ((size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1157
!(vector = (void **)
1158
realloc(array->vector, capacity * sizeof(void *)))) {
1159
JS_ReportOutOfMemory(cx);
1162
array->capacity = capacity;
1163
array->vector = vector;
1164
for (i = array->length; i < index; i++)
1167
array->length = index + 1;
1170
array->vector[index] = elt;
1175
XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1178
JSXMLArrayCursor *cursor;
1182
if (!XMLArraySetCapacity(cx, array, j + n))
1185
array->length = j + n;
1186
JS_ASSERT(n != (uint32)-1);
1189
array->vector[j + n] = array->vector[j];
1192
for (cursor = array->cursors; cursor; cursor = cursor->next) {
1193
if (cursor->index > i)
1200
XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1203
void **vector, *elt;
1204
JSXMLArrayCursor *cursor;
1206
length = array->length;
1207
if (index >= length)
1210
vector = array->vector;
1211
elt = vector[index];
1213
while (++index < length)
1214
vector[index-1] = vector[index];
1215
array->length = length - 1;
1216
array->capacity = JSXML_CAPACITY(array);
1218
vector[index] = NULL;
1221
for (cursor = array->cursors; cursor; cursor = cursor->next) {
1222
if (cursor->index > index)
1229
XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1233
JS_ASSERT(!array->cursors);
1234
if (length >= array->length)
1239
free(array->vector);
1242
vector = realloc(array->vector, length * sizeof(void *));
1247
if (array->length > length)
1248
array->length = length;
1249
array->capacity = length;
1250
array->vector = vector;
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) != \
1256
#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1257
? (t *) (a)->vector[i] \
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)); \
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)
1271
* Define XML setting property strings and constants early, so everyone can
1272
* use the same names and their magic numbers (tinyids, flags).
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";
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.
1287
enum xml_static_tinyid {
1288
XML_IGNORE_COMMENTS,
1289
XML_IGNORE_PROCESSING_INSTRUCTIONS,
1290
XML_IGNORE_WHITESPACE,
1291
XML_PRETTY_PRINTING,
1296
xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1302
xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1307
JS_ASSERT(JSVAL_IS_INT(id));
1308
if (!js_ValueToBoolean(cx, *vp, &b))
1311
flag = JS_BIT(JSVAL_TO_INT(id));
1313
cx->xmlSettingFlags |= flag;
1315
cx->xmlSettingFlags &= ~flag;
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},
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)
1343
* Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1344
* This flag means a couple of things:
1346
* - The top JSXML created for a parse tree must have an object owning it.
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.
1353
* Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1355
#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
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)))
1361
#define IS_XMLNS(str) \
1362
(JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1364
#define IS_XML_CHARS(chars) \
1365
(JS_TOLOWER((chars)[0]) == 'x' && \
1366
JS_TOLOWER((chars)[1]) == 'm' && \
1367
JS_TOLOWER((chars)[2]) == 'l')
1369
#define HAS_NS_AFTER_XML(chars) \
1370
(JS_TOLOWER((chars)[3]) == 'n' && \
1371
JS_TOLOWER((chars)[4]) == 's')
1373
#define IS_XMLNS_CHARS(chars) \
1374
(IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1376
#define STARTS_WITH_XML(chars,length) \
1377
(length >= 3 && IS_XML_CHARS(chars))
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/";
1383
ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
1384
JSBool isAttributeName)
1386
JSString *str, *uri, *prefix, *localName;
1387
size_t length, offset;
1388
const jschar *start, *limit, *colon;
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 != '*');
1399
uri = cx->runtime->emptyString;
1400
limit = start + length;
1401
colon = js_strchr_limit(start, ':', limit);
1403
offset = PTRDIFF(colon, start, jschar);
1404
prefix = js_NewDependentString(cx, str, 0, offset, 0);
1408
if (STARTS_WITH_XML(start, offset)) {
1410
uri = JS_InternString(cx, xml_namespace_str);
1413
} else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1414
uri = JS_InternString(cx, xmlns_namespace_str);
1422
n = inScopeNSes->length;
1425
ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
1426
if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) {
1434
js_ReportCompileErrorNumber(cx, pn,
1435
JSREPORT_PN | JSREPORT_ERROR,
1436
JSMSG_BAD_XML_NAMESPACE,
1437
js_ValueToPrintableString(cx,
1438
STRING_TO_JSVAL(prefix)));
1442
localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0);
1446
if (isAttributeName) {
1448
* An unprefixed attribute is not in any namespace, so set prefix
1449
* as well as uri to the empty string.
1454
* Loop from back to front looking for the closest declared default
1457
n = inScopeNSes->length;
1460
ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
1461
if (!ns->prefix || IS_EMPTY(ns->prefix)) {
1466
prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
1471
return js_NewXMLQName(cx, uri, prefix, localName);
1475
ChompXMLWhitespace(JSContext *cx, JSString *str)
1477
size_t length, newlength, offset;
1478
const jschar *cp, *start, *end;
1481
length = JSSTRING_LENGTH(str);
1482
for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
1484
if (!JS_ISXMLSPACE(c))
1489
if (!JS_ISXMLSPACE(c))
1493
newlength = PTRDIFF(end, cp, jschar);
1494
if (newlength == length)
1496
offset = PTRDIFF(cp, start, jschar);
1497
return js_NewDependentString(cx, str, offset, newlength, 0);
1501
ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
1504
JSXML *xml, *kid, *attr, *attrj;
1506
uint32 length, n, i, j;
1507
JSParseNode *pn2, *pn3, *head, **pnp;
1509
JSXMLQName *qn, *attrjqn;
1510
JSXMLClass xml_class;
1513
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1514
js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
1515
JSMSG_OVER_RECURSED);
1519
#define PN2X_SKIP_CHILD ((JSXML *) 1)
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.
1527
if (!js_EnterLocalRootScope(cx))
1529
switch (pn->pn_type) {
1531
length = inScopeNSes->length;
1533
xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
1537
flags &= ~XSF_PRECOMPILED_ROOT;
1541
if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
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);
1552
if ((flags & XSF_IGNORE_WHITESPACE) &&
1553
n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1558
kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
1559
if (kid == PN2X_SKIP_CHILD) {
1567
/* Store kid in xml right away, to protect it from GC. */
1568
XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
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);
1578
kid->xml_value = str;
1583
if (n < pn->pn_count - 2)
1584
XMLArrayTrim(&xml->xml_kids);
1585
XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1589
xml = js_NewXML(cx, JSXML_CLASS_LIST);
1594
if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1598
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
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).
1604
if (pn2->pn_type == TOK_XMLSPACE) {
1609
kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
1610
if (kid == PN2X_SKIP_CHILD) {
1618
XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1622
if (n < pn->pn_count)
1623
XMLArrayTrim(&xml->xml_kids);
1628
length = inScopeNSes->length;
1630
JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1631
if (pn2->pn_arity == PN_LIST)
1634
xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
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;
1643
while ((pn2 = *pnp) != NULL) {
1645
const jschar *chars;
1647
if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
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)));
1662
str = ATOM_TO_STRING(pn2->pn_atom);
1665
if (pn2->pn_type != TOK_XMLATTR)
1668
length = JSSTRING_LENGTH(str);
1669
chars = JSSTRING_CHARS(str);
1671
IS_XMLNS_CHARS(chars) &&
1672
(length == 5 || chars[5] == ':')) {
1673
JSString *uri, *prefix;
1675
uri = ATOM_TO_STRING(pn2->pn_atom);
1677
/* 10.3.2.1. Step 6(h)(i)(1)(a). */
1678
prefix = cx->runtime->emptyString;
1680
prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0);
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.
1691
ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE);
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.
1702
if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1703
if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1704
!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1711
*pnp = pn2->pn_next;
1712
/* XXXbe recycle pn2 */
1716
pnp = &pn2->pn_next;
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).
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);
1732
if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1735
XMLArrayTrim(&xml->xml_namespaces);
1737
/* Second pass: process tag name and attributes, using namespaces. */
1739
qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE);
1744
JS_ASSERT((n & 1) == 0);
1746
if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
1749
for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1750
qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE);
1752
xml->xml_attrs.length = i;
1757
* Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1758
* this time checking local name and namespace URI.
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)));
1776
JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1778
attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1782
XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1785
attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1788
/* Point tag closes its own namespace scope. */
1789
if (pn->pn_type == TOK_XMLPTAGC)
1790
XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1796
case TOK_XMLCOMMENT:
1798
str = ATOM_TO_STRING(pn->pn_atom);
1800
if (pn->pn_type == TOK_XMLCOMMENT) {
1801
if (flags & XSF_IGNORE_COMMENTS)
1803
xml_class = JSXML_CLASS_COMMENT;
1804
} else if (pn->pn_type == TOK_XMLPI) {
1806
js_ReportCompileErrorNumber(cx, pn,
1807
JSREPORT_PN | JSREPORT_ERROR,
1809
js_ValueToPrintableString(cx,
1810
STRING_TO_JSVAL(str)));
1814
if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1817
qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE);
1822
? ATOM_TO_STRING(pn->pn_atom2)
1823
: cx->runtime->emptyString;
1824
xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1826
/* CDATA section content, or element text. */
1827
xml_class = JSXML_CLASS_TEXT;
1830
xml = js_NewXML(cx, xml_class);
1834
if (pn->pn_type == TOK_XMLSPACE)
1835
xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1836
xml->xml_value = str;
1843
js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
1844
if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
1849
js_LeaveLocalRootScope(cx);
1850
return PN2X_SKIP_CHILD;
1852
#undef PN2X_SKIP_CHILD
1855
js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
1856
JSMSG_BAD_XML_MARKUP);
1858
js_LeaveLocalRootScope(cx);
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.
1867
GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1871
if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
1873
if (!VALUE_IS_FUNCTION(cx, v)) {
1877
return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1881
FillSettingsCache(JSContext *cx)
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))
1894
cx->xmlSettingFlags |= JS_BIT(i);
1896
cx->xmlSettingFlags &= ~JS_BIT(i);
1899
cx->xmlSettingFlags |= XSF_CACHE_VALID;
1904
GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1908
if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
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;
1922
GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1926
return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip);
1930
GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1934
/* Just get the first flag to validate the setting flags cache. */
1935
if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
1937
*flagsp = cx->xmlSettingFlags;
1942
ParseXMLSource(JSContext *cx, JSString *src)
1946
size_t urilen, srclen, length, offset, dstlen;
1948
const jschar *srcp, *endp;
1959
static const char prefix[] = "<parent xmlns='";
1960
static const char middle[] = "'>";
1961
static const char suffix[] = "</parent>";
1963
#define constrlen(constr) (sizeof(constr) - 1)
1965
if (!js_GetDefaultXMLNamespace(cx, &nsval))
1967
ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
1969
urilen = JSSTRING_LENGTH(ns->uri);
1970
srclen = JSSTRING_LENGTH(src);
1971
length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1974
chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1979
js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1981
js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen);
1983
dstlen = length - offset + 1;
1984
js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1987
srcp = JSSTRING_CHARS(src);
1988
js_strncpy(chars + offset, srcp, srclen);
1990
dstlen = length - offset + 1;
1991
js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1993
chars [offset + dstlen] = 0;
1995
mark = JS_ARENA_MARK(&cx->tempPool);
1996
ts = js_NewBufferTokenStream(cx, chars, length);
1999
for (fp = cx->fp; fp && !fp->pc; fp = fp->down)
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++)
2009
ts->lineno = lineno;
2013
JS_KEEP_ATOMS(cx->runtime);
2014
pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE);
2016
if (pn && XMLArrayInit(cx, &nsarray, 1)) {
2017
if (GetXMLSettingFlags(cx, &flags))
2018
xml = ParseNodeToXML(cx, pn, &nsarray, flags);
2020
XMLArrayFinish(cx, &nsarray);
2022
JS_UNKEEP_ATOMS(cx->runtime);
2024
JS_ARENA_RELEASE(&cx->tempPool, mark);
2032
* Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
2034
* 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
2037
* for all x belonging to XML:
2038
* x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
2040
* must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
2041
* (in new sub-step 6(a), renumbering the others to (b) and (c)).
2043
* Same goes for 10.4.1 Step 7(a).
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.
2051
OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
2055
ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace);
2056
xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
2059
if (xml->xml_class == JSXML_CLASS_ELEMENT) {
2060
if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
2062
ns->declared = JS_FALSE;
2069
ToXML(JSContext *cx, jsval v)
2077
if (JSVAL_IS_PRIMITIVE(v)) {
2078
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
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)
2087
xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
2089
JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
2090
return js_GetXMLObject(cx, xml);
2096
clasp = OBJ_GET_CLASS(cx, obj);
2097
if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2101
if (clasp != &js_StringClass &&
2102
clasp != &js_NumberClass &&
2103
clasp != &js_BooleanClass) {
2108
str = js_ValueToString(cx, v);
2111
if (IS_EMPTY(str)) {
2113
#ifdef __GNUC__ /* suppress bogus gcc warnings */
2117
xml = ParseXMLSource(cx, str);
2120
length = JSXML_LENGTH(xml);
2124
obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2127
} else if (length == 1) {
2128
xml = OrphanXMLChild(cx, xml, 0);
2131
obj = js_GetXMLObject(cx, xml);
2135
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2141
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
2143
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2144
JSMSG_BAD_XML_CONVERSION,
2145
JS_GetStringBytes(str));
2151
Append(JSContext *cx, JSXML *list, JSXML *kid);
2154
ToXMLList(JSContext *cx, jsval v)
2156
JSObject *obj, *listobj;
2157
JSXML *xml, *list, *kid;
2162
if (JSVAL_IS_PRIMITIVE(v)) {
2163
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
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);
2173
list = (JSXML *) JS_GetPrivate(cx, listobj);
2174
if (!Append(cx, list, xml))
2181
clasp = OBJ_GET_CLASS(cx, obj);
2182
if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2186
if (clasp != &js_StringClass &&
2187
clasp != &js_NumberClass &&
2188
clasp != &js_BooleanClass) {
2193
str = js_ValueToString(cx, v);
2196
if (IS_EMPTY(str)) {
2200
if (!js_EnterLocalRootScope(cx))
2202
xml = ParseXMLSource(cx, str);
2204
js_LeaveLocalRootScope(cx);
2207
length = JSXML_LENGTH(xml);
2210
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
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)) {
2223
js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2227
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
2229
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2230
JSMSG_BAD_XMLLIST_CONVERSION,
2231
JS_GetStringBytes(str));
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.
2242
* These functions take ownership of sb->base, if sb is non-null, in all cases
2243
* of success or failure.
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)
2251
JSStringBuffer localSB;
2252
size_t length, length2, newlength;
2257
js_InitStringBuffer(sb);
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) +
2265
bp = base = (jschar *)
2266
JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2268
js_FinishStringBuffer(sb);
2272
bp += STRING_BUFFER_OFFSET(sb);
2273
js_strncpy(bp, prefix, prefixlength);
2275
js_strncpy(bp, JSSTRING_CHARS(str), length);
2278
*bp++ = (jschar) ' ';
2279
js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2282
js_strncpy(bp, suffix, suffixlength);
2283
bp[suffixlength] = 0;
2285
str = js_NewString(cx, base, newlength, 0);
2292
MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2294
static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2295
'C', 'D', 'A', 'T', 'A',
2297
static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2299
return MakeXMLSpecialString(cx, sb, str, NULL,
2300
cdata_prefix_ucNstr, 9,
2301
cdata_suffix_ucNstr, 3);
2305
MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2307
static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2308
static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2310
return MakeXMLSpecialString(cx, sb, str, NULL,
2311
comment_prefix_ucNstr, 4,
2312
comment_suffix_ucNstr, 3);
2316
MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2319
static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2320
static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2322
return MakeXMLSpecialString(cx, sb, name, value,
2323
pi_prefix_ucNstr, 2,
2324
pi_suffix_ucNstr, 2);
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.
2332
AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2334
js_AppendCString(sb, "=\"");
2335
valstr = js_EscapeAttributeValue(cx, valstr);
2338
sb->base = STRING_BUFFER_ERROR_BASE;
2341
js_AppendJSString(sb, valstr);
2342
js_AppendChar(sb, '"');
2346
* ECMA-357 10.2.1.1 EscapeElementValue helper method.
2348
* This function takes ownership of sb->base, if sb is non-null, in all cases
2349
* of success or failure.
2352
EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2354
size_t length, newlength;
2355
const jschar *cp, *start, *end;
2358
length = newlength = JSSTRING_LENGTH(str);
2359
for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
2361
if (c == '<' || c == '>')
2366
if (newlength < length) {
2367
JS_ReportOutOfMemory(cx);
2371
if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2372
JSStringBuffer localSB;
2375
js_InitStringBuffer(sb);
2377
if (!sb->grow(sb, newlength)) {
2378
JS_ReportOutOfMemory(cx);
2381
for (cp = start; cp < end; cp++) {
2384
js_AppendCString(sb, js_lt_entity_str);
2386
js_AppendCString(sb, js_gt_entity_str);
2388
js_AppendCString(sb, js_amp_entity_str);
2390
js_AppendChar(sb, c);
2392
JS_ASSERT(STRING_BUFFER_OK(sb));
2393
str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
2395
js_FinishStringBuffer(sb);
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.
2405
EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2407
size_t length, newlength;
2408
const jschar *cp, *start, *end;
2411
length = newlength = JSSTRING_LENGTH(str);
2412
for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
2418
else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2421
if (newlength < length) {
2422
JS_ReportOutOfMemory(cx);
2426
if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2427
JSStringBuffer localSB;
2430
js_InitStringBuffer(sb);
2432
if (!sb->grow(sb, newlength)) {
2433
JS_ReportOutOfMemory(cx);
2436
for (cp = start; cp < end; cp++) {
2439
js_AppendCString(sb, js_quot_entity_str);
2441
js_AppendCString(sb, js_lt_entity_str);
2443
js_AppendCString(sb, js_amp_entity_str);
2445
js_AppendCString(sb, "
");
2447
js_AppendCString(sb, "
");
2449
js_AppendCString(sb, "	");
2451
js_AppendChar(sb, c);
2453
JS_ASSERT(STRING_BUFFER_OK(sb));
2454
str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
2456
js_FinishStringBuffer(sb);
2461
/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2462
static JSXMLNamespace *
2463
GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes)
2465
JSXMLNamespace *match, *ns;
2472
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2473
JSMSG_BAD_XML_NAMESPACE,
2475
? js_ValueToPrintableString(cx,
2476
STRING_TO_JSVAL(qn->prefix))
2477
: js_type_strs[JSTYPE_VOID]);
2481
/* Look for a matching namespace in inScopeNSes, if provided. */
2484
for (i = 0, n = inScopeNSes->length; i < n; i++) {
2485
ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace);
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:
2495
* x = <t xmlns="http://foo.com"/>
2496
* print(x.toXMLString());
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):
2501
* 1. If the [local name] property of a is "xmlns"
2502
* a. Map ns.prefix to the empty string
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).
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.
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)))) {
2527
/* If we didn't match, make a new namespace from qn. */
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,
2535
match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
2541
GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2543
const jschar *cp, *start, *end;
2544
size_t length, newlength, offset;
2545
uint32 i, n, m, serial;
2551
JS_ASSERT(!IS_EMPTY(uri));
2554
* If there are no *declared* namespaces, skip all collision detection and
2555
* return a short prefix quickly; an example of such a situation:
2558
* var n = new Namespace("http://example.com/");
2559
* x.@n::att = "val";
2562
* This is necessary for various log10 uses below to be valid.
2564
if (decls->length == 0)
2565
return JS_NewStringCopyZ(cx, "a");
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".
2573
start = JSSTRING_CHARS(uri);
2574
cp = end = start + JSSTRING_LENGTH(uri);
2575
while (--cp > start) {
2576
if (*cp == '.' || *cp == '/' || *cp == ':') {
2578
length = PTRDIFF(end, cp, jschar);
2579
if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2584
length = PTRDIFF(end, cp, jschar);
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).
2595
if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2596
newlength = length + 2 + (size_t) log10(decls->length);
2598
JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2603
for (i = 0; i < newlength; i++)
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.
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))) {
2621
newlength = length + 2 + (size_t) log10(n);
2623
JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2626
js_strncpy(bp, cp, length);
2630
JS_ASSERT(serial <= n);
2631
dp = bp + length + 2 + (size_t) log10(serial);
2633
for (m = serial; m != 0; m /= 10)
2634
*--dp = (jschar)('0' + m % 10);
2636
JS_ASSERT(dp == bp + length);
2645
offset = PTRDIFF(cp, start, jschar);
2646
prefix = js_NewDependentString(cx, uri, offset, length, 0);
2648
prefix = js_NewString(cx, bp, newlength, 0);
2656
namespace_match(const void *a, const void *b)
2658
const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
2659
const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
2662
return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix);
2663
return js_EqualStrings(nsa->uri, nsb->uri);
2666
/* ECMA-357 10.2.1 and 10.2.2 */
2668
XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2671
JSBool pretty, indentKids;
2673
JSString *str, *prefix, *kidstr;
2674
JSXMLArrayCursor cursor;
2676
JSXMLArray empty, decls, ancdecls;
2677
JSXMLNamespace *ns, *ns2;
2678
uintN nextIndentLevel;
2681
if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2684
js_InitStringBuffer(&sb);
2686
js_RepeatChar(&sb, ' ', indentLevel);
2689
switch (xml->xml_class) {
2690
case JSXML_CLASS_TEXT:
2693
str = ChompXMLWhitespace(cx, xml->xml_value);
2697
str = xml->xml_value;
2699
return EscapeElementValue(cx, &sb, str);
2701
case JSXML_CLASS_ATTRIBUTE:
2703
return EscapeAttributeValue(cx, &sb, xml->xml_value);
2705
case JSXML_CLASS_COMMENT:
2707
return MakeXMLCommentString(cx, &sb, xml->xml_value);
2709
case JSXML_CLASS_PROCESSING_INSTRUCTION:
2711
return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value);
2713
case JSXML_CLASS_LIST:
2714
/* ECMA-357 10.2.2. */
2715
XMLArrayCursorInit(&cursor, &xml->xml_kids);
2717
while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2718
if (pretty && i != 0)
2719
js_AppendChar(&sb, '\n');
2721
kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
2725
js_AppendJSString(&sb, kidstr);
2728
XMLArrayCursorFinish(&cursor);
2733
if (!STRING_BUFFER_OK(&sb)) {
2734
JS_ReportOutOfMemory(cx);
2737
return cx->runtime->emptyString;
2740
str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
2743
js_FinishStringBuffer(&sb);
2749
/* After this point, control must flow through label out: to exit. */
2750
if (!js_EnterLocalRootScope(cx))
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 = ∅
2758
XMLArrayInit(cx, &decls, 0);
2759
ancdecls.capacity = 0;
2761
/* Clone in-scope namespaces not in ancestorNSes into decls. */
2762
XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
2763
while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
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))
2773
XMLArrayCursorFinish(&cursor);
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).
2782
if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
2784
for (i = 0, n = ancestorNSes->length; i < n; i++) {
2785
ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace);
2788
JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
2789
if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2792
for (i = 0, n = decls.length; i < n; i++) {
2793
ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace);
2796
JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
2797
if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
2801
/* Step 11, except we don't clone ns unless its prefix is undefined. */
2802
ns = GetNamespace(cx, xml->name, &ancdecls);
2806
/* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
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.
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.
2819
* This helps descendants inherit the namespace instead of redundantly
2820
* redeclaring it with generated prefixes in each descendant.
2822
if (!xml->name->prefix) {
2823
prefix = cx->runtime->emptyString;
2825
prefix = GeneratePrefix(cx, ns->uri, &ancdecls);
2829
ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE);
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.
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().
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);
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.
2859
if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
2860
!XMLARRAY_APPEND(cx, &decls, ns)) {
2865
/* Format the element or point-tag into sb. */
2866
js_AppendChar(&sb, '<');
2868
if (ns->prefix && !IS_EMPTY(ns->prefix)) {
2869
js_AppendJSString(&sb, ns->prefix);
2870
js_AppendChar(&sb, ':');
2872
js_AppendJSString(&sb, xml->name->localName);
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.
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);
2888
/* 17(b)(ii): NULL means *undefined* here. */
2890
prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
2894
/* Again, we avoid copying ns2 until we know it's prefix-less. */
2895
ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE);
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.
2906
if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
2907
!XMLARRAY_APPEND(cx, &decls, ns2)) {
2913
if (!IS_EMPTY(ns2->prefix)) {
2914
js_AppendJSString(&sb, ns2->prefix);
2915
js_AppendChar(&sb, ':');
2919
js_AppendJSString(&sb, attr->name->localName);
2922
AppendAttributeValue(cx, &sb, attr->xml_value);
2924
XMLArrayCursorFinish(&cursor);
2928
/* Step 17(c): append XML namespace declarations. */
2929
XMLArrayCursorInit(&cursor, &decls);
2930
while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
2931
JS_ASSERT(ns2->declared);
2933
js_AppendCString(&sb, " xmlns");
2935
/* 17(c)(ii): NULL means *undefined* here. */
2937
prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
2940
ns2->prefix = prefix;
2944
if (!IS_EMPTY(ns2->prefix)) {
2945
js_AppendChar(&sb, ':');
2946
js_AppendJSString(&sb, ns2->prefix);
2950
AppendAttributeValue(cx, &sb, ns2->uri);
2952
XMLArrayCursorFinish(&cursor);
2956
/* Step 18: handle point tags. */
2957
n = xml->xml_kids.length;
2959
js_AppendCString(&sb, "/>");
2961
/* Steps 19 through 25: handle element content, and open the end-tag. */
2962
js_AppendChar(&sb, '>');
2963
indentKids = n > 1 ||
2965
(kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2966
kid->xml_class != JSXML_CLASS_TEXT);
2968
if (pretty && indentKids) {
2969
if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2971
nextIndentLevel = indentLevel + i;
2973
nextIndentLevel = 0;
2976
XMLArrayCursorInit(&cursor, &xml->xml_kids);
2977
while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
2978
if (pretty && indentKids)
2979
js_AppendChar(&sb, '\n');
2981
kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
2985
js_AppendJSString(&sb, kidstr);
2987
XMLArrayCursorFinish(&cursor);
2991
if (pretty && indentKids) {
2992
js_AppendChar(&sb, '\n');
2993
js_RepeatChar(&sb, ' ', indentLevel);
2995
js_AppendCString(&sb, "</");
2998
if (ns->prefix && !IS_EMPTY(ns->prefix)) {
2999
js_AppendJSString(&sb, ns->prefix);
3000
js_AppendChar(&sb, ':');
3004
js_AppendJSString(&sb, xml->name->localName);
3005
js_AppendChar(&sb, '>');
3008
if (!STRING_BUFFER_OK(&sb)) {
3009
JS_ReportOutOfMemory(cx);
3013
str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
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);
3026
ToXMLString(JSContext *cx, jsval v)
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)
3041
if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
3042
return js_ValueToString(cx, v);
3044
if (JSVAL_IS_STRING(v))
3045
return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
3047
obj = JSVAL_TO_OBJECT(v);
3048
if (!OBJECT_IS_XML(cx, obj)) {
3049
if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
3051
str = js_ValueToString(cx, v);
3054
return EscapeElementValue(cx, NULL, str);
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);
3063
ToAttributeName(JSContext *cx, jsval v)
3065
JSString *name, *uri, *prefix;
3069
JSTempValueRooter tvr;
3071
if (JSVAL_IS_STRING(v)) {
3072
name = JSVAL_TO_STRING(v);
3073
uri = prefix = cx->runtime->emptyString;
3075
if (JSVAL_IS_PRIMITIVE(v)) {
3076
name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
3078
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3079
JSMSG_BAD_XML_ATTR_NAME,
3080
JS_GetStringBytes(name));
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);
3090
if (clasp == &js_QNameClass.base) {
3091
qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
3093
prefix = qn->prefix;
3094
name = qn->localName;
3096
if (clasp == &js_AnyNameClass) {
3097
name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3099
name = js_ValueToString(cx, v);
3103
uri = prefix = cx->runtime->emptyString;
3107
qn = js_NewXMLQName(cx, uri, prefix, name);
3111
JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr);
3112
obj = js_GetAttributeNameObject(cx, qn);
3113
JS_POP_TEMP_ROOT(cx, &tvr);
3120
ToXMLName(JSContext *cx, jsval v, jsid *funidp)
3129
if (JSVAL_IS_STRING(v)) {
3130
name = JSVAL_TO_STRING(v);
3132
if (JSVAL_IS_PRIMITIVE(v)) {
3133
name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
3139
obj = JSVAL_TO_OBJECT(v);
3140
clasp = OBJ_GET_CLASS(cx, obj);
3141
if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
3143
if (clasp == &js_AnyNameClass) {
3144
name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
3147
name = js_ValueToString(cx, v);
3153
* ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3155
* 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3157
* First, _P_ should be _s_, to refer to the given string.
3159
* Second, why does ToXMLName applied to the string type throw TypeError
3160
* only for numeric literals without any leading or trailing whitespace?
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.
3165
if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
3168
if (*JSSTRING_CHARS(name) == '@') {
3169
name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0);
3173
return ToAttributeName(cx, STRING_TO_JSVAL(name));
3177
v = STRING_TO_JSVAL(name);
3178
obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
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))
3196
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3198
js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
3202
/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3204
AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
3206
JSXMLNamespace *match, *ns2;
3209
if (xml->xml_class != JSXML_CLASS_ELEMENT)
3212
/* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
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)) {
3222
if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
3225
if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri))
3228
#ifdef __GNUC__ /* suppress bogus gcc warnings */
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)) {
3240
if (match && !js_EqualStrings(match->uri, ns->uri)) {
3241
ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
3243
JS_ASSERT(ns2 == match);
3244
match->prefix = NULL;
3245
if (!AddInScopeNamespace(cx, xml, match))
3248
if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
3252
/* OPTION: enforce that descendants have superset namespaces. */
3256
/* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3258
Append(JSContext *cx, JSXML *list, JSXML *xml)
3263
JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
3264
i = list->xml_kids.length;
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);
3271
if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
3273
for (j = 0; j < n; j++) {
3274
kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
3276
XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3281
list->xml_target = xml->parent;
3282
if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3283
list->xml_targetprop = NULL;
3285
list->xml_targetprop = xml->name;
3286
if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3291
/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3293
DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
3296
DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
3301
/* Our caller may not be protecting newborns with a local root scope. */
3302
if (!js_EnterLocalRootScope(cx))
3304
copy = DeepCopyInLRS(cx, xml, flags);
3307
/* Caller provided the object for this copy, hook 'em up. */
3308
ok = JS_SetPrivate(cx, obj, copy);
3312
ok = js_GetXMLObject(cx, copy) != NULL;
3317
js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
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.
3327
DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
3331
JSXMLArrayCursor cursor;
3336
JS_ASSERT(cx->localRootStack);
3339
if (!XMLArraySetCapacity(cx, to, n))
3342
XMLArrayCursorInit(&cursor, from);
3345
while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
3346
if ((flags & XSF_IGNORE_COMMENTS) &&
3347
kid->xml_class == JSXML_CLASS_COMMENT) {
3350
if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3351
kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3354
if ((flags & XSF_IGNORE_WHITESPACE) &&
3355
(kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3358
kid2 = DeepCopyInLRS(cx, kid, flags);
3365
if ((flags & XSF_IGNORE_WHITESPACE) &&
3366
n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3367
str = ChompXMLWhitespace(cx, kid2->xml_value);
3373
kid2->xml_value = str;
3376
XMLARRAY_SET_MEMBER(to, j, kid2);
3378
if (parent->xml_class != JSXML_CLASS_LIST)
3379
kid2->parent = parent;
3381
XMLArrayCursorFinish(&cursor);
3391
DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
3397
JSXMLNamespace *ns, *ns2;
3399
/* Our caller must be protecting newborn objects. */
3400
JS_ASSERT(cx->localRootStack);
3402
copy = js_NewXML(cx, xml->xml_class);
3407
qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
3414
copy->xml_flags = xml->xml_flags;
3416
if (JSXML_HAS_VALUE(xml)) {
3417
copy->xml_value = xml->xml_value;
3420
ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags);
3424
if (xml->xml_class == JSXML_CLASS_LIST) {
3425
copy->xml_target = xml->xml_target;
3426
copy->xml_targetprop = xml->xml_targetprop;
3428
n = xml->xml_namespaces.length;
3429
ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n);
3432
for (i = 0; i < n; i++) {
3433
ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
3436
ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared);
3438
copy->xml_namespaces.length = i;
3442
XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2);
3445
ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy,
3459
ReportBadXMLName(JSContext *cx, jsval id)
3463
name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL);
3465
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3467
JS_GetStringBytes(name));
3471
/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3473
DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp)
3478
if (!js_IdIsIndex(id, &index)) {
3479
ReportBadXMLName(cx, id);
3483
if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3484
kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3487
XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3494
typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml);
3497
MatchAttrName(JSXMLQName *nameqn, JSXML *attr)
3499
JSXMLQName *attrqn = attr->name;
3501
return (IS_STAR(nameqn->localName) ||
3502
js_EqualStrings(attrqn->localName, nameqn->localName)) &&
3504
js_EqualStrings(attrqn->uri, nameqn->uri));
3508
MatchElemName(JSXMLQName *nameqn, JSXML *elem)
3510
return (IS_STAR(nameqn->localName) ||
3511
(elem->xml_class == JSXML_CLASS_ELEMENT &&
3512
js_EqualStrings(elem->name->localName, nameqn->localName))) &&
3514
(elem->xml_class == JSXML_CLASS_ELEMENT &&
3515
js_EqualStrings(elem->name->uri, nameqn->uri)));
3518
/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3520
DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list)
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))
3536
for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3537
kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3540
if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass &&
3541
MatchElemName(nameqn, kid)) {
3542
if (!Append(cx, list, kid))
3545
if (!DescendantsHelper(cx, kid, nameqn, list))
3552
Descendants(JSContext *cx, JSXML *xml, jsval id)
3561
nameqn = ToXMLName(cx, id, &funid);
3565
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3568
list = (JSXML *) JS_GetPrivate(cx, listobj);
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.
3578
list->name = nameqn;
3579
if (!js_EnterLocalRootScope(cx))
3581
if (xml->xml_class == JSXML_CLASS_LIST) {
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);
3592
ok = DescendantsHelper(cx, xml, nameqn, list);
3594
js_LeaveLocalRootScopeWithResult(cx, (jsval) list);
3602
xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
3604
/* Recursive (JSXML *) parameterized version of Equals. */
3606
XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3608
JSXMLQName *qn, *vqn;
3610
JSXMLArrayCursor cursor, vcursor;
3611
JSXML *kid, *vkid, *attr, *vattr;
3613
JSObject *xobj, *vobj;
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);
3622
if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3623
vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3635
js_EqualStrings(qn->localName, vqn->localName) &&
3636
js_EqualStrings(qn->uri, vqn->uri);
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) {
3648
XMLArrayCursorInit(&cursor, &xml->xml_kids);
3649
XMLArrayCursorInit(&vcursor, &vxml->xml_kids);
3651
kid = (JSXML *) XMLArrayCursorNext(&cursor);
3652
vkid = (JSXML *) XMLArrayCursorNext(&vcursor);
3653
if (!kid || !vkid) {
3654
*bp = !kid && !vkid;
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);
3665
XMLArrayCursorFinish(&vcursor);
3666
XMLArrayCursorFinish(&cursor);
3670
if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3671
n = xml->xml_attrs.length;
3672
if (n != vxml->xml_attrs.length)
3674
for (i = 0; *bp && i < n; i++) {
3675
attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3678
j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3679
if (j == XML_NOT_FOUND) {
3683
vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3686
*bp = js_EqualStrings(attr->xml_value, vattr->xml_value);
3694
/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3696
Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3701
if (JSVAL_IS_PRIMITIVE(v)) {
3703
if (xml->xml_class == JSXML_CLASS_LIST) {
3704
if (xml->xml_kids.length == 1) {
3705
vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3708
vobj = js_GetXMLObject(cx, vxml);
3711
return js_XMLObjectOps.equality(cx, vobj, v, bp);
3713
if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3717
vobj = JSVAL_TO_OBJECT(v);
3718
if (!OBJECT_IS_XML(cx, vobj)) {
3721
vxml = (JSXML *) JS_GetPrivate(cx, vobj);
3722
if (!XMLEquals(cx, xml, vxml, bp))
3730
CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3732
JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3736
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3737
JSMSG_CYCLIC_VALUE, js_XML_str);
3740
} while ((xml = xml->parent) != NULL);
3745
/* ECMA-357 9.1.1.11 XML [[Insert]]. */
3747
Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
3754
if (!JSXML_HAS_KIDS(xml))
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;
3767
for (j = 0; j < n; j++) {
3768
kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3771
if (!CheckCycle(cx, xml, kid))
3774
} else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3775
/* OPTION: enforce that descendants have superset namespaces. */
3776
if (!CheckCycle(cx, xml, vxml))
3782
str = js_ValueToString(cx, v);
3786
vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3789
vxml->xml_value = str;
3792
if (i > xml->xml_kids.length)
3793
i = xml->xml_kids.length;
3795
if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3798
if (vxml->xml_class == JSXML_CLASS_LIST) {
3799
for (j = 0; j < n; j++) {
3800
kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3804
XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3806
/* OPTION: enforce that descendants have superset namespaces. */
3810
XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3816
IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp)
3820
if (index <= JSVAL_INT_MAX) {
3821
*idvp = INT_TO_JSVAL(index);
3823
str = js_NumberToString(cx, (jsdouble) index);
3826
*idvp = STRING_TO_JSVAL(str);
3831
/* ECMA-357 9.1.1.12 XML [[Replace]]. */
3833
Replace(JSContext *cx, JSXML *xml, jsval id, jsval v)
3841
if (!JSXML_HAS_KIDS(xml))
3844
if (!js_IdIsIndex(id, &i)) {
3845
ReportBadXMLName(cx, id);
3851
* [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3852
* It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3854
n = xml->xml_kids.length;
3856
if (!IndexToIdVal(cx, n, &id))
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);
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))
3873
case JSXML_CLASS_COMMENT:
3874
case JSXML_CLASS_PROCESSING_INSTRUCTION:
3875
case JSXML_CLASS_TEXT:
3878
case JSXML_CLASS_LIST:
3879
if (i < n && !DeleteByIndex(cx, xml, id, &junk))
3881
if (!Insert(cx, xml, i, v))
3886
str = js_ValueToString(cx, v);
3890
vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3893
vxml->xml_value = str;
3898
kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3902
if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3910
/* Forward declared -- its implementation uses other statics that call it. */
3912
ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
3914
/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */
3916
DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3918
JSXML *xml, *kid, *parent;
3921
uint32 length, index, kidIndex, deleteCount;
3924
JSObject *nameobj, *kidobj;
3925
JSXMLNameMatcher matcher;
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;
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);
3943
parent = kid->parent;
3945
JS_ASSERT(parent != xml);
3946
JS_ASSERT(JSXML_HAS_KIDS(parent));
3948
if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
3950
nameobj = js_GetAttributeNameObject(cx, nameqn);
3951
if (!nameobj || !js_GetXMLObject(cx, parent))
3954
id = OBJECT_TO_JSVAL(nameobj);
3955
if (!DeleteProperty(cx, parent->object, id, vp))
3958
kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
3960
JS_ASSERT(kidIndex != XML_NOT_FOUND);
3961
if (!IndexToIdVal(cx, kidIndex, &id))
3963
if (!DeleteByIndex(cx, parent, id, vp))
3968
XMLArrayDelete(cx, array, index, JS_TRUE);
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))
3980
/* ECMA-357 9.1.1.3. */
3982
/* See NOTE in spec: this variation is reserved for future use. */
3983
ReportBadXMLName(cx, id);
3987
nameqn = ToXMLName(cx, id, &funid);
3992
nameobj = nameqn->object;
3994
if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
3995
if (xml->xml_class != JSXML_CLASS_ELEMENT)
3997
array = &xml->xml_attrs;
3998
length = array->length;
3999
matcher = MatchAttrName;
4001
matcher = MatchElemName;
4005
for (index = 0; index < length; index++) {
4006
kid = XMLARRAY_MEMBER(array, index, JSXML);
4007
if (kid && matcher(nameqn, kid)) {
4009
XMLArrayDelete(cx, array, index, JS_FALSE);
4011
} else if (deleteCount != 0) {
4012
XMLARRAY_SET_MEMBER(array,
4013
index - deleteCount,
4014
array->vector[index]);
4017
array->length -= deleteCount;
4027
SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
4029
JSXMLArray *nsarray;
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))
4047
GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn,
4048
JSBool attributes, JSXML *list)
4051
JSXMLNameMatcher matcher;
4052
JSXMLArrayCursor cursor;
4056
if (!JSXML_HAS_KIDS(xml))
4060
array = &xml->xml_attrs;
4061
matcher = MatchAttrName;
4063
array = &xml->xml_kids;
4064
matcher = MatchElemName;
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);
4075
ok = Append(cx, list, kid);
4083
XMLArrayCursorFinish(&cursor);
4087
/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
4089
GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4091
JSXML *xml, *list, *kid;
4093
JSObject *kidobj, *listobj;
4097
JSTempValueRooter tvr;
4099
JSXMLArrayCursor cursor;
4101
xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
4105
if (js_IdIsIndex(id, &index)) {
4106
if (xml->xml_class != JSXML_CLASS_LIST) {
4107
*vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
4110
* ECMA-357 9.2.1.1 starts here.
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.
4116
if (index < xml->xml_kids.length) {
4117
kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
4122
kidobj = js_GetXMLObject(cx, kid);
4126
*vp = OBJECT_TO_JSVAL(kidobj);
4135
* ECMA-357 9.2.1.1/9.1.1.1 qname case.
4137
nameqn = ToXMLName(cx, id, &funid);
4141
return js_GetXMLFunction(cx, obj, funid, vp);
4143
roots[0] = OBJECT_TO_JSVAL(nameqn->object);
4144
JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr);
4146
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
4148
roots[1] = OBJECT_TO_JSVAL(listobj);
4151
list = (JSXML *) JS_GetPrivate(cx, listobj);
4152
attributes = (OBJ_GET_CLASS(cx, nameqn->object) ==
4153
&js_AttributeNameClass);
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)) {
4164
XMLArrayCursorFinish(&cursor);
4166
if (!GetNamedProperty(cx, xml, nameqn, attributes, list))
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.
4178
list->xml_target = xml;
4179
list->xml_targetprop = nameqn;
4180
*vp = OBJECT_TO_JSVAL(listobj);
4183
JS_POP_TEMP_ROOT(cx, &tvr);
4184
return listobj != NULL;
4188
CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
4190
JS_ASSERT(xml->object != obj);
4192
xml = DeepCopy(cx, xml, obj, 0);
4196
JS_ASSERT(xml->object == obj);
4200
#define CHECK_COPY_ON_WRITE(cx,xml,obj) \
4201
(xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
4204
KidToString(JSContext *cx, JSXML *xml, uint32 index)
4209
kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
4211
return cx->runtime->emptyString;
4212
kidobj = js_GetXMLObject(cx, kid);
4215
return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj));
4218
/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
4220
PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4222
JSBool ok, primitiveAssign;
4223
enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
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;
4232
JSString *left, *right, *space;
4235
xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
4239
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
4243
/* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
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);
4251
/* Control flow after here must exit via label out. */
4252
ok = js_EnterLocalRootScope(cx);
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);
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. */
4267
if (xml->xml_target) {
4268
ok = ResolveValue(cx, xml->xml_target, &rxml);
4273
JS_ASSERT(rxml->object);
4279
if (index >= xml->xml_kids.length) {
4282
if (rxml->xml_class == JSXML_CLASS_LIST) {
4283
if (rxml->xml_kids.length != 1)
4285
rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
4288
ok = js_GetXMLObject(cx, rxml) != NULL;
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:
4299
* var MYXML = new XML();
4300
* MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
4302
* (testcase from Werner Sharp <wsharp@macromedia.com>).
4304
* To match insertChildAfter, insertChildBefore,
4305
* prependChild, and setChildren, we should silently
4306
* do nothing in this case.
4308
if (!JSXML_HAS_KIDS(rxml))
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);
4320
nameobj = js_GetXMLQNameObject(cx, targetprop);
4323
if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
4326
* Note that rxml can't be null here, because target
4327
* and targetprop are non-null.
4329
ok = GetProperty(cx, rxml->object, id, &attrval);
4332
if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
4334
attrobj = JSVAL_TO_OBJECT(attrval);
4335
attr = (JSXML *) JS_GetPrivate(cx, attrobj);
4336
if (JSXML_LENGTH(attr) != 0)
4339
kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4342
kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
4347
/* An important bit of 2(c)(ii). */
4348
kid->name = targetprop;
4351
/* Final important bit of 2(c)(ii). */
4355
i = xml->xml_kids.length;
4356
if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
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.
4364
JS_ASSERT(JSXML_HAS_KIDS(rxml));
4365
n = rxml->xml_kids.length;
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]) {
4376
kidobj = js_GetXMLObject(cx, kid);
4379
ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4386
* Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4387
* typo for [[TargetProperty]].
4390
kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4391
? vxml->xml_targetprop
4397
ok = Append(cx, xml, kid);
4404
vxml->xml_class == JSXML_CLASS_TEXT ||
4405
vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4406
ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4409
roots[VAL_ROOT] = *vp;
4413
kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4416
parent = kid->parent;
4417
if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4418
nameobj = js_GetAttributeNameObject(cx, kid->name);
4421
id = OBJECT_TO_JSVAL(nameobj);
4425
parentobj = js_GetXMLObject(cx, parent);
4428
ok = PutProperty(cx, parentobj, id, vp);
4433
ok = GetProperty(cx, parentobj, id, vp);
4436
attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
4439
xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
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);
4449
copy = (JSXML *) JS_GetPrivate(cx, copyobj);
4450
n = vxml->xml_kids.length;
4451
ok = XMLArraySetCapacity(cx, ©->xml_kids, n);
4454
for (k = 0; k < n; k++) {
4455
kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML);
4456
XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2);
4459
JS_ASSERT(parent != xml);
4461
q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4462
JS_ASSERT(q != XML_NOT_FOUND);
4464
ok = IndexToIdVal(cx, q, &id);
4467
ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj));
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(©->xml_kids, j, JSXML)
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.
4487
XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4489
ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4493
for (j = 0; j < n; j++)
4494
xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4499
else if (vxml || JSXML_HAS_VALUE(kid)) {
4501
q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
4502
JS_ASSERT(q != XML_NOT_FOUND);
4504
ok = IndexToIdVal(cx, q, &id);
4507
ok = Replace(cx, parent, id, *vp);
4511
vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4514
roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
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.
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.
4528
JS_ASSERT(JSVAL_IS_STRING(*vp));
4529
vobj = ToXML(cx, *vp);
4532
roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4533
vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4535
XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4540
kidobj = js_GetXMLObject(cx, kid);
4543
id = ATOM_KEY(cx->runtime->atomState.starAtom);
4544
ok = PutProperty(cx, kidobj, id, vp);
4551
* Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4552
* or an r with r.[[Length]] != 1, throw TypeError.
4554
n = JSXML_LENGTH(xml);
4558
ok = ResolveValue(cx, xml, &rxml);
4561
if (!rxml || JSXML_LENGTH(rxml) != 1)
4563
ok = Append(cx, xml, rxml);
4567
JS_ASSERT(JSXML_LENGTH(xml) == 1);
4568
kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4571
kidobj = js_GetXMLObject(cx, kid);
4574
ok = PutProperty(cx, kidobj, id, vp);
4581
* Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4582
* effort in ToString or [[DeepCopy]].
4584
if (js_IdIsIndex(id, &index)) {
4585
/* See NOTE in spec: this variation is reserved for future use. */
4586
ReportBadXMLName(cx, id);
4590
nameqn = ToXMLName(cx, id, &funid);
4594
ok = js_SetProperty(cx, obj, funid, vp);
4597
nameobj = nameqn->object;
4599
if (JSXML_HAS_VALUE(xml))
4603
vxml->xml_class == JSXML_CLASS_TEXT ||
4604
vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4605
ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4609
rxml = DeepCopyInLRS(cx, vxml, 0);
4610
if (!rxml || !js_GetXMLObject(cx, rxml))
4613
*vp = OBJECT_TO_JSVAL(vxml->object);
4615
roots[VAL_ROOT] = *vp;
4619
* Erratum: why is this done here, so early? use is way later....
4621
ok = js_GetDefaultXMLNamespace(cx, &nsval);
4625
if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
4627
if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4631
if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4632
n = vxml->xml_kids.length;
4634
*vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4636
left = KidToString(cx, vxml, 0);
4640
space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom);
4641
for (i = 1; i < n; i++) {
4642
left = js_ConcatStrings(cx, left, space);
4645
right = KidToString(cx, vxml, i);
4648
left = js_ConcatStrings(cx, left, right);
4653
roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4656
ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4659
roots[VAL_ROOT] = *vp;
4664
for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4665
attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4668
attrqn = attr->name;
4669
if (js_EqualStrings(attrqn->localName, nameqn->localName) &&
4671
js_EqualStrings(attrqn->uri, nameqn->uri))) {
4675
nameobj = js_GetAttributeNameObject(cx, attrqn);
4679
id = OBJECT_TO_JSVAL(nameobj);
4680
ok = DeleteProperty(cx, obj, id, &junk);
4693
left = right = cx->runtime->emptyString;
4696
right = nameqn->prefix;
4698
nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
4703
attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4707
attr->name = nameqn;
4710
ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4715
ns = GetNamespace(cx, nameqn, NULL);
4718
ok = AddInScopeNamespace(cx, xml, ns);
4724
attr->xml_value = JSVAL_TO_STRING(*vp);
4729
if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4730
!IS_STAR(nameqn->localName)) {
4736
primitiveAssign = !vxml && !IS_STAR(nameqn->localName);
4739
k = n = xml->xml_kids.length;
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);
4750
ok = IndexToIdVal(cx, k, &id);
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.
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.
4774
JS_ASSERT(kid2->parent == xml || !kid2->parent);
4780
if (JSVAL_IS_VOID(id)) {
4782
ok = IndexToIdVal(cx, n, &id);
4787
if (primitiveAssign) {
4789
ns = (JSXMLNamespace *)
4790
JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
4795
right = nameqn->prefix;
4797
nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
4802
vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4805
vxml = (JSXML *) JS_GetPrivate(cx, vobj);
4807
vxml->name = nameqn;
4810
ns = GetNamespace(cx, nameqn, NULL);
4813
ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj));
4816
ok = AddInScopeNamespace(cx, vxml, ns);
4823
if (primitiveAssign) {
4824
JSXMLArrayCursor cursor;
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);
4836
/* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
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);
4845
XMLArrayCursorFinish(&cursor);
4848
ok = Replace(cx, xml, id, *vp);
4853
JS_POP_TEMP_ROOT(cx, &tvr);
4854
js_LeaveLocalRootScope(cx);
4858
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4859
JSMSG_BAD_XMLLIST_PUT,
4860
js_ValueToPrintableString(cx, id));
4866
/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4868
ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4870
JSXML *target, *base;
4871
JSXMLQName *targetprop;
4872
JSObject *targetpropobj;
4875
/* Our caller must be protecting newborn objects. */
4876
JS_ASSERT(cx->localRootStack);
4878
if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4879
if (!js_GetXMLObject(cx, list))
4885
target = list->xml_target;
4886
targetprop = list->xml_targetprop;
4887
if (!target || !targetprop || IS_STAR(targetprop->localName)) {
4892
targetpropobj = js_GetXMLQNameObject(cx, targetprop);
4895
if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) {
4900
if (!ResolveValue(cx, target, &base))
4906
if (!js_GetXMLObject(cx, base))
4909
id = OBJECT_TO_JSVAL(targetpropobj);
4910
if (!GetProperty(cx, base->object, id, &tv))
4912
target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
4914
if (JSXML_LENGTH(target) == 0) {
4915
if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4919
tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4920
if (!PutProperty(cx, base->object, id, &tv))
4922
if (!GetProperty(cx, base->object, id, &tv))
4924
target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
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.
4937
* DROP_PROPERTY helps HasProperty callers drop function properties without
4938
* trying to drop the magic FOUND_XML_PROPERTY cookie.
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) \
4945
/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4947
HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp,
4951
JSXMLArrayCursor cursor;
4956
JSXMLNameMatcher matcher;
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)) {
4967
*propp = FOUND_XML_PROPERTY;
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))
4981
XMLArrayCursorFinish(&cursor);
4983
return *propp != NULL;
4985
if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) {
4987
*propp = FOUND_XML_PROPERTY;
4991
qn = ToXMLName(cx, id, &funid);
4995
return js_LookupProperty(cx, obj, funid, objp, propp);
4997
if (xml->xml_class != JSXML_CLASS_ELEMENT)
5000
if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) {
5001
array = &xml->xml_attrs;
5002
matcher = MatchAttrName;
5004
array = &xml->xml_kids;
5005
matcher = MatchElemName;
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;
5020
xml_finalize(JSContext *cx, JSObject *obj)
5024
xml = (JSXML *) JS_GetPrivate(cx, obj);
5027
if (xml->object == obj)
5029
UNMETER(xml_stats.livexmlobj);
5033
xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len)
5038
for (i = 0; i < len; i++) {
5041
#ifdef GC_MARK_DEBUG
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;
5049
JS_snprintf(buf, sizeof buf, "%s::%s",
5050
qn->uri ? JS_GetStringBytes(qn->uri) : "*",
5051
JS_GetStringBytes(qn->localName));
5053
JSString *str = elt->xml_value;
5054
size_t srclen = JSSTRING_LENGTH(str);
5055
size_t dstlen = sizeof buf;
5057
if (srclen >= sizeof buf / 6)
5058
srclen = sizeof buf / 6 - 1;
5059
js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen,
5063
GC_MARK(cx, elt, buf);
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.
5078
* This scope property keeps the JSOP_NAME code in js_Interpret happy by
5079
* giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
5081
* NB: xml_deleteProperty must take care to remove any property added here.
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.
5094
xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
5097
JSScopeProperty *sprop;
5099
if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp))
5102
if (*propp == FOUND_XML_PROPERTY) {
5103
sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
5104
SPROP_INVALID_SLOT, JSPROP_ENUMERATE,
5109
JS_LOCK_OBJ(cx, obj);
5111
*propp = (JSProperty *) sprop;
5117
xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
5118
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
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,
5128
if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value))
5136
xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5138
if (id == JS_DEFAULT_XML_NAMESPACE_ID) {
5143
return GetProperty(cx, obj, ID_TO_VALUE(id), vp);
5147
xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5149
return PutProperty(cx, obj, ID_TO_VALUE(id), vp);
5153
FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5161
if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop))
5164
DROP_PROPERTY(cx, pobj, prop);
5165
*foundp = (prop != NULL);
5171
xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5176
if (!FoundProperty(cx, obj, id, prop, &found))
5178
*attrsp = found ? JSPROP_ENUMERATE : 0;
5183
xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
5188
if (!FoundProperty(cx, obj, id, prop, &found))
5191
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5192
JSMSG_CANT_SET_XML_ATTRS);
5198
xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
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.
5208
* But now it's time to remove any such property, to purge the property
5209
* cache and remove the scope entry.
5211
if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) {
5212
if (!js_DeleteProperty(cx, obj, id, rval))
5216
return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval);
5220
xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
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));
5232
*vp = OBJECT_TO_JSVAL(obj);
5236
return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp);
5240
xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
5241
jsval *statep, jsid *idp)
5244
uint32 length, index;
5245
JSXMLArrayCursor *cursor;
5247
xml = (JSXML *) JS_GetPrivate(cx, obj);
5248
length = JSXML_LENGTH(xml);
5251
case JSENUMERATE_INIT:
5255
cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
5258
XMLArrayCursorInit(cursor, &xml->xml_kids);
5260
*statep = PRIVATE_TO_JSVAL(cursor);
5262
*idp = INT_TO_JSID(length);
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;
5274
case JSENUMERATE_DESTROY:
5275
cursor = JSVAL_TO_PRIVATE(*statep);
5277
XMLArrayCursorFinish(cursor);
5278
JS_free(cx, cursor);
5280
*statep = JSVAL_NULL;
5287
xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
5293
xml_mark(JSContext *cx, JSObject *obj, void *arg)
5297
xml = (JSXML *) JS_GetPrivate(cx, obj);
5298
GC_MARK(cx, xml, "private");
5299
return js_Mark(cx, obj, NULL);
5303
xml_clear(JSContext *cx, JSObject *obj)
5308
HasSimpleContent(JSXML *xml)
5315
switch (xml->xml_class) {
5316
case JSXML_CLASS_COMMENT:
5317
case JSXML_CLASS_PROCESSING_INSTRUCTION:
5319
case JSXML_CLASS_LIST:
5320
if (xml->xml_kids.length == 0)
5322
if (xml->xml_kids.length == 1) {
5323
kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
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) {
5344
* 11.2.2.1 Step 3(d) onward.
5347
xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5349
JSTempValueRooter tvr;
5351
JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL));
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.
5357
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
5358
if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value))
5361
JS_POP_TEMP_ROOT(cx, &tvr);
5366
xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
5368
return js_SetProperty(cx, obj, id, vp);
5372
xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
5373
jsval *statep, jsid *idp, jsval *vp)
5376
uint32 length, index;
5377
JSXMLArrayCursor *cursor;
5380
xml = (JSXML *) JS_GetPrivate(cx, obj);
5381
length = JSXML_LENGTH(xml);
5382
JS_ASSERT(INT_FITS_IN_JSVAL(length));
5385
case JSENUMERATE_INIT:
5389
cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
5392
XMLArrayCursorInit(cursor, &xml->xml_kids);
5394
*statep = PRIVATE_TO_JSVAL(cursor);
5396
*idp = INT_TO_JSID(length);
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)
5408
kidobj = js_GetXMLObject(cx, kid);
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;
5419
case JSENUMERATE_DESTROY:
5420
cursor = JSVAL_TO_PRIVATE(*statep);
5423
XMLArrayCursorFinish(cursor);
5424
JS_free(cx, cursor);
5426
*statep = JSVAL_NULL;
5433
xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
5438
JSString *str, *vstr;
5441
xml = (JSXML *) JS_GetPrivate(cx, obj);
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);
5449
if (xml->xml_class == JSXML_CLASS_LIST) {
5450
ok = Equals(cx, xml, v, bp);
5452
if (vxml->xml_class == JSXML_CLASS_LIST) {
5453
ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
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);
5463
str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5464
vstr = js_ValueToString(cx, v);
5467
*bp = js_EqualStrings(str, vstr);
5468
js_LeaveLocalRootScope(cx);
5471
ok = XMLEquals(cx, xml, vxml, bp);
5475
ok = js_EnterLocalRootScope(cx);
5477
if (HasSimpleContent(xml)) {
5478
str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
5479
vstr = js_ValueToString(cx, v);
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));
5487
} else if (JSVAL_IS_STRING(v)) {
5488
*bp = js_EqualStrings(str, JSVAL_TO_STRING(v));
5490
ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5492
d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5493
: *JSVAL_TO_DOUBLE(v);
5494
*bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
5500
js_LeaveLocalRootScope(cx);
5507
xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp)
5510
JSObject *listobj, *robj;
5511
JSXML *list, *lxml, *rxml;
5513
ok = js_EnterLocalRootScope(cx);
5517
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5523
list = (JSXML *) JS_GetPrivate(cx, listobj);
5524
lxml = (JSXML *) JS_GetPrivate(cx, obj);
5525
ok = Append(cx, list, lxml);
5529
if (VALUE_IS_XML(cx, v)) {
5530
rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5532
robj = ToXML(cx, v);
5537
rxml = (JSXML *) JS_GetPrivate(cx, robj);
5539
ok = Append(cx, list, rxml);
5543
*vp = OBJECT_TO_JSVAL(listobj);
5545
js_LeaveLocalRootScopeWithResult(cx, *vp);
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,
5559
NULL, xml_hasInstance,
5560
js_SetProtoOrParent, js_SetProtoOrParent,
5561
xml_mark, xml_clear,
5563
xml_getMethod, xml_setMethod,
5564
xml_enumerateValues, xml_equality,
5568
static JSObjectOps *
5569
xml_getObjectOps(JSContext *cx, JSClass *clasp)
5571
return &js_XMLObjectOps.base;
5574
JS_FRIEND_DATA(JSClass) js_XMLClass = {
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
5584
CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp,
5585
uintN argc, jsval *argv)
5590
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
5592
if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval))
5594
JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval));
5595
return JSVAL_TO_OBJECT(rval);
5599
StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv)
5604
JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2]));
5606
xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv);
5607
if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5610
if (xml->xml_kids.length == 1) {
5611
xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5613
*objp = js_GetXMLObject(cx, xml);
5616
argv[-1] = OBJECT_TO_JSVAL(*objp);
5621
fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
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);
5632
#define XML_METHOD_PROLOG \
5634
xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \
5639
#define NON_LIST_XML_METHOD_PROLOG \
5641
xml = StartNonListXMLMethod(cx, &obj, argv); \
5644
JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \
5648
xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5655
NON_LIST_XML_METHOD_PROLOG;
5656
if (xml->xml_class != JSXML_CLASS_ELEMENT)
5658
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5662
nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv);
5665
argv[0] = OBJECT_TO_JSVAL(nsobj);
5667
ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
5668
if (!AddInScopeNamespace(cx, xml, ns))
5670
ns->declared = JS_TRUE;
5671
*rval = OBJECT_TO_JSVAL(obj);
5676
xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5683
NON_LIST_XML_METHOD_PROLOG;
5684
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5688
if (!js_GetAnyName(cx, &name))
5691
if (!GetProperty(cx, obj, name, &v))
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);
5700
if (!IndexToIdVal(cx, vxml->xml_kids.length, &name))
5702
if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0]))
5705
*rval = OBJECT_TO_JSVAL(obj);
5709
/* XML and XMLList */
5711
xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5716
qn = ToAttributeName(cx, argv[0]);
5719
argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */
5720
return GetProperty(cx, obj, argv[0], rval);
5723
/* XML and XMLList */
5725
xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5730
JSTempValueRooter tvr;
5733
name = ATOM_KEY(cx->runtime->atomState.starAtom);
5734
qn = ToAttributeName(cx, name);
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);
5745
xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5750
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5754
*rval = OBJECT_TO_JSVAL(listobj);
5755
list = (JSXML *) JS_GetPrivate(cx, listobj);
5756
list->xml_target = xml;
5761
xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5768
/* ECMA-357 13.4.4.6 */
5769
JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5771
if (js_IdIsIndex(name, &index)) {
5772
if (index >= JSXML_LENGTH(xml)) {
5775
kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5779
kidobj = js_GetXMLObject(cx, kid);
5782
*rval = OBJECT_TO_JSVAL(kidobj);
5788
return GetProperty(cx, obj, name, rval);
5791
/* XML and XMLList */
5793
xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
5795
JSXML *xml, *list, *kid, *vxml;
5796
JSXMLArrayCursor cursor;
5802
if (xml->xml_class == JSXML_CLASS_LIST) {
5803
/* ECMA-357 13.5.4.4 */
5804
list = xml_list_helper(cx, xml, rval);
5808
XMLArrayCursorInit(&cursor, &xml->xml_kids);
5809
while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
5810
kidobj = js_GetXMLObject(cx, kid);
5813
if (!xml_child_helper(cx, kidobj, kid, name, &v))
5815
if (JSVAL_IS_VOID(v)) {
5816
/* The property didn't exist in this kid. */
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)) {
5827
XMLArrayCursorFinish(&cursor);
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))
5834
if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval))
5840
xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5843
JSXML *xml, *parent;
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);
5852
for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5853
if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5857
return js_NewNumberValue(cx, i, rval);
5860
/* XML and XMLList */
5862
xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5867
name = ATOM_KEY(cx->runtime->atomState.starAtom);
5868
return GetProperty(cx, obj, name, rval);
5871
/* XML and XMLList */
5873
xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5876
JSXML *xml, *list, *kid, *vxml;
5883
list = xml_list_helper(cx, xml, rval);
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);
5897
kidobj = js_GetXMLObject(cx, kid);
5899
ok = xml_comments(cx, kidobj, argc, argv, &v);
5904
js_LeaveLocalRootScopeWithResult(cx, v);
5907
vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
5908
if (JSXML_LENGTH(vxml) != 0) {
5909
ok = Append(cx, list, vxml);
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);
5930
/* XML and XMLList */
5932
xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5938
JSXMLArrayCursor cursor;
5943
if (xml->xml_class == JSXML_CLASS_LIST) {
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))
5953
XMLArrayCursorFinish(&cursor);
5957
if (!xml_equality(cx, obj, value, &eq))
5960
*rval = BOOLEAN_TO_JSVAL(eq);
5964
/* XML and XMLList */
5966
xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
5971
copy = DeepCopy(cx, xml, NULL, 0);
5974
*rval = OBJECT_TO_JSVAL(copy->object);
5978
/* XML and XMLList */
5980
xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
5987
name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
5988
list = Descendants(cx, xml, name);
5991
*rval = OBJECT_TO_JSVAL(list->object);
5995
/* XML and XMLList */
5997
xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6000
JSXML *xml, *list, *kid, *vxml;
6005
JSXMLArrayCursor cursor;
6010
name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
6011
nameqn = ToXMLName(cx, name, &funid);
6014
argv[0] = OBJECT_TO_JSVAL(nameqn->object);
6016
list = xml_list_helper(cx, xml, rval);
6022
list->xml_targetprop = nameqn;
6025
if (xml->xml_class == JSXML_CLASS_LIST) {
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);
6033
kidobj = js_GetXMLObject(cx, kid);
6035
ok = xml_elements(cx, kidobj, argc, argv, &v);
6040
js_LeaveLocalRootScopeWithResult(cx, v);
6043
vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
6044
if (JSXML_LENGTH(vxml) != 0) {
6045
ok = Append(cx, list, vxml);
6051
XMLArrayCursorFinish(&cursor);
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);
6067
/* XML and XMLList */
6069
xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6076
if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv))
6080
if (!HasProperty(cx, obj, name, &pobj, &prop))
6083
return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv,
6086
DROP_PROPERTY(cx, pobj, prop);
6091
/* XML and XMLList */
6093
xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
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;
6109
case JSXML_CLASS_LIST:
6110
if (xml->xml_kids.length == 0) {
6112
} else if (xml->xml_kids.length == 1) {
6113
kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6115
kidobj = js_GetXMLObject(cx, kid);
6119
xml = (JSXML *) JS_GetPrivate(cx, obj);
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) {
6138
/* XML and XMLList */
6140
xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6146
*rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
6150
typedef struct JSTempRootedNSArray {
6151
JSTempValueRooter tvr;
6153
jsval value; /* extra root for temporaries */
6154
} JSTempRootedNSArray;
6156
JS_STATIC_DLL_CALLBACK(void)
6157
mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr)
6159
JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr;
6161
namespace_mark_vector(cx,
6162
(JSXMLNamespace **)tmp->array.vector,
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");
6170
InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
6172
XMLArrayInit(cx, &tmp->array, 0);
6173
tmp->value = JSVAL_NULL;
6174
JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr);
6178
FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
6180
JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array);
6181
JS_POP_TEMP_ROOT(cx, &tmp->tvr);
6182
XMLArrayFinish(cx, &tmp->array);
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.
6190
TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval)
6197
arrayobj = js_NewArrayObject(cx, 0, NULL);
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);
6205
nsobj = js_GetXMLNamespaceObject(cx, ns);
6208
tmp->value = OBJECT_TO_JSVAL(nsobj);
6209
if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value))
6216
FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
6218
uint32 length, i, j, n;
6219
JSXMLNamespace *ns, *ns2;
6221
length = nsarray->length;
6223
if (xml->xml_class != JSXML_CLASS_ELEMENT)
6225
for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6226
ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
6230
for (j = 0; j < length; j++) {
6231
ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace);
6233
((ns2->prefix && ns->prefix)
6234
? js_EqualStrings(ns2->prefix, ns->prefix)
6235
: js_EqualStrings(ns2->uri, ns->uri))) {
6241
if (!XMLARRAY_APPEND(cx, nsarray, ns))
6246
} while ((xml = xml->parent) != NULL);
6247
JS_ASSERT(length == nsarray->length);
6253
xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6257
JSTempRootedNSArray namespaces;
6260
NON_LIST_XML_METHOD_PROLOG;
6262
InitTempNSArray(cx, &namespaces);
6263
ok = FindInScopeNamespaces(cx, xml, &namespaces.array) &&
6264
TempNSArrayToJSArray(cx, &namespaces, rval);
6265
FinishTempNSArray(cx, &namespaces);
6270
xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6277
NON_LIST_XML_METHOD_PROLOG;
6278
if (!JSXML_HAS_KIDS(xml))
6282
if (JSVAL_IS_NULL(arg)) {
6286
if (!VALUE_IS_XML(cx, arg))
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)
6295
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6298
if (!Insert(cx, xml, i, argv[1]))
6300
*rval = OBJECT_TO_JSVAL(obj);
6305
xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6312
NON_LIST_XML_METHOD_PROLOG;
6313
if (!JSXML_HAS_KIDS(xml))
6317
if (JSVAL_IS_NULL(arg)) {
6319
i = xml->xml_kids.length;
6321
if (!VALUE_IS_XML(cx, arg))
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)
6329
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6332
if (!Insert(cx, xml, i, argv[1]))
6334
*rval = OBJECT_TO_JSVAL(obj);
6338
/* XML and XMLList */
6340
xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
6345
if (xml->xml_class != JSXML_CLASS_LIST) {
6348
if (!js_NewNumberValue(cx, xml->xml_kids.length, rval))
6355
xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6360
NON_LIST_XML_METHOD_PROLOG;
6361
*rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL;
6366
xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
6371
NON_LIST_XML_METHOD_PROLOG;
6375
nameobj = js_GetXMLQNameObject(cx, xml->name);
6378
*rval = OBJECT_TO_JSVAL(nameobj);
6384
xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6389
JSTempRootedNSArray inScopeNSes;
6395
NON_LIST_XML_METHOD_PROLOG;
6396
if (argc == 0 && !JSXML_HAS_NAME(xml)) {
6404
prefix = js_ValueToString(cx, argv[0]);
6407
argv[0] = STRING_TO_JSVAL(prefix); /* local root */
6410
/* After this point the control must flow through label out. */
6411
InitTempNSArray(cx, &inScopeNSes);
6412
ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array);
6417
ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
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))
6435
nsobj = js_GetXMLNamespaceObject(cx, ns);
6440
*rval = OBJECT_TO_JSVAL(nsobj);
6444
FinishTempNSArray(cx, &inScopeNSes);
6449
xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6454
JSTempRootedNSArray ancestors, declared;
6458
NON_LIST_XML_METHOD_PROLOG;
6459
if (JSXML_HAS_VALUE(xml))
6462
/* From here, control flow must goto out to finish these arrays. */
6464
InitTempNSArray(cx, &ancestors);
6465
InitTempNSArray(cx, &declared);
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);
6473
!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6474
ok = XMLARRAY_APPEND(cx, &ancestors.array, ns);
6481
for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6482
ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
6487
if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6488
ok = XMLARRAY_APPEND(cx, &declared.array, ns);
6494
ok = TempNSArrayToJSArray(cx, &declared, rval);
6497
/* Finishing must be in reverse order of initialization to follow LIFO. */
6498
FinishTempNSArray(cx, &declared);
6499
FinishTempNSArray(cx, &ancestors);
6503
static const char js_attribute_str[] = "attribute";
6504
static const char js_text_str[] = "text";
6506
/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */
6507
const char *js_xml_class_str[] = {
6511
"processing-instruction",
6517
xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6523
NON_LIST_XML_METHOD_PROLOG;
6524
str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6527
*rval = STRING_TO_JSVAL(str);
6532
NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id)
6536
if (xml->xml_class == JSXML_CLASS_LIST)
6537
return DeleteProperty(cx, obj, id, &junk);
6538
return DeleteByIndex(cx, xml, id, &junk);
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.
6546
IsXMLSpace(JSString *str)
6548
const jschar *cp, *end;
6550
cp = JSSTRING_CHARS(str);
6551
end = cp + JSSTRING_LENGTH(str);
6553
if (!JS_ISXMLSPACE(*cp))
6560
/* XML and XMLList */
6562
xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6565
JSXML *xml, *kid, *kid2;
6572
*rval = OBJECT_TO_JSVAL(obj);
6573
if (!JSXML_HAS_KIDS(xml))
6576
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6580
for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6581
kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6584
if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6585
kidobj = js_GetXMLObject(cx, kid);
6586
if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk))
6588
} else if (kid->xml_class == JSXML_CLASS_TEXT) {
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);
6595
if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1)))
6597
n = xml->xml_kids.length;
6598
kid->xml_value = str;
6600
if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) {
6601
if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i)))
6603
n = xml->xml_kids.length;
6612
/* XML and XMLList */
6614
xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
6616
JSXML *xml, *parent, *kid;
6618
JSObject *parentobj;
6621
parent = xml->parent;
6622
if (xml->xml_class == JSXML_CLASS_LIST) {
6624
n = xml->xml_kids.length;
6628
kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
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)
6644
parentobj = js_GetXMLObject(cx, parent);
6647
*rval = OBJECT_TO_JSVAL(parentobj);
6651
/* XML and XMLList */
6653
xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc,
6654
jsval *argv, jsval *rval)
6656
JSXML *xml, *list, *kid, *vxml;
6661
JSXMLArrayCursor cursor;
6666
name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
6667
nameqn = ToXMLName(cx, name, &funid);
6670
argv[0] = OBJECT_TO_JSVAL(nameqn->object);
6672
list = xml_list_helper(cx, xml, rval);
6678
list->xml_targetprop = nameqn;
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);
6689
kidobj = js_GetXMLObject(cx, kid);
6691
ok = xml_processingInstructions(cx, kidobj, argc, argv, &v);
6696
js_LeaveLocalRootScopeWithResult(cx, v);
6699
vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
6700
if (JSXML_LENGTH(vxml) != 0) {
6701
ok = Append(cx, list, vxml);
6707
XMLArrayCursorFinish(&cursor);
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);
6726
xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6731
NON_LIST_XML_METHOD_PROLOG;
6732
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6735
*rval = OBJECT_TO_JSVAL(obj);
6736
return Insert(cx, xml, 0, argv[0]);
6739
/* XML and XMLList */
6741
xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6750
*rval = JSVAL_FALSE;
6751
if (js_IdIsIndex(name, &index)) {
6752
if (xml->xml_class == JSXML_CLASS_LIST) {
6754
*rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6757
*rval = BOOLEAN_TO_JSVAL(index == 0);
6764
namespace_full_match(const void *a, const void *b)
6766
const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
6767
const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
6769
if (nsa->prefix && nsb->prefix &&
6770
!js_EqualStrings(nsa->prefix, nsb->prefix)) {
6773
return js_EqualStrings(nsa->uri, nsb->uri);
6777
xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
6779
JSXMLNamespace *thisns, *attrns;
6783
thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6788
for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6789
attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6792
attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
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);
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))
6813
xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6820
NON_LIST_XML_METHOD_PROLOG;
6821
*rval = OBJECT_TO_JSVAL(obj);
6822
if (xml->xml_class != JSXML_CLASS_ELEMENT)
6824
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6828
nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv);
6831
argv[0] = OBJECT_TO_JSVAL(nsobj);
6832
ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
6834
/* NOTE: remove ns from each ancestor if not used by that ancestor. */
6835
return xml_removeNamespace_helper(cx, xml, ns);
6839
xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
6841
JSXML *xml, *vxml, *kid;
6842
jsval name, value, id, junk;
6847
NON_LIST_XML_METHOD_PROLOG;
6848
*rval = OBJECT_TO_JSVAL(obj);
6849
if (xml->xml_class != JSXML_CLASS_ELEMENT)
6853
vxml = VALUE_IS_XML(cx, value)
6854
? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value))
6857
if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1]))
6861
vxml = DeepCopy(cx, vxml, NULL, 0);
6864
value = argv[1] = OBJECT_TO_JSVAL(vxml->object);
6867
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6872
if (js_IdIsIndex(name, &index))
6873
return Replace(cx, xml, name, value);
6875
/* Call function QName per spec, not ToXMLName, to avoid attribute names. */
6876
nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name);
6879
argv[0] = OBJECT_TO_JSVAL(nameobj);
6880
nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
6883
index = xml->xml_kids.length;
6884
while (index != 0) {
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))
6890
if (!IndexToIdVal(cx, index, &id))
6894
if (JSVAL_IS_VOID(id))
6896
return Replace(cx, xml, id, value);
6900
xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6903
if (!StartNonListXMLMethod(cx, &obj, argv))
6906
if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom),
6911
*rval = OBJECT_TO_JSVAL(obj);
6916
xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
6924
NON_LIST_XML_METHOD_PROLOG;
6925
if (!JSXML_HAS_NAME(xml))
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;
6934
if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0]))
6937
namestr = JSVAL_TO_STRING(name);
6940
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6943
xml->name->localName = namestr;
6948
xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
6950
JSXML *xml, *nsowner;
6954
JSXMLArray *nsarray;
6958
NON_LIST_XML_METHOD_PROLOG;
6959
if (!JSXML_HAS_NAME(xml))
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)))
6967
name = argv[0] = STRING_TO_JSVAL(nameqn->localName);
6970
nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name);
6973
nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
6975
/* ECMA-357 13.4.4.35 Step 4. */
6976
if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6977
nameqn->uri = cx->runtime->emptyString;
6979
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
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).
6991
if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6994
if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6996
nsowner = xml->parent;
6999
if (nameqn->prefix) {
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.
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).
7010
ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
7014
/* XXXbe have to test membership to see whether GetNamespace added */
7015
if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
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).
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.
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).
7032
JS_ASSERT(!IS_EMPTY(nameqn->uri));
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;
7043
ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE);
7048
return AddInScopeNamespace(cx, nsowner, ns);
7052
xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7055
JSXML *xml, *nsowner;
7056
JSObject *nsobj, *qnobj;
7060
NON_LIST_XML_METHOD_PROLOG;
7061
if (!JSXML_HAS_NAME(xml))
7064
xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
7065
if (!xml || !js_GetXMLQNameObject(cx, xml->name))
7068
nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv);
7071
ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
7072
ns->declared = JS_TRUE;
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);
7080
xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj);
7083
* Erratum: the spec fails to update the governing in-scope namespaces.
7084
* See the erratum noted in xml_setName, above.
7086
if (xml->xml_class == JSXML_CLASS_ELEMENT) {
7089
if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
7091
nsowner = xml->parent;
7093
return AddInScopeNamespace(cx, nsowner, ns);
7096
/* XML and XMLList */
7098
xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7100
JSXML *xml, *list, *kid, *vxml;
7107
list = xml_list_helper(cx, xml, rval);
7111
if (xml->xml_class == JSXML_CLASS_LIST) {
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);
7119
kidobj = js_GetXMLObject(cx, kid);
7121
ok = xml_text(cx, kidobj, argc, argv, &v);
7126
js_LeaveLocalRootScopeWithResult(cx, v);
7129
vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
7130
if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
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))
7146
/* XML and XMLList */
7148
xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7153
str = ToXMLString(cx, OBJECT_TO_JSVAL(obj));
7156
*rval = STRING_TO_JSVAL(str);
7160
/* XML and XMLList */
7162
xml_toString_helper(JSContext *cx, JSXML *xml)
7164
JSString *str, *kidstr;
7166
JSXMLArrayCursor cursor;
7168
if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
7169
xml->xml_class == JSXML_CLASS_TEXT) {
7170
return xml->xml_value;
7173
if (!HasSimpleContent(xml))
7174
return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object));
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);
7187
str = js_ConcatStrings(cx, str, kidstr);
7192
XMLArrayCursorFinish(&cursor);
7193
js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
7198
xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7205
str = xml_toString_helper(cx, xml);
7208
*rval = STRING_TO_JSVAL(str);
7212
/* XML and XMLList */
7214
xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7216
*rval = OBJECT_TO_JSVAL(obj);
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},
7265
CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
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))
7275
if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v))
7279
name = xml_static_props[i].name;
7280
if (!JS_GetProperty(cx, from, name, &v))
7282
if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v))
7288
SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
7293
for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
7295
if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
7298
v = INT_TO_JSVAL(2);
7299
return JS_SetProperty(cx, obj, xml_static_props[i].name, &v);
7303
xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7307
settings = JS_NewObject(cx, NULL, NULL, NULL);
7310
*rval = OBJECT_TO_JSVAL(settings);
7311
return CopyXMLSettings(cx, obj, settings);
7315
xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7323
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
7324
cx->xmlSettingFlags = 0;
7325
ok = SetDefaultXMLSettings(cx, obj);
7327
if (JSVAL_IS_PRIMITIVE(v))
7329
settings = JSVAL_TO_OBJECT(v);
7330
cx->xmlSettingFlags = 0;
7331
ok = CopyXMLSettings(cx, settings, obj);
7334
cx->xmlSettingFlags |= XSF_CACHE_VALID;
7339
xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7344
settings = JS_NewObject(cx, NULL, NULL, NULL);
7347
*rval = OBJECT_TO_JSVAL(settings);
7348
return SetDefaultXMLSettings(cx, settings);
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},
7359
XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7363
JSObject *xobj, *vobj;
7367
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7368
v = STRING_TO_JSVAL(cx->runtime->emptyString);
7370
xobj = ToXML(cx, v);
7373
*rval = OBJECT_TO_JSVAL(xobj);
7374
xml = (JSXML *) JS_GetPrivate(cx, xobj);
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);
7385
JS_ASSERT(copy->object == obj);
7386
*rval = OBJECT_TO_JSVAL(obj);
7394
XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
7397
JSObject *vobj, *listobj;
7401
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7402
v = STRING_TO_JSVAL(cx->runtime->emptyString);
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);
7412
*rval = OBJECT_TO_JSVAL(listobj);
7414
list = (JSXML *) JS_GetPrivate(cx, listobj);
7415
if (!Append(cx, list, xml))
7422
/* Toggle on XML support since the script has explicitly requested it. */
7423
listobj = ToXMLList(cx, v);
7427
*rval = OBJECT_TO_JSVAL(listobj);
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 *))
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 */
7445
JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
7450
js_NewXML(JSContext *cx, JSXMLClass xml_class)
7454
xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]);
7459
xml->domnode = NULL;
7462
xml->xml_class = xml_class;
7464
if (JSXML_CLASS_HAS_VALUE(xml_class)) {
7465
xml->xml_value = cx->runtime->emptyString;
7467
XMLArrayInit(cx, &xml->xml_kids, 0);
7468
if (xml_class == JSXML_CLASS_LIST) {
7469
xml->xml_target = NULL;
7470
xml->xml_targetprop = NULL;
7472
XMLArrayInit(cx, &xml->xml_namespaces, 0);
7473
XMLArrayInit(cx, &xml->xml_attrs, 0);
7478
JS_APPEND_LINK(&xml->links, &xml_leaks);
7479
xml->serial = xml_serial++;
7481
METER(xml_stats.xml);
7482
METER(xml_stats.livexml);
7487
js_MarkXML(JSContext *cx, JSXML *xml)
7489
GC_MARK(cx, xml->object, "object");
7490
GC_MARK(cx, xml->name, "name");
7491
GC_MARK(cx, xml->parent, "xml_parent");
7493
if (JSXML_HAS_VALUE(xml)) {
7494
GC_MARK(cx, xml->xml_value, "value");
7499
(JSXML **) xml->xml_kids.vector,
7500
xml->xml_kids.length);
7501
XMLArrayCursorMark(cx, xml->xml_kids.cursors);
7502
XMLArrayTrim(&xml->xml_kids);
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");
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);
7517
(JSXML **) xml->xml_attrs.vector,
7518
xml->xml_attrs.length);
7519
XMLArrayCursorMark(cx, xml->xml_attrs.cursors);
7520
XMLArrayTrim(&xml->xml_attrs);
7525
js_FinalizeXML(JSContext *cx, JSXML *xml)
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);
7536
JS_REMOVE_LINK(&xml->links);
7539
UNMETER(xml_stats.livexml);
7543
js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn)
7550
if (!js_GetDefaultXMLNamespace(cx, &nsval))
7552
JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
7553
ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
7555
if (!XMLArrayInit(cx, &nsarray, 1))
7558
XMLARRAY_APPEND(cx, &nsarray, ns);
7559
xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT);
7560
XMLArrayFinish(cx, &nsarray);
7568
js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7572
JSTempValueRooter tvr;
7574
xml = js_NewXML(cx, xml_class);
7577
JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr);
7578
obj = js_GetXMLObject(cx, xml);
7579
JS_POP_TEMP_ROOT(cx, &tvr);
7584
NewXMLObject(JSContext *cx, JSXML *xml)
7588
obj = js_NewObject(cx, &js_XMLClass, NULL, NULL);
7589
if (!obj || !JS_SetPrivate(cx, obj, xml)) {
7590
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
7593
METER(xml_stats.xmlobj);
7594
METER(xml_stats.livexmlobj);
7599
js_GetXMLObject(JSContext *cx, JSXML *xml)
7605
JS_ASSERT(JS_GetPrivate(cx, obj) == xml);
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.
7616
* Once given an object, a JSXML is immutable.
7618
JS_ASSERT(!xml->parent ||
7619
!xml->parent->object ||
7620
JS_IS_OBJ_LOCKED(cx, xml->parent->object));
7622
obj = NewXMLObject(cx, xml);
7630
js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7632
return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2,
7633
namespace_props, namespace_methods, NULL, NULL);
7637
js_InitQNameClass(JSContext *cx, JSObject *obj)
7639
return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2,
7640
qname_props, qname_methods, NULL, NULL);
7644
js_InitAttributeNameClass(JSContext *cx, JSObject *obj)
7646
return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2,
7647
qname_props, qname_methods, NULL, NULL);
7651
js_InitAnyNameClass(JSContext *cx, JSObject *obj)
7655
if (!js_GetAnyName(cx, &v))
7657
return JSVAL_TO_OBJECT(v);
7661
js_InitXMLClass(JSContext *cx, JSObject *obj)
7663
JSObject *proto, *pobj, *ctor;
7667
JSScopeProperty *sprop;
7668
jsval cval, argv[1], junk;
7670
/* Define the isXMLName function. */
7671
if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7674
/* Define the XML class constructor and prototype. */
7675
proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1,
7677
xml_static_props, xml_static_methods);
7681
xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7682
if (!xml || !JS_SetPrivate(cx, proto, xml))
7684
xml->object = proto;
7685
METER(xml_stats.xmlobj);
7686
METER(xml_stats.livexmlobj);
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.
7694
if (!js_LookupProperty(cx, proto,
7695
ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
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));
7706
/* Set default settings. */
7707
ctor = JSVAL_TO_OBJECT(cval);
7708
argv[0] = JSVAL_VOID;
7709
if (!xml_setSettings(cx, ctor, 1, argv, &junk))
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);
7716
if (!js_SetClassPrototype(cx, fun->object, proto,
7717
JSPROP_READONLY | JSPROP_PERMANENT)) {
7724
js_InitXMLClasses(JSContext *cx, JSObject *obj)
7726
if (!js_InitNamespaceClass(cx, obj))
7728
if (!js_InitQNameClass(cx, obj))
7730
if (!js_InitAttributeNameClass(cx, obj))
7732
if (!js_InitAnyNameClass(cx, obj))
7734
return js_InitXMLClass(cx, obj);
7738
js_GetFunctionNamespace(JSContext *cx, jsval *vp)
7743
JSString *prefix, *uri;
7745
/* An invalid URI, for internal use only, guaranteed not to collide. */
7746
static const char anti_uri[] = "@mozilla.org/js/function";
7748
/* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7750
obj = rt->functionNamespaceObject;
7753
obj = rt->functionNamespaceObject;
7756
atom = js_Atomize(cx, js_function_str, 8, 0);
7758
prefix = ATOM_TO_STRING(atom);
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
7767
atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED);
7770
rt->atomState.lazy.functionNamespaceURIAtom = atom;
7772
uri = ATOM_TO_STRING(atom);
7773
obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE);
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.
7783
OBJ_SET_PROTO(cx, obj, NULL);
7784
OBJ_SET_PARENT(cx, obj, NULL);
7787
if (!rt->functionNamespaceObject)
7788
rt->functionNamespaceObject = obj;
7790
obj = rt->functionNamespaceObject;
7794
*vp = OBJECT_TO_JSVAL(obj);
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).
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.
7814
js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7817
JSObject *nsobj, *obj, *tmp;
7821
nsobj = fp->xmlNamespace;
7823
*vp = OBJECT_TO_JSVAL(nsobj);
7828
for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) {
7830
if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v))
7832
if (!JSVAL_IS_PRIMITIVE(v)) {
7833
fp->xmlNamespace = JSVAL_TO_OBJECT(v);
7839
nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL);
7842
v = OBJECT_TO_JSVAL(nsobj);
7844
!OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v,
7845
JS_PropertyStub, JS_PropertyStub,
7846
JSPROP_PERMANENT, NULL)) {
7849
fp->xmlNamespace = nsobj;
7855
js_SetDefaultXMLNamespace(JSContext *cx, jsval v)
7858
JSObject *nsobj, *varobj;
7861
argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString);
7863
nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
7867
v = OBJECT_TO_JSVAL(nsobj);
7870
varobj = fp->varobj;
7872
if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v,
7873
JS_PropertyStub, JS_PropertyStub,
7874
JSPROP_PERMANENT, NULL)) {
7878
JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags));
7880
fp->xmlNamespace = JSVAL_TO_OBJECT(v);
7885
js_ToAttributeName(JSContext *cx, jsval *vp)
7889
qn = ToAttributeName(cx, *vp);
7892
*vp = OBJECT_TO_JSVAL(qn->object);
7897
js_EscapeAttributeValue(JSContext *cx, JSString *str)
7899
return EscapeAttributeValue(cx, NULL, str);
7903
js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7905
size_t len, len2, newlen;
7908
if (JSSTRING_IS_DEPENDENT(str) ||
7909
!(*js_GetGCThingFlags(str) & GCF_MUTABLE)) {
7910
str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
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));
7924
* Reallocating str (because we know it has no other references) requires
7925
* purging any deflated string cached for it.
7927
js_PurgeDeflatedStringCache(cx->runtime, str);
7930
str->length = newlen;
7934
js_strncpy(chars, JSSTRING_CHARS(str2), len2);
7939
js_strncpy(chars, JSSTRING_CHARS(str2), len2);
7948
js_EscapeElementValue(JSContext *cx, JSString *str)
7950
return EscapeElementValue(cx, NULL, str);
7954
js_ValueToXMLString(JSContext *cx, jsval v)
7956
return ToXMLString(cx, v);
7960
anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
7963
*rval = ATOM_KEY(cx->runtime->atomState.starAtom);
7968
js_GetAnyName(JSContext *cx, jsval *vp)
7975
/* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7977
obj = rt->anynameObject;
7980
obj = rt->anynameObject;
7985
* Protect multiple newborns created below, in the do-while(0)
7986
* loop used to ensure that we leave this local root scope.
7988
ok = js_EnterLocalRootScope(cx);
7993
qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString,
7994
ATOM_TO_STRING(rt->atomState.starAtom));
8000
obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL);
8001
if (!obj || !JS_SetPrivate(cx, obj, qn)) {
8002
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
8007
METER(xml_stats.qnameobj);
8008
METER(xml_stats.liveqnameobj);
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.
8016
if (!JS_DefineFunction(cx, obj, js_toString_str,
8017
anyname_toString, 0, 0)) {
8021
OBJ_SET_PROTO(cx, obj, NULL);
8022
JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
8025
js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj));
8030
if (!rt->anynameObject)
8031
rt->anynameObject = obj;
8033
obj = rt->anynameObject;
8037
*vp = OBJECT_TO_JSVAL(obj);
8042
js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep)
8046
JSObject *obj, *pobj, *lastobj;
8048
const char *printable;
8050
qn = ToXMLName(cx, name, &funid);
8053
id = OBJECT_TO_JSID(qn->object);
8055
obj = cx->fp->scopeChain;
8057
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
8060
OBJ_DROP_PROPERTY(cx, pobj, prop);
8063
* Call OBJ_THIS_OBJECT to skip any With object that wraps an XML
8064
* object to carry scope chain linkage in js_FilterXMLList.
8066
pobj = OBJ_THIS_OBJECT(cx, obj);
8067
if (OBJECT_IS_XML(cx, pobj)) {
8069
*namep = ID_TO_VALUE(id);
8075
} while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
8077
printable = js_ValueToPrintableString(cx, name);
8079
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
8080
js_GetErrorMessage, NULL,
8081
JSMSG_UNDEFINED_XML_NAME, printable);
8087
js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp)
8089
return GetProperty(cx, obj, name, vp);
8093
js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
8097
JSTempValueRooter tvr;
8100
JS_ASSERT(OBJECT_IS_XML(cx, obj));
8102
/* After this point, control must flow through label out: to exit. */
8103
JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
8106
* See comments before xml_lookupProperty about the need for the proto
8111
ok = js_GetProperty(cx, target, id, vp);
8114
if (VALUE_IS_FUNCTION(cx, *vp)) {
8118
target = OBJ_GET_PROTO(cx, target);
8121
tvr.u.object = target;
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),
8131
JS_ASSERT(tvr.u.object);
8132
ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp);
8136
JS_POP_TEMP_ROOT(cx, &tvr);
8141
js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp)
8143
return PutProperty(cx, obj, name, vp);
8147
GetPrivate(JSContext *cx, JSObject *obj, const char *method)
8151
xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
8153
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
8154
JSMSG_INCOMPATIBLE_METHOD,
8155
js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name);
8161
js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
8165
xml = GetPrivate(cx, obj, "descendants internal method");
8169
list = Descendants(cx, xml, id);
8172
*vp = OBJECT_TO_JSVAL(list->object);
8177
js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
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))
8192
js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp)
8197
JSObject *scobj, *listobj, *resobj, *withobj, *kidobj;
8198
JSXML *xml, *list, *result, *kid;
8199
JSXMLArrayCursor cursor;
8201
ok = js_EnterLocalRootScope(cx);
8205
/* All control flow after this point must exit via label out or bad. */
8209
fp->flags = flags | JSFRAME_FILTERING;
8210
scobj = js_GetScopeChain(cx, fp);
8214
xml = GetPrivate(cx, obj, "filtering predicate operator");
8218
if (xml->xml_class == JSXML_CLASS_LIST) {
8221
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
8224
list = (JSXML *) JS_GetPrivate(cx, listobj);
8225
ok = Append(cx, list, xml);
8230
resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
8233
result = (JSXML *) JS_GetPrivate(cx, resobj);
8235
/* Hoist the scope chain update out of the loop over kids. */
8236
withobj = js_NewWithObject(cx, NULL, scobj, -1);
8239
fp->scopeChain = withobj;
8241
XMLArrayCursorInit(&cursor, &list->xml_kids);
8242
while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
8243
kidobj = js_GetXMLObject(cx, kid);
8246
OBJ_SET_PROTO(cx, withobj, kidobj);
8247
ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match);
8249
ok = Append(cx, result, kid);
8253
XMLArrayCursorFinish(&cursor);
8259
*vp = OBJECT_TO_JSVAL(resobj);
8262
fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS);
8264
fp->scopeChain = scobj;
8265
JS_SetPrivate(cx, withobj, NULL);
8267
js_LeaveLocalRootScopeWithResult(cx, *vp);
8275
js_ValueToXMLObject(JSContext *cx, jsval v)
8277
return ToXML(cx, v);
8281
js_ValueToXMLListObject(JSContext *cx, jsval v)
8283
return ToXMLList(cx, v);
8287
js_CloneXMLObject(JSContext *cx, JSObject *obj)
8292
if (!GetXMLSettingFlags(cx, &flags))
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);
8303
return NewXMLObject(cx, xml);
8307
js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
8315
if (!GetXMLSettingFlags(cx, &flags))
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);
8325
obj = js_NewXMLObject(cx, xml_class);
8328
xml = (JSXML *) JS_GetPrivate(cx, obj);
8330
qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name);
8335
xml->xml_value = value;
8340
js_MakeXMLCDATAString(JSContext *cx, JSString *str)
8342
return MakeXMLCDATAString(cx, NULL, str);
8346
js_MakeXMLCommentString(JSContext *cx, JSString *str)
8348
return MakeXMLCommentString(cx, NULL, str);
8352
js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
8354
return MakeXMLPIString(cx, NULL, name, str);
8357
#endif /* JS_HAS_XML_SUPPORT */