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 ***** */
41 #include "jsversion.h"
43 #if JS_HAS_XML_SUPPORT
70 #include "jsstaticcheck.h"
73 #include <string.h> /* for #ifdef DEBUG memset calls */
78 * - in the js shell, you must use the -x command line option, or call
79 * options('xml') before compiling anything that uses XML literals
83 * - Fuse objects and their JSXML* private data into single GC-things
84 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
85 * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
86 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
87 * - JS_TypeOfValue sure could use a cleaner interface to "types"
93 jsrefcount xmlnamespace
;
98 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
99 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
101 #define METER(x) /* nothing */
102 #define UNMETER(x) /* nothing */
106 * Random utilities and global functions.
108 const char js_isXMLName_str
[] = "isXMLName";
109 const char js_XMLList_str
[] = "XMLList";
110 const char js_localName_str
[] = "localName";
111 const char js_xml_parent_str
[] = "parent";
112 const char js_prefix_str
[] = "prefix";
113 const char js_toXMLString_str
[] = "toXMLString";
114 const char js_uri_str
[] = "uri";
116 const char js_amp_entity_str
[] = "&";
117 const char js_gt_entity_str
[] = ">";
118 const char js_lt_entity_str
[] = "<";
119 const char js_quot_entity_str
[] = """;
121 #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
122 #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
123 /* Slot indexes shared between Namespace and QName objects. */
124 const uint32 JSSLOT_PREFIX
= JSSLOT_PRIVATE
;
125 const uint32 JSSLOT_URI
= JSSLOT_PRIVATE
+ 1;
127 /* Namespace-specific slot. */
128 const uint32 JSSLOT_DECLARED
= JSSLOT_PRIVATE
+ 2;
130 /* QName-specific slot. */
131 const uint32 JSSLOT_LOCAL_NAME
= JSSLOT_PRIVATE
+ 2;
133 const uint32 NAMESPACE_RESERVED_SLOTS
= 3;
134 const uint32 QNAME_RESERVED_SLOTS
= 3;
137 IsQNameClass(JSClass
*clasp
)
139 return clasp
== &js_QNameClass
.base
||
140 clasp
== &js_AttributeNameClass
||
141 clasp
== &js_AnyNameClass
;
145 GetSlotString(const JSObject
*obj
, uint32 slot
)
149 JS_ASSERT(slot
== JSSLOT_PREFIX
||
150 slot
== JSSLOT_URI
||
151 slot
== JSSLOT_LOCAL_NAME
);
152 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_NamespaceClass
.base
||
153 IsQNameClass(STOBJ_GET_CLASS(obj
)));
154 JS_ASSERT_IF(STOBJ_GET_CLASS(obj
) == &js_NamespaceClass
.base
,
155 slot
!= JSSLOT_LOCAL_NAME
);
157 v
= obj
->fslots
[slot
];
158 if (JSVAL_IS_VOID(v
))
160 JS_ASSERT(JSVAL_IS_STRING(v
));
161 return JSVAL_TO_STRING(v
);
164 static JS_INLINE JSString
*
165 GetPrefix(const JSObject
*obj
)
167 return GetSlotString(obj
, JSSLOT_PREFIX
);
171 GetURI(const JSObject
*obj
)
173 return GetSlotString(obj
, JSSLOT_URI
);
177 GetLocalName(const JSObject
*obj
)
179 return GetSlotString(obj
, JSSLOT_LOCAL_NAME
);
183 IsDeclared(const JSObject
*obj
)
187 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_NamespaceClass
.base
);
188 v
= obj
->fslots
[JSSLOT_DECLARED
];
189 JS_ASSERT(JSVAL_IS_VOID(v
) || v
== JSVAL_TRUE
);
190 return v
== JSVAL_TRUE
;
194 xml_isXMLName(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
197 *rval
= BOOLEAN_TO_JSVAL(js_IsXMLName(cx
, argv
[0]));
202 * Namespace class and library functions.
204 enum namespace_tinyid
{
205 NAMESPACE_PREFIX
= -1,
210 namespace_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
212 if (!JSVAL_IS_INT(id
))
215 if (STOBJ_GET_CLASS(obj
) != &js_NamespaceClass
.base
)
218 switch (JSVAL_TO_INT(id
)) {
219 case NAMESPACE_PREFIX
:
220 *vp
= obj
->fslots
[JSSLOT_PREFIX
];
223 *vp
= obj
->fslots
[JSSLOT_URI
];
230 namespace_finalize(JSContext
*cx
, JSObject
*obj
)
232 if (cx
->runtime
->functionNamespaceObject
== obj
)
233 cx
->runtime
->functionNamespaceObject
= NULL
;
237 namespace_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
241 JS_ASSERT(JSVAL_IS_OBJECT(v
));
242 obj2
= JSVAL_TO_OBJECT(v
);
243 *bp
= (!obj2
|| OBJ_GET_CLASS(cx
, obj2
) != &js_NamespaceClass
.base
)
245 : js_EqualStrings(GetURI(obj
), GetURI(obj2
));
249 JS_FRIEND_DATA(JSExtendedClass
) js_NamespaceClass
= {
251 JSCLASS_CONSTRUCT_PROTOTYPE
| JSCLASS_IS_EXTENDED
|
252 JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS
) |
253 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace
),
254 JS_PropertyStub
, JS_PropertyStub
, namespace_getProperty
, NULL
,
255 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, namespace_finalize
,
256 NULL
, NULL
, NULL
, NULL
,
257 NULL
, NULL
, NULL
, NULL
},
258 namespace_equality
,NULL
, NULL
, NULL
,
259 NULL
, NULL
, NULL
, NULL
262 #define NAMESPACE_ATTRS \
263 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
265 static JSPropertySpec namespace_props
[] = {
266 {js_prefix_str
, NAMESPACE_PREFIX
, NAMESPACE_ATTRS
, 0, 0},
267 {js_uri_str
, NAMESPACE_URI
, NAMESPACE_ATTRS
, 0, 0},
272 namespace_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
276 obj
= JS_THIS_OBJECT(cx
, vp
);
277 if (!JS_InstanceOf(cx
, obj
, &js_NamespaceClass
.base
, vp
))
279 *vp
= obj
->fslots
[JSSLOT_URI
];
283 static JSFunctionSpec namespace_methods
[] = {
284 JS_FN(js_toString_str
, namespace_toString
, 0,0),
289 NewXMLNamespace(JSContext
*cx
, JSString
*prefix
, JSString
*uri
, JSBool declared
)
293 obj
= js_NewObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
, 0);
296 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_PREFIX
]));
297 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_URI
]));
298 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_DECLARED
]));
300 obj
->fslots
[JSSLOT_PREFIX
] = STRING_TO_JSVAL(prefix
);
302 obj
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(uri
);
304 obj
->fslots
[JSSLOT_DECLARED
] = JSVAL_TRUE
;
305 METER(xml_stats
.xmlnamespace
);
310 * QName class and library functions.
318 qname_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
320 if (!JSVAL_IS_INT(id
))
323 if (STOBJ_GET_CLASS(obj
) != &js_QNameClass
.base
)
326 switch (JSVAL_TO_INT(id
)) {
328 *vp
= obj
->fslots
[JSSLOT_URI
];
329 if (*vp
== JSVAL_VOID
)
332 case QNAME_LOCALNAME
:
333 *vp
= obj
->fslots
[JSSLOT_LOCAL_NAME
];
340 anyname_finalize(JSContext
* cx
, JSObject
* obj
)
342 /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
343 if (cx
->runtime
->anynameObject
== obj
)
344 cx
->runtime
->anynameObject
= NULL
;
348 qname_identity(JSObject
*qna
, JSObject
*qnb
)
350 JSString
*uri1
= GetURI(qna
);
351 JSString
*uri2
= GetURI(qnb
);
355 if (uri1
&& !js_EqualStrings(uri1
, uri2
))
357 return js_EqualStrings(GetLocalName(qna
), GetLocalName(qnb
));
361 qname_equality(JSContext
*cx
, JSObject
*qn
, jsval v
, JSBool
*bp
)
365 JS_ASSERT(JSVAL_IS_OBJECT(v
));
366 obj2
= JSVAL_TO_OBJECT(v
);
367 *bp
= (!obj2
|| OBJ_GET_CLASS(cx
, obj2
) != &js_QNameClass
.base
)
369 : qname_identity(qn
, obj2
);
373 JS_FRIEND_DATA(JSExtendedClass
) js_QNameClass
= {
375 JSCLASS_CONSTRUCT_PROTOTYPE
| JSCLASS_IS_EXTENDED
|
376 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS
) |
377 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_QName
),
378 JS_PropertyStub
, JS_PropertyStub
, qname_getProperty
, NULL
,
379 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
,
380 NULL
, NULL
, NULL
, NULL
,
381 NULL
, NULL
, NULL
, NULL
},
382 qname_equality
, NULL
, NULL
, NULL
,
383 NULL
, NULL
, NULL
, NULL
387 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
388 * are like QName, except that they have no property getters. They share the
389 * qname_toString method, and therefore are exposed as constructable objects
390 * in this implementation.
392 JS_FRIEND_DATA(JSClass
) js_AttributeNameClass
= {
393 js_AttributeName_str
,
394 JSCLASS_CONSTRUCT_PROTOTYPE
|
395 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS
) |
396 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName
),
397 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
398 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
,
399 NULL
, NULL
, NULL
, NULL
,
400 NULL
, NULL
, NULL
, NULL
403 JS_FRIEND_DATA(JSClass
) js_AnyNameClass
= {
405 JSCLASS_CONSTRUCT_PROTOTYPE
|
406 JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS
) |
407 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName
),
408 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
409 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, anyname_finalize
,
410 NULL
, NULL
, NULL
, NULL
,
411 NULL
, NULL
, NULL
, NULL
414 #define QNAME_ATTRS \
415 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
417 static JSPropertySpec qname_props
[] = {
418 {js_uri_str
, QNAME_URI
, QNAME_ATTRS
, 0, 0},
419 {js_localName_str
, QNAME_LOCALNAME
, QNAME_ATTRS
, 0, 0},
424 qname_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
428 JSString
*uri
, *str
, *qualstr
;
432 obj
= JS_THIS_OBJECT(cx
, vp
);
435 clasp
= OBJ_GET_CLASS(cx
, obj
);
436 if (clasp
!= &js_AttributeNameClass
&&
437 clasp
!= &js_AnyNameClass
&&
438 !JS_InstanceOf(cx
, obj
, &js_QNameClass
.base
, vp
+ 2)) {
444 /* No uri means wildcard qualifier. */
445 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.starQualifierAtom
);
446 } else if (IS_EMPTY(uri
)) {
447 /* Empty string for uri means localName is in no namespace. */
448 str
= cx
->runtime
->emptyString
;
450 qualstr
= ATOM_TO_STRING(cx
->runtime
->atomState
.qualifierAtom
);
451 str
= js_ConcatStrings(cx
, uri
, qualstr
);
455 str
= js_ConcatStrings(cx
, str
, GetLocalName(obj
));
459 if (str
&& clasp
== &js_AttributeNameClass
) {
460 length
= JSSTRING_LENGTH(str
);
461 chars
= (jschar
*) JS_malloc(cx
, (length
+ 2) * sizeof(jschar
));
465 js_strncpy(chars
+ 1, JSSTRING_CHARS(str
), length
);
467 str
= js_NewString(cx
, chars
, length
);
474 *vp
= STRING_TO_JSVAL(str
);
478 static JSFunctionSpec qname_methods
[] = {
479 JS_FN(js_toString_str
, qname_toString
, 0,0),
485 InitXMLQName(JSObject
*obj
, JSString
*uri
, JSString
*prefix
,
488 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_PREFIX
]));
489 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_URI
]));
490 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_LOCAL_NAME
]));
492 obj
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(uri
);
494 obj
->fslots
[JSSLOT_PREFIX
] = STRING_TO_JSVAL(prefix
);
496 obj
->fslots
[JSSLOT_LOCAL_NAME
] = STRING_TO_JSVAL(localName
);
500 NewXMLQName(JSContext
*cx
, JSString
*uri
, JSString
*prefix
, JSString
*localName
,
501 JSClass
*clasp
= &js_QNameClass
.base
)
505 JS_ASSERT(IsQNameClass(clasp
));
506 obj
= js_NewObject(cx
, clasp
, NULL
, NULL
, 0);
509 InitXMLQName(obj
, uri
, prefix
, localName
);
510 METER(xml_stats
.qname
);
515 js_ConstructXMLQNameObject(JSContext
*cx
, jsval nsval
, jsval lnval
)
521 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
522 * production, step 2.
524 if (!JSVAL_IS_PRIMITIVE(nsval
) &&
525 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(nsval
)) == &js_AnyNameClass
) {
531 return js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 2, argv
);
535 IsXMLName(const jschar
*cp
, size_t n
)
541 if (n
!= 0 && JS_ISXMLNSSTART(*cp
)) {
553 js_IsXMLName(JSContext
*cx
, jsval v
)
556 JSErrorReporter older
;
559 * Inline specialization of the QName constructor called with v passed as
560 * the only argument, to compute the localName for the constructed qname,
561 * without actually allocating the object or computing its uri and prefix.
562 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
564 if (!JSVAL_IS_PRIMITIVE(v
) &&
565 IsQNameClass(OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)))) {
566 name
= GetLocalName(JSVAL_TO_OBJECT(v
));
568 older
= JS_SetErrorReporter(cx
, NULL
);
569 name
= js_ValueToString(cx
, v
);
570 JS_SetErrorReporter(cx
, older
);
572 JS_ClearPendingException(cx
);
577 return IsXMLName(JSSTRING_CHARS(name
), JSSTRING_LENGTH(name
));
581 * When argc is -1, it indicates argv is empty but the code should behave as
582 * if argc is 1 and argv[0] is JSVAL_VOID.
585 NamespaceHelper(JSContext
*cx
, JSObject
*obj
, intN argc
, jsval
*argv
,
588 jsval urival
, prefixval
;
590 JSBool isNamespace
, isQName
;
592 JSString
*empty
, *uri
, *prefix
;
594 isNamespace
= isQName
= JS_FALSE
;
595 #ifdef __GNUC__ /* suppress bogus gcc warnings */
601 urival
= argv
[argc
> 1];
602 if (!JSVAL_IS_PRIMITIVE(urival
)) {
603 uriobj
= JSVAL_TO_OBJECT(urival
);
604 clasp
= OBJ_GET_CLASS(cx
, uriobj
);
605 isNamespace
= (clasp
== &js_NamespaceClass
.base
);
606 isQName
= (clasp
== &js_QNameClass
.base
);
611 /* Namespace called as function. */
612 if (argc
== 1 && isNamespace
) {
613 /* Namespace called with one Namespace argument is identity. */
618 obj
= js_NewObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
, 0);
621 *rval
= OBJECT_TO_JSVAL(obj
);
623 METER(xml_stats
.xmlnamespace
);
625 empty
= cx
->runtime
->emptyString
;
626 obj
->fslots
[JSSLOT_PREFIX
] = STRING_TO_JSVAL(empty
);
627 obj
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(empty
);
629 if (argc
== 1 || argc
== -1) {
631 obj
->fslots
[JSSLOT_URI
] = uriobj
->fslots
[JSSLOT_URI
];
632 obj
->fslots
[JSSLOT_PREFIX
] = uriobj
->fslots
[JSSLOT_PREFIX
];
633 } else if (isQName
&& (uri
= GetURI(uriobj
))) {
634 obj
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(uri
);
635 obj
->fslots
[JSSLOT_PREFIX
] = uriobj
->fslots
[JSSLOT_PREFIX
];
637 uri
= js_ValueToString(cx
, urival
);
640 obj
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(uri
);
642 obj
->fslots
[JSSLOT_PREFIX
] = JSVAL_VOID
;
644 } else if (argc
== 2) {
645 if (!isQName
|| !(uri
= GetURI(uriobj
))) {
646 uri
= js_ValueToString(cx
, urival
);
650 obj
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(uri
);
654 if (!JSVAL_IS_VOID(prefixval
)) {
655 prefix
= js_ValueToString(cx
, prefixval
);
658 if (!IS_EMPTY(prefix
)) {
659 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
660 JSMSG_BAD_XML_NAMESPACE
,
661 js_ValueToPrintableString(cx
,
662 STRING_TO_JSVAL(prefix
)));
666 } else if (JSVAL_IS_VOID(prefixval
) || !js_IsXMLName(cx
, prefixval
)) {
667 obj
->fslots
[JSSLOT_PREFIX
] = JSVAL_VOID
;
669 prefix
= js_ValueToString(cx
, prefixval
);
672 obj
->fslots
[JSSLOT_PREFIX
] = STRING_TO_JSVAL(prefix
);
680 Namespace(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
682 return NamespaceHelper(cx
,
683 JS_IsConstructing(cx
) ? obj
: NULL
,
688 * When argc is -1, it indicates argv is empty but the code should behave as
689 * if argc is 1 and argv[0] is JSVAL_VOID.
692 QNameHelper(JSContext
*cx
, JSObject
*obj
, JSClass
*clasp
, intN argc
,
693 jsval
*argv
, jsval
*rval
)
695 jsval nameval
, nsval
;
696 JSBool isQName
, isNamespace
;
698 JSString
*uri
, *prefix
, *name
;
701 JS_ASSERT(clasp
== &js_QNameClass
.base
||
702 clasp
== &js_AttributeNameClass
);
704 nameval
= JSVAL_VOID
;
707 nameval
= argv
[argc
> 1];
709 !JSVAL_IS_PRIMITIVE(nameval
) &&
710 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(nameval
)) == &js_QNameClass
.base
;
714 /* QName called as function. */
715 if (argc
== 1 && isQName
) {
716 /* QName called with one QName argument is identity. */
722 * Create and return a new QName or AttributeName object exactly as if
725 obj
= js_NewObject(cx
, clasp
, NULL
, NULL
, 0);
728 *rval
= OBJECT_TO_JSVAL(obj
);
730 METER(xml_stats
.qname
);
733 /* If namespace is not specified and name is a QName, clone it. */
734 qn
= JSVAL_TO_OBJECT(nameval
);
737 prefix
= GetPrefix(qn
);
738 name
= GetLocalName(qn
);
742 /* Namespace and qname were passed -- use the qname's localName. */
743 nameval
= qn
->fslots
[JSSLOT_LOCAL_NAME
];
747 name
= cx
->runtime
->emptyString
;
748 } else if (argc
< 0) {
749 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
751 name
= js_ValueToString(cx
, nameval
);
754 argv
[argc
> 1] = STRING_TO_JSVAL(name
);
757 if (argc
> 1 && !JSVAL_IS_VOID(argv
[0])) {
759 } else if (IS_STAR(name
)) {
762 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
764 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval
));
765 JS_ASSERT(OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(nsval
)) ==
766 &js_NamespaceClass
.base
);
769 if (JSVAL_IS_NULL(nsval
)) {
770 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
774 * Inline specialization of the Namespace constructor called with
775 * nsval passed as the only argument, to compute the uri and prefix
776 * for the constructed namespace, without actually allocating the
777 * object or computing other members. See ECMA-357 13.3.2 6(a) and
780 isNamespace
= isQName
= JS_FALSE
;
781 if (!JSVAL_IS_PRIMITIVE(nsval
)) {
782 obj2
= JSVAL_TO_OBJECT(nsval
);
783 clasp
= OBJ_GET_CLASS(cx
, obj2
);
784 isNamespace
= (clasp
== &js_NamespaceClass
.base
);
785 isQName
= (clasp
== &js_QNameClass
.base
);
787 #ifdef __GNUC__ /* suppress bogus gcc warnings */
793 prefix
= GetPrefix(obj2
);
794 } else if (isQName
&& (uri
= GetURI(obj2
))) {
796 prefix
= GetPrefix(obj2
);
799 uri
= js_ValueToString(cx
, nsval
);
802 argv
[0] = STRING_TO_JSVAL(uri
); /* local root */
804 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
805 prefix
= IS_EMPTY(uri
) ? cx
->runtime
->emptyString
: NULL
;
810 InitXMLQName(obj
, uri
, prefix
, name
);
815 QName(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
817 return QNameHelper(cx
, JS_IsConstructing(cx
) ? obj
: NULL
,
818 &js_QNameClass
.base
, argc
, argv
, rval
);
822 AttributeName(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
825 return QNameHelper(cx
, JS_IsConstructing(cx
) ? obj
: NULL
,
826 &js_AttributeNameClass
, argc
, argv
, rval
);
830 * XMLArray library functions.
833 namespace_identity(const void *a
, const void *b
)
835 const JSObject
*nsa
= (const JSObject
*) a
;
836 const JSObject
*nsb
= (const JSObject
*) b
;
837 JSString
*prefixa
= GetPrefix(nsa
);
838 JSString
*prefixb
= GetPrefix(nsb
);
840 if (prefixa
&& prefixb
) {
841 if (!js_EqualStrings(prefixa
, prefixb
))
844 if (prefixa
|| prefixb
)
847 return js_EqualStrings(GetURI(nsa
), GetURI(nsb
));
851 attr_identity(const void *a
, const void *b
)
853 const JSXML
*xmla
= (const JSXML
*) a
;
854 const JSXML
*xmlb
= (const JSXML
*) b
;
856 return qname_identity(xmla
->name
, xmlb
->name
);
860 XMLArrayCursorInit(JSXMLArrayCursor
*cursor
, JSXMLArray
*array
)
862 JSXMLArrayCursor
*next
;
864 cursor
->array
= array
;
866 next
= cursor
->next
= array
->cursors
;
868 next
->prevp
= &cursor
->next
;
869 cursor
->prevp
= &array
->cursors
;
870 array
->cursors
= cursor
;
875 XMLArrayCursorFinish(JSXMLArrayCursor
*cursor
)
877 JSXMLArrayCursor
*next
;
883 next
->prevp
= cursor
->prevp
;
884 *cursor
->prevp
= next
;
885 cursor
->array
= NULL
;
889 XMLArrayCursorNext(JSXMLArrayCursor
*cursor
)
893 array
= cursor
->array
;
894 if (!array
|| cursor
->index
>= array
->length
)
896 return cursor
->root
= array
->vector
[cursor
->index
++];
900 XMLArrayCursorItem(JSXMLArrayCursor
*cursor
)
904 array
= cursor
->array
;
905 if (!array
|| cursor
->index
>= array
->length
)
907 return cursor
->root
= array
->vector
[cursor
->index
];
911 XMLArrayCursorTrace(JSTracer
*trc
, JSXMLArrayCursor
*cursor
)
918 for (; cursor
; cursor
= cursor
->next
) {
920 JS_SET_TRACING_INDEX(trc
, "cursor_root", index
++);
921 js_CallValueTracerIfGCThing(trc
, (jsval
)root
);
925 /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
927 XMLArraySetCapacity(JSContext
*cx
, JSXMLArray
*array
, uint32 capacity
)
932 /* We could let realloc(p, 0) free this, but purify gets confused. */
938 #if JS_BITS_PER_WORD == 32
939 (size_t)capacity
> ~(size_t)0 / sizeof(void *) ||
942 realloc(array
->vector
, capacity
* sizeof(void *)))) {
944 JS_ReportOutOfMemory(cx
);
948 array
->capacity
= JSXML_PRESET_CAPACITY
| capacity
;
949 array
->vector
= vector
;
954 XMLArrayTrim(JSXMLArray
*array
)
956 if (array
->capacity
& JSXML_PRESET_CAPACITY
)
958 if (array
->length
< array
->capacity
)
959 XMLArraySetCapacity(NULL
, array
, array
->length
);
963 XMLArrayInit(JSContext
*cx
, JSXMLArray
*array
, uint32 capacity
)
965 array
->length
= array
->capacity
= 0;
966 array
->vector
= NULL
;
967 array
->cursors
= NULL
;
968 return capacity
== 0 || XMLArraySetCapacity(cx
, array
, capacity
);
972 XMLArrayFinish(JSContext
*cx
, JSXMLArray
*array
)
974 JSXMLArrayCursor
*cursor
;
976 JS_free(cx
, array
->vector
);
978 while ((cursor
= array
->cursors
) != NULL
)
979 XMLArrayCursorFinish(cursor
);
982 memset(array
, 0xd5, sizeof *array
);
986 #define XML_NOT_FOUND ((uint32) -1)
989 XMLArrayFindMember(const JSXMLArray
*array
, void *elt
, JSIdentityOp identity
)
994 /* The identity op must not reallocate array->vector. */
995 vector
= array
->vector
;
997 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
998 if (identity(vector
[i
], elt
))
1002 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
1003 if (vector
[i
] == elt
)
1007 return XML_NOT_FOUND
;
1011 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1012 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1013 * should be greater than increment.
1015 #define LINEAR_THRESHOLD 256
1016 #define LINEAR_INCREMENT 32
1019 XMLArrayAddMember(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, void *elt
)
1025 if (index
>= array
->length
) {
1026 if (index
>= JSXML_CAPACITY(array
)) {
1027 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1028 capacity
= index
+ 1;
1029 if (index
>= LINEAR_THRESHOLD
) {
1030 capacity
= JS_ROUNDUP(capacity
, LINEAR_INCREMENT
);
1032 JS_CEILING_LOG2(log2
, capacity
);
1033 capacity
= JS_BIT(log2
);
1036 #if JS_BITS_PER_WORD == 32
1037 (size_t)capacity
> ~(size_t)0 / sizeof(void *) ||
1039 !(vector
= (void **)
1040 realloc(array
->vector
, capacity
* sizeof(void *)))) {
1041 JS_ReportOutOfMemory(cx
);
1044 array
->capacity
= capacity
;
1045 array
->vector
= vector
;
1046 for (i
= array
->length
; i
< index
; i
++)
1049 array
->length
= index
+ 1;
1052 array
->vector
[index
] = elt
;
1057 XMLArrayInsert(JSContext
*cx
, JSXMLArray
*array
, uint32 i
, uint32 n
)
1060 JSXMLArrayCursor
*cursor
;
1064 if (!XMLArraySetCapacity(cx
, array
, j
+ n
))
1067 array
->length
= j
+ n
;
1068 JS_ASSERT(n
!= (uint32
)-1);
1071 array
->vector
[j
+ n
] = array
->vector
[j
];
1074 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1075 if (cursor
->index
> i
)
1082 XMLArrayDelete(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, JSBool compress
)
1085 void **vector
, *elt
;
1086 JSXMLArrayCursor
*cursor
;
1088 length
= array
->length
;
1089 if (index
>= length
)
1092 vector
= array
->vector
;
1093 elt
= vector
[index
];
1095 while (++index
< length
)
1096 vector
[index
-1] = vector
[index
];
1097 array
->length
= length
- 1;
1098 array
->capacity
= JSXML_CAPACITY(array
);
1100 vector
[index
] = NULL
;
1103 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1104 if (cursor
->index
> index
)
1111 XMLArrayTruncate(JSContext
*cx
, JSXMLArray
*array
, uint32 length
)
1115 JS_ASSERT(!array
->cursors
);
1116 if (length
>= array
->length
)
1121 free(array
->vector
);
1124 vector
= (void **) realloc(array
->vector
, length
* sizeof(void *));
1129 if (array
->length
> length
)
1130 array
->length
= length
;
1131 array
->capacity
= length
;
1132 array
->vector
= vector
;
1135 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1136 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1138 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1139 ? (t *) (a)->vector[i] \
1141 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1142 if ((a)->length <= (i)) \
1143 (a)->length = (i) + 1; \
1144 ((a)->vector[i] = (void *)(e)); \
1146 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1147 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1148 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1149 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1150 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1153 * Define XML setting property strings and constants early, so everyone can
1154 * use the same names and their magic numbers (tinyids, flags).
1156 static const char js_ignoreComments_str
[] = "ignoreComments";
1157 static const char js_ignoreProcessingInstructions_str
[]
1158 = "ignoreProcessingInstructions";
1159 static const char js_ignoreWhitespace_str
[] = "ignoreWhitespace";
1160 static const char js_prettyPrinting_str
[] = "prettyPrinting";
1161 static const char js_prettyIndent_str
[] = "prettyIndent";
1164 * NB: These XML static property tinyids must
1165 * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1166 * (b) index their corresponding xml_static_props array elements.
1169 enum xml_static_tinyid
{
1170 XML_IGNORE_COMMENTS
,
1171 XML_IGNORE_PROCESSING_INSTRUCTIONS
,
1172 XML_IGNORE_WHITESPACE
,
1173 XML_PRETTY_PRINTING
,
1178 xml_setting_getter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1184 xml_setting_setter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1188 JS_ASSERT(JSVAL_IS_INT(id
));
1190 flag
= JS_BIT(JSVAL_TO_INT(id
));
1191 if (js_ValueToBoolean(*vp
))
1192 cx
->xmlSettingFlags
|= flag
;
1194 cx
->xmlSettingFlags
&= ~flag
;
1198 static JSPropertySpec xml_static_props
[] = {
1199 {js_ignoreComments_str
, XML_IGNORE_COMMENTS
, JSPROP_PERMANENT
,
1200 xml_setting_getter
, xml_setting_setter
},
1201 {js_ignoreProcessingInstructions_str
,
1202 XML_IGNORE_PROCESSING_INSTRUCTIONS
, JSPROP_PERMANENT
,
1203 xml_setting_getter
, xml_setting_setter
},
1204 {js_ignoreWhitespace_str
, XML_IGNORE_WHITESPACE
, JSPROP_PERMANENT
,
1205 xml_setting_getter
, xml_setting_setter
},
1206 {js_prettyPrinting_str
, XML_PRETTY_PRINTING
, JSPROP_PERMANENT
,
1207 xml_setting_getter
, xml_setting_setter
},
1208 {js_prettyIndent_str
, XML_PRETTY_INDENT
, JSPROP_PERMANENT
,
1209 xml_setting_getter
, NULL
},
1213 /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1214 #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1215 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1216 JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1217 #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1218 #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1219 #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1222 * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1223 * This flag means a couple of things:
1225 * - The top JSXML created for a parse tree must have an object owning it.
1227 * - That the default namespace normally inherited from the temporary
1228 * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1229 * string must, in the case of a precompiled XML object tree, inherit via
1230 * ad-hoc code in ParseNodeToXML.
1232 * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1234 #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1236 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1237 #define IS_XML(str) \
1238 (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
1240 #define IS_XMLNS(str) \
1241 (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1243 #define IS_XML_CHARS(chars) \
1244 (JS_TOLOWER((chars)[0]) == 'x' && \
1245 JS_TOLOWER((chars)[1]) == 'm' && \
1246 JS_TOLOWER((chars)[2]) == 'l')
1248 #define HAS_NS_AFTER_XML(chars) \
1249 (JS_TOLOWER((chars)[3]) == 'n' && \
1250 JS_TOLOWER((chars)[4]) == 's')
1252 #define IS_XMLNS_CHARS(chars) \
1253 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1255 #define STARTS_WITH_XML(chars,length) \
1256 (length >= 3 && IS_XML_CHARS(chars))
1258 static const char xml_namespace_str
[] = "http://www.w3.org/XML/1998/namespace";
1259 static const char xmlns_namespace_str
[] = "http://www.w3.org/2000/xmlns/";
1262 ParseNodeToQName(JSContext
*cx
, JSParseContext
*pc
, JSParseNode
*pn
,
1263 JSXMLArray
*inScopeNSes
, JSBool isAttributeName
)
1265 JSString
*str
, *uri
, *prefix
, *localName
;
1266 size_t length
, offset
;
1267 const jschar
*start
, *limit
, *colon
;
1272 JS_ASSERT(pn
->pn_arity
== PN_NULLARY
);
1273 str
= ATOM_TO_STRING(pn
->pn_atom
);
1274 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
1275 JS_ASSERT(length
!= 0 && *start
!= '@');
1276 JS_ASSERT(length
!= 1 || *start
!= '*');
1278 uri
= cx
->runtime
->emptyString
;
1279 limit
= start
+ length
;
1280 colon
= js_strchr_limit(start
, ':', limit
);
1282 offset
= PTRDIFF(colon
, start
, jschar
);
1283 prefix
= js_NewDependentString(cx
, str
, 0, offset
);
1287 if (STARTS_WITH_XML(start
, offset
)) {
1289 uri
= JS_InternString(cx
, xml_namespace_str
);
1292 } else if (offset
== 5 && HAS_NS_AFTER_XML(start
)) {
1293 uri
= JS_InternString(cx
, xmlns_namespace_str
);
1301 n
= inScopeNSes
->length
;
1304 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSObject
);
1305 nsprefix
= GetPrefix(ns
);
1306 if (nsprefix
&& js_EqualStrings(nsprefix
, prefix
)) {
1314 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
,
1316 JSMSG_BAD_XML_NAMESPACE
,
1317 js_ValueToPrintableString(cx
,
1318 STRING_TO_JSVAL(prefix
)));
1322 localName
= js_NewStringCopyN(cx
, colon
+ 1, length
- (offset
+ 1));
1326 if (isAttributeName
) {
1328 * An unprefixed attribute is not in any namespace, so set prefix
1329 * as well as uri to the empty string.
1334 * Loop from back to front looking for the closest declared default
1337 n
= inScopeNSes
->length
;
1340 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSObject
);
1341 nsprefix
= GetPrefix(ns
);
1342 if (!nsprefix
|| IS_EMPTY(nsprefix
)) {
1347 prefix
= IS_EMPTY(uri
) ? cx
->runtime
->emptyString
: NULL
;
1352 return NewXMLQName(cx
, uri
, prefix
, localName
);
1356 ChompXMLWhitespace(JSContext
*cx
, JSString
*str
)
1358 size_t length
, newlength
, offset
;
1359 const jschar
*cp
, *start
, *end
;
1362 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
1363 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
1365 if (!JS_ISXMLSPACE(c
))
1370 if (!JS_ISXMLSPACE(c
))
1374 newlength
= PTRDIFF(end
, cp
, jschar
);
1375 if (newlength
== length
)
1377 offset
= PTRDIFF(cp
, start
, jschar
);
1378 return js_NewDependentString(cx
, str
, offset
, newlength
);
1382 ParseNodeToXML(JSContext
*cx
, JSParseContext
*pc
, JSParseNode
*pn
,
1383 JSXMLArray
*inScopeNSes
, uintN flags
)
1385 JSXML
*xml
, *kid
, *attr
, *attrj
;
1387 uint32 length
, n
, i
, j
;
1388 JSParseNode
*pn2
, *pn3
, *head
, **pnp
;
1390 JSObject
*qn
, *attrjqn
;
1391 JSXMLClass xml_class
;
1394 if (!JS_CHECK_STACK_SIZE(cx
, stackDummy
)) {
1395 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
, JSREPORT_ERROR
,
1396 JSMSG_OVER_RECURSED
);
1400 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1403 * Cases return early to avoid common code that gets an outermost xml's
1404 * object, which protects GC-things owned by xml and its descendants from
1405 * garbage collection.
1408 if (!js_EnterLocalRootScope(cx
))
1410 switch (pn
->pn_type
) {
1412 length
= inScopeNSes
->length
;
1414 xml
= ParseNodeToXML(cx
, pc
, pn2
, inScopeNSes
, flags
);
1418 flags
&= ~XSF_PRECOMPILED_ROOT
;
1422 if (!XMLArraySetCapacity(cx
, &xml
->xml_kids
, n
))
1426 while ((pn2
= pn2
->pn_next
) != NULL
) {
1427 if (!pn2
->pn_next
) {
1428 /* Don't append the end tag! */
1429 JS_ASSERT(pn2
->pn_type
== TOK_XMLETAGO
);
1433 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1434 n
> 1 && pn2
->pn_type
== TOK_XMLSPACE
) {
1439 kid
= ParseNodeToXML(cx
, pc
, pn2
, inScopeNSes
, flags
);
1440 if (kid
== PN2X_SKIP_CHILD
) {
1448 /* Store kid in xml right away, to protect it from GC. */
1449 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1453 /* XXX where is this documented in an XML spec, or in E4X? */
1454 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1455 n
> 1 && kid
->xml_class
== JSXML_CLASS_TEXT
) {
1456 str
= ChompXMLWhitespace(cx
, kid
->xml_value
);
1459 kid
->xml_value
= str
;
1464 if (n
< pn
->pn_count
- 2)
1465 XMLArrayTrim(&xml
->xml_kids
);
1466 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1470 xml
= js_NewXML(cx
, JSXML_CLASS_LIST
);
1475 if (!XMLArraySetCapacity(cx
, &xml
->xml_kids
, n
))
1479 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
1481 * Always ignore insignificant whitespace in lists -- we shouldn't
1482 * condition this on an XML.ignoreWhitespace setting when the list
1483 * constructor is XMLList (note XML/XMLList unification hazard).
1485 if (pn2
->pn_type
== TOK_XMLSPACE
) {
1490 kid
= ParseNodeToXML(cx
, pc
, pn2
, inScopeNSes
, flags
);
1491 if (kid
== PN2X_SKIP_CHILD
) {
1499 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1503 if (n
< pn
->pn_count
)
1504 XMLArrayTrim(&xml
->xml_kids
);
1509 length
= inScopeNSes
->length
;
1511 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
);
1512 if (pn2
->pn_arity
== PN_LIST
)
1515 xml
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
1519 /* First pass: check syntax and process namespace declarations. */
1520 JS_ASSERT(pn
->pn_count
>= 1);
1521 n
= pn
->pn_count
- 1;
1522 pnp
= &pn2
->pn_next
;
1524 while ((pn2
= *pnp
) != NULL
) {
1526 const jschar
*chars
;
1528 if (pn2
->pn_type
!= TOK_XMLNAME
|| pn2
->pn_arity
!= PN_NULLARY
)
1531 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1532 for (pn3
= head
; pn3
!= pn2
; pn3
= pn3
->pn_next
->pn_next
) {
1533 if (pn3
->pn_atom
== pn2
->pn_atom
) {
1534 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn2
,
1536 JSMSG_DUPLICATE_XML_ATTR
,
1537 js_ValueToPrintableString(cx
,
1538 ATOM_KEY(pn2
->pn_atom
)));
1543 str
= ATOM_TO_STRING(pn2
->pn_atom
);
1546 if (pn2
->pn_type
!= TOK_XMLATTR
)
1549 JSSTRING_CHARS_AND_LENGTH(str
, chars
, length
);
1551 IS_XMLNS_CHARS(chars
) &&
1552 (length
== 5 || chars
[5] == ':')) {
1553 JSString
*uri
, *prefix
;
1555 uri
= ATOM_TO_STRING(pn2
->pn_atom
);
1557 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1558 prefix
= cx
->runtime
->emptyString
;
1560 prefix
= js_NewStringCopyN(cx
, chars
+ 6, length
- 6);
1566 * Once the new ns is appended to xml->xml_namespaces, it is
1567 * protected from GC by the object that owns xml -- which is
1568 * either xml->object if outermost, or the object owning xml's
1569 * oldest ancestor if !outermost.
1571 ns
= NewXMLNamespace(cx
, prefix
, uri
, JS_TRUE
);
1576 * Don't add a namespace that's already in scope. If someone
1577 * extracts a child property from its parent via [[Get]], then
1578 * we enforce the invariant, noted many times in ECMA-357, that
1579 * the child's namespaces form a possibly-improper superset of
1580 * its ancestors' namespaces.
1582 if (!XMLARRAY_HAS_MEMBER(inScopeNSes
, ns
, namespace_identity
)) {
1583 if (!XMLARRAY_APPEND(cx
, inScopeNSes
, ns
) ||
1584 !XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
)) {
1591 *pnp
= pn2
->pn_next
;
1592 /* XXXbe recycle pn2 */
1596 pnp
= &pn2
->pn_next
;
1600 * If called from js_ParseNodeToXMLObject, emulate the effect of the
1601 * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1602 * the String Type" (ECMA-357 10.3.1).
1604 if (flags
& XSF_PRECOMPILED_ROOT
) {
1605 JS_ASSERT(length
>= 1);
1606 ns
= XMLARRAY_MEMBER(inScopeNSes
, 0, JSObject
);
1607 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml
->xml_namespaces
, ns
,
1608 namespace_identity
));
1609 ns
= NewXMLNamespace(cx
, GetPrefix(ns
), GetURI(ns
), JS_FALSE
);
1612 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
1615 XMLArrayTrim(&xml
->xml_namespaces
);
1617 /* Second pass: process tag name and attributes, using namespaces. */
1619 qn
= ParseNodeToQName(cx
, pc
, pn2
, inScopeNSes
, JS_FALSE
);
1624 JS_ASSERT((n
& 1) == 0);
1626 if (!XMLArraySetCapacity(cx
, &xml
->xml_attrs
, n
))
1629 for (i
= 0; (pn2
= pn2
->pn_next
) != NULL
; i
++) {
1630 qn
= ParseNodeToQName(cx
, pc
, pn2
, inScopeNSes
, JS_TRUE
);
1632 xml
->xml_attrs
.length
= i
;
1637 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1638 * this time checking local name and namespace URI.
1640 for (j
= 0; j
< i
; j
++) {
1641 attrj
= XMLARRAY_MEMBER(&xml
->xml_attrs
, j
, JSXML
);
1642 attrjqn
= attrj
->name
;
1643 if (js_EqualStrings(GetURI(attrjqn
), GetURI(qn
)) &&
1644 js_EqualStrings(GetLocalName(attrjqn
), GetLocalName(qn
))) {
1645 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn2
,
1647 JSMSG_DUPLICATE_XML_ATTR
,
1648 js_ValueToPrintableString(cx
,
1649 ATOM_KEY(pn2
->pn_atom
)));
1656 JS_ASSERT(pn2
->pn_type
== TOK_XMLATTR
);
1658 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
1662 XMLARRAY_SET_MEMBER(&xml
->xml_attrs
, i
, attr
);
1665 attr
->xml_value
= ATOM_TO_STRING(pn2
->pn_atom
);
1668 /* Point tag closes its own namespace scope. */
1669 if (pn
->pn_type
== TOK_XMLPTAGC
)
1670 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1676 case TOK_XMLCOMMENT
:
1678 str
= ATOM_TO_STRING(pn
->pn_atom
);
1680 if (pn
->pn_type
== TOK_XMLCOMMENT
) {
1681 if (flags
& XSF_IGNORE_COMMENTS
)
1683 xml_class
= JSXML_CLASS_COMMENT
;
1684 } else if (pn
->pn_type
== TOK_XMLPI
) {
1686 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
,
1689 js_ValueToPrintableString(cx
,
1690 STRING_TO_JSVAL(str
)));
1694 if (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
)
1697 qn
= ParseNodeToQName(cx
, pc
, pn
, inScopeNSes
, JS_FALSE
);
1702 ? ATOM_TO_STRING(pn
->pn_atom2
)
1703 : cx
->runtime
->emptyString
;
1704 xml_class
= JSXML_CLASS_PROCESSING_INSTRUCTION
;
1706 /* CDATA section content, or element text. */
1707 xml_class
= JSXML_CLASS_TEXT
;
1710 xml
= js_NewXML(cx
, xml_class
);
1714 if (pn
->pn_type
== TOK_XMLSPACE
)
1715 xml
->xml_flags
|= XMLF_WHITESPACE_TEXT
;
1716 xml
->xml_value
= str
;
1723 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) xml
);
1724 if ((flags
& XSF_PRECOMPILED_ROOT
) && !js_GetXMLObject(cx
, xml
))
1729 js_LeaveLocalRootScope(cx
);
1730 return PN2X_SKIP_CHILD
;
1732 #undef PN2X_SKIP_CHILD
1735 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
, JSREPORT_ERROR
,
1736 JSMSG_BAD_XML_MARKUP
);
1738 js_LeaveLocalRootScope(cx
);
1743 * XML helper, object-ops, and library functions. We start with the helpers,
1744 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1747 GetXMLSetting(JSContext
*cx
, const char *name
, jsval
*vp
)
1751 if (!js_FindClassObject(cx
, NULL
, INT_TO_JSID(JSProto_XML
), &v
))
1753 if (!VALUE_IS_FUNCTION(cx
, v
)) {
1757 return JS_GetProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
);
1761 FillSettingsCache(JSContext
*cx
)
1767 /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1768 for (i
= XML_IGNORE_COMMENTS
; i
< XML_PRETTY_INDENT
; i
++) {
1769 name
= xml_static_props
[i
].name
;
1770 if (!GetXMLSetting(cx
, name
, &v
))
1772 if (js_ValueToBoolean(v
))
1773 cx
->xmlSettingFlags
|= JS_BIT(i
);
1775 cx
->xmlSettingFlags
&= ~JS_BIT(i
);
1778 cx
->xmlSettingFlags
|= XSF_CACHE_VALID
;
1783 GetBooleanXMLSetting(JSContext
*cx
, const char *name
, JSBool
*bp
)
1787 if (!(cx
->xmlSettingFlags
& XSF_CACHE_VALID
) && !FillSettingsCache(cx
))
1790 for (i
= 0; xml_static_props
[i
].name
; i
++) {
1791 if (!strcmp(xml_static_props
[i
].name
, name
)) {
1792 *bp
= (cx
->xmlSettingFlags
& JS_BIT(i
)) != 0;
1801 GetUint32XMLSetting(JSContext
*cx
, const char *name
, uint32
*uip
)
1805 return GetXMLSetting(cx
, name
, &v
) && JS_ValueToECMAUint32(cx
, v
, uip
);
1809 GetXMLSettingFlags(JSContext
*cx
, uintN
*flagsp
)
1813 /* Just get the first flag to validate the setting flags cache. */
1814 if (!GetBooleanXMLSetting(cx
, js_ignoreComments_str
, &flag
))
1816 *flagsp
= cx
->xmlSettingFlags
;
1821 ParseXMLSource(JSContext
*cx
, JSString
*src
)
1825 size_t urilen
, srclen
, length
, offset
, dstlen
;
1827 const jschar
*srcp
, *endp
;
1830 const char *filename
;
1838 static const char prefix
[] = "<parent xmlns=\"";
1839 static const char middle
[] = "\">";
1840 static const char suffix
[] = "</parent>";
1842 #define constrlen(constr) (sizeof(constr) - 1)
1844 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
1846 uri
= GetURI(JSVAL_TO_OBJECT(nsval
));
1847 uri
= js_EscapeAttributeValue(cx
, uri
, JS_FALSE
);
1849 urilen
= JSSTRING_LENGTH(uri
);
1850 srclen
= JSSTRING_LENGTH(src
);
1851 length
= constrlen(prefix
) + urilen
+ constrlen(middle
) + srclen
+
1854 chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
1859 js_InflateStringToBuffer(cx
, prefix
, constrlen(prefix
), chars
, &dstlen
);
1861 js_strncpy(chars
+ offset
, JSSTRING_CHARS(uri
), urilen
);
1863 dstlen
= length
- offset
+ 1;
1864 js_InflateStringToBuffer(cx
, middle
, constrlen(middle
), chars
+ offset
,
1867 srcp
= JSSTRING_CHARS(src
);
1868 js_strncpy(chars
+ offset
, srcp
, srclen
);
1870 dstlen
= length
- offset
+ 1;
1871 js_InflateStringToBuffer(cx
, suffix
, constrlen(suffix
), chars
+ offset
,
1873 chars
[offset
+ dstlen
] = 0;
1876 for (fp
= js_GetTopStackFrame(cx
); fp
&& !fp
->regs
; fp
= fp
->down
)
1877 JS_ASSERT(!fp
->script
);
1881 op
= (JSOp
) *fp
->regs
->pc
;
1882 if (op
== JSOP_TOXML
|| op
== JSOP_TOXMLLIST
) {
1883 filename
= fp
->script
->filename
;
1884 lineno
= js_FramePCToLineNumber(cx
, fp
);
1885 for (endp
= srcp
+ srclen
; srcp
< endp
; srcp
++) {
1892 if (!js_InitParseContext(cx
, &pc
, NULL
, NULL
, chars
, length
, NULL
,
1895 pn
= js_ParseXMLText(cx
, js_GetTopStackFrame(cx
)->scopeChain
, &pc
,
1897 if (pn
&& XMLArrayInit(cx
, &nsarray
, 1)) {
1898 if (GetXMLSettingFlags(cx
, &flags
))
1899 xml
= ParseNodeToXML(cx
, &pc
, pn
, &nsarray
, flags
);
1901 XMLArrayFinish(cx
, &nsarray
);
1903 js_FinishParseContext(cx
, &pc
);
1913 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1915 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1918 * for all x belonging to XML:
1919 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1921 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1922 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1924 * Same goes for 10.4.1 Step 7(a).
1926 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1927 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1928 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1929 * undeclared namespaces associated with x not belonging to ancestorNS.
1932 OrphanXMLChild(JSContext
*cx
, JSXML
*xml
, uint32 i
)
1936 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, 0, JSObject
);
1937 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
1940 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
1941 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
1943 ns
->fslots
[JSSLOT_DECLARED
] = JSVAL_VOID
;
1950 ToXML(JSContext
*cx
, jsval v
)
1958 if (JSVAL_IS_PRIMITIVE(v
)) {
1959 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
1962 obj
= JSVAL_TO_OBJECT(v
);
1963 if (OBJECT_IS_XML(cx
, obj
)) {
1964 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
1965 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
1966 if (xml
->xml_kids
.length
!= 1)
1968 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
1970 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
1971 return js_GetXMLObject(cx
, xml
);
1977 clasp
= OBJ_GET_CLASS(cx
, obj
);
1978 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
1982 if (clasp
!= &js_StringClass
&&
1983 clasp
!= &js_NumberClass
&&
1984 clasp
!= &js_BooleanClass
) {
1989 str
= js_ValueToString(cx
, v
);
1992 if (IS_EMPTY(str
)) {
1994 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1998 xml
= ParseXMLSource(cx
, str
);
2001 length
= JSXML_LENGTH(xml
);
2005 obj
= js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
2008 } else if (length
== 1) {
2009 xml
= OrphanXMLChild(cx
, xml
, 0);
2012 obj
= js_GetXMLObject(cx
, xml
);
2016 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_SYNTAX_ERROR
);
2022 js_ReportValueError(cx
, JSMSG_BAD_XML_CONVERSION
,
2023 JSDVG_IGNORE_STACK
, v
, NULL
);
2028 Append(JSContext
*cx
, JSXML
*list
, JSXML
*kid
);
2031 ToXMLList(JSContext
*cx
, jsval v
)
2033 JSObject
*obj
, *listobj
;
2034 JSXML
*xml
, *list
, *kid
;
2039 if (JSVAL_IS_PRIMITIVE(v
)) {
2040 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
2043 obj
= JSVAL_TO_OBJECT(v
);
2044 if (OBJECT_IS_XML(cx
, obj
)) {
2045 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
2046 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
2047 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
2050 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
2051 if (!Append(cx
, list
, xml
))
2058 clasp
= OBJ_GET_CLASS(cx
, obj
);
2059 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
2063 if (clasp
!= &js_StringClass
&&
2064 clasp
!= &js_NumberClass
&&
2065 clasp
!= &js_BooleanClass
) {
2070 str
= js_ValueToString(cx
, v
);
2073 if (IS_EMPTY(str
)) {
2077 if (!js_EnterLocalRootScope(cx
))
2079 xml
= ParseXMLSource(cx
, str
);
2081 js_LeaveLocalRootScope(cx
);
2084 length
= JSXML_LENGTH(xml
);
2087 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
2089 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
2090 for (i
= 0; i
< length
; i
++) {
2091 kid
= OrphanXMLChild(cx
, xml
, i
);
2092 if (!kid
|| !Append(cx
, list
, kid
)) {
2100 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) listobj
);
2104 js_ReportValueError(cx
, JSMSG_BAD_XMLLIST_CONVERSION
,
2105 JSDVG_IGNORE_STACK
, v
, NULL
);
2110 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2111 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2112 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2113 * MakeXMLSpecialString subroutine.
2115 * These functions take ownership of sb->base, if sb is non-null, in all cases
2116 * of success or failure.
2119 MakeXMLSpecialString(JSContext
*cx
, JSStringBuffer
*sb
,
2120 JSString
*str
, JSString
*str2
,
2121 const jschar
*prefix
, size_t prefixlength
,
2122 const jschar
*suffix
, size_t suffixlength
)
2124 JSStringBuffer localSB
;
2125 size_t length
, length2
, newlength
;
2130 js_InitStringBuffer(sb
);
2133 length
= JSSTRING_LENGTH(str
);
2134 length2
= str2
? JSSTRING_LENGTH(str2
) : 0;
2135 newlength
= STRING_BUFFER_OFFSET(sb
) +
2136 prefixlength
+ length
+ ((length2
!= 0) ? 1 + length2
: 0) +
2138 bp
= base
= (jschar
*)
2139 JS_realloc(cx
, sb
->base
, (newlength
+ 1) * sizeof(jschar
));
2141 js_FinishStringBuffer(sb
);
2145 bp
+= STRING_BUFFER_OFFSET(sb
);
2146 js_strncpy(bp
, prefix
, prefixlength
);
2148 js_strncpy(bp
, JSSTRING_CHARS(str
), length
);
2151 *bp
++ = (jschar
) ' ';
2152 js_strncpy(bp
, JSSTRING_CHARS(str2
), length2
);
2155 js_strncpy(bp
, suffix
, suffixlength
);
2156 bp
[suffixlength
] = 0;
2158 str
= js_NewString(cx
, base
, newlength
);
2165 MakeXMLCDATAString(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
)
2167 static const jschar cdata_prefix_ucNstr
[] = {'<', '!', '[',
2168 'C', 'D', 'A', 'T', 'A',
2170 static const jschar cdata_suffix_ucNstr
[] = {']', ']', '>'};
2172 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2173 cdata_prefix_ucNstr
, 9,
2174 cdata_suffix_ucNstr
, 3);
2178 MakeXMLCommentString(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
)
2180 static const jschar comment_prefix_ucNstr
[] = {'<', '!', '-', '-'};
2181 static const jschar comment_suffix_ucNstr
[] = {'-', '-', '>'};
2183 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2184 comment_prefix_ucNstr
, 4,
2185 comment_suffix_ucNstr
, 3);
2189 MakeXMLPIString(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*name
,
2192 static const jschar pi_prefix_ucNstr
[] = {'<', '?'};
2193 static const jschar pi_suffix_ucNstr
[] = {'?', '>'};
2195 return MakeXMLSpecialString(cx
, sb
, name
, value
,
2196 pi_prefix_ucNstr
, 2,
2197 pi_suffix_ucNstr
, 2);
2201 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2202 * equals, a double quote, an attribute value, and a closing double quote.
2205 AppendAttributeValue(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*valstr
)
2207 js_AppendChar(sb
, '=');
2208 valstr
= js_EscapeAttributeValue(cx
, valstr
, JS_TRUE
);
2210 if (STRING_BUFFER_OK(sb
)) {
2212 sb
->base
= STRING_BUFFER_ERROR_BASE
;
2216 js_AppendJSString(sb
, valstr
);
2220 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2222 * This function takes ownership of sb->base, if sb is non-null, in all cases
2223 * of success or failure.
2226 EscapeElementValue(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
)
2228 size_t length
, newlength
;
2229 const jschar
*cp
, *start
, *end
;
2232 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
2234 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
2236 if (c
== '<' || c
== '>')
2241 if (newlength
< length
) {
2242 js_ReportAllocationOverflow(cx
);
2246 if ((sb
&& STRING_BUFFER_OFFSET(sb
) != 0) || newlength
> length
) {
2247 JSStringBuffer localSB
;
2250 js_InitStringBuffer(sb
);
2252 if (!sb
->grow(sb
, newlength
)) {
2253 JS_ReportOutOfMemory(cx
);
2256 for (cp
= start
; cp
< end
; cp
++) {
2259 js_AppendCString(sb
, js_lt_entity_str
);
2261 js_AppendCString(sb
, js_gt_entity_str
);
2263 js_AppendCString(sb
, js_amp_entity_str
);
2265 js_AppendChar(sb
, c
);
2267 JS_ASSERT(STRING_BUFFER_OK(sb
));
2268 str
= js_NewString(cx
, sb
->base
, STRING_BUFFER_OFFSET(sb
));
2270 js_FinishStringBuffer(sb
);
2276 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2277 * This function takes ownership of sb->base, if sb is non-null, in all cases.
2280 EscapeAttributeValue(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
,
2283 size_t length
, newlength
;
2284 const jschar
*cp
, *start
, *end
;
2287 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
2288 newlength
= length
+ (quote
? 2 : 0);
2289 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
2295 else if (c
== '&' || c
== '\n' || c
== '\r' || c
== '\t')
2298 if (newlength
< length
) {
2299 js_ReportAllocationOverflow(cx
);
2303 if ((sb
&& STRING_BUFFER_OFFSET(sb
) != 0) || newlength
> length
) {
2304 JSStringBuffer localSB
;
2307 js_InitStringBuffer(sb
);
2309 if (!sb
->grow(sb
, newlength
)) {
2310 JS_ReportOutOfMemory(cx
);
2314 js_AppendChar(sb
, '"');
2315 for (cp
= start
; cp
< end
; cp
++) {
2318 js_AppendCString(sb
, js_quot_entity_str
);
2320 js_AppendCString(sb
, js_lt_entity_str
);
2322 js_AppendCString(sb
, js_amp_entity_str
);
2324 js_AppendCString(sb
, "
");
2326 js_AppendCString(sb
, "
");
2328 js_AppendCString(sb
, "	");
2330 js_AppendChar(sb
, c
);
2333 js_AppendChar(sb
, '"');
2334 JS_ASSERT(STRING_BUFFER_OK(sb
));
2335 str
= js_NewString(cx
, sb
->base
, STRING_BUFFER_OFFSET(sb
));
2337 js_FinishStringBuffer(sb
);
2342 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2344 GetNamespace(JSContext
*cx
, JSObject
*qn
, const JSXMLArray
*inScopeNSes
)
2346 JSString
*uri
, *prefix
, *nsprefix
;
2347 JSObject
*match
, *ns
;
2352 prefix
= GetPrefix(qn
);
2355 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2356 JSMSG_BAD_XML_NAMESPACE
,
2358 ? js_ValueToPrintableString(cx
,
2359 STRING_TO_JSVAL(prefix
))
2360 : js_undefined_str
);
2364 /* Look for a matching namespace in inScopeNSes, if provided. */
2367 for (i
= 0, n
= inScopeNSes
->length
; i
< n
; i
++) {
2368 ns
= XMLARRAY_MEMBER(inScopeNSes
, i
, JSObject
);
2373 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2374 * If we preserve prefixes, we must match null prefix against
2375 * an empty prefix of ns, in order to avoid generating redundant
2376 * prefixed and default namespaces for cases such as:
2378 * x = <t xmlns="http://foo.com"/>
2379 * print(x.toXMLString());
2381 * Per 10.3.2.1, the namespace attribute in t has an empty string
2382 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2384 * 1. If the [local name] property of a is "xmlns"
2385 * a. Map ns.prefix to the empty string
2387 * But t's name has a null prefix in this implementation, meaning
2388 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2389 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2390 * saying how "no value" maps to an ECMA-357 value -- but it must
2391 * map to the *undefined* prefix value).
2393 * Since "" != undefined (or null, in the current implementation)
2394 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2395 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2396 * This spec bug leads to ToXMLString results that duplicate the
2397 * declared namespace.
2399 if (js_EqualStrings(GetURI(ns
), uri
)) {
2400 nsprefix
= GetPrefix(ns
);
2401 if (nsprefix
== prefix
||
2402 ((nsprefix
&& prefix
)
2403 ? js_EqualStrings(nsprefix
, prefix
)
2404 : IS_EMPTY(nsprefix
? nsprefix
: prefix
))) {
2412 /* If we didn't match, make a new namespace from qn. */
2414 argv
[0] = prefix
? STRING_TO_JSVAL(prefix
) : JSVAL_VOID
;
2415 argv
[1] = STRING_TO_JSVAL(uri
);
2416 ns
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
,
2426 GeneratePrefix(JSContext
*cx
, JSString
*uri
, JSXMLArray
*decls
)
2428 const jschar
*cp
, *start
, *end
;
2429 size_t length
, newlength
, offset
;
2430 uint32 i
, n
, m
, serial
;
2434 JSString
*nsprefix
, *prefix
;
2436 JS_ASSERT(!IS_EMPTY(uri
));
2439 * If there are no *declared* namespaces, skip all collision detection and
2440 * return a short prefix quickly; an example of such a situation:
2443 * var n = new Namespace("http://example.com/");
2444 * x.@n::att = "val";
2447 * This is necessary for various log10 uses below to be valid.
2449 if (decls
->length
== 0)
2450 return JS_NewStringCopyZ(cx
, "a");
2453 * Try peeling off the last filename suffix or pathname component till
2454 * we have a valid XML name. This heuristic will prefer "xul" given
2455 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2456 * likely URI of the form ".../xbl2/2005".
2458 JSSTRING_CHARS_AND_END(uri
, start
, end
);
2460 while (--cp
> start
) {
2461 if (*cp
== '.' || *cp
== '/' || *cp
== ':') {
2463 length
= PTRDIFF(end
, cp
, jschar
);
2464 if (IsXMLName(cp
, length
) && !STARTS_WITH_XML(cp
, length
))
2469 length
= PTRDIFF(end
, cp
, jschar
);
2472 * If the namespace consisted only of non-XML names or names that begin
2473 * case-insensitively with "xml", arbitrarily create a prefix consisting
2474 * of 'a's of size length (allowing dp-calculating code to work with or
2475 * without this branch executing) plus the space for storing a hyphen and
2476 * the serial number (avoiding reallocation if a collision happens).
2480 if (STARTS_WITH_XML(cp
, length
) || !IsXMLName(cp
, length
)) {
2481 newlength
= length
+ 2 + (size_t) log10((double) decls
->length
);
2483 JS_malloc(cx
, (newlength
+ 1) * sizeof(jschar
));
2488 for (i
= 0; i
< newlength
; i
++)
2493 * Now search through decls looking for a collision. If we collide with
2494 * an existing prefix, start tacking on a hyphen and a serial number.
2499 for (i
= 0, n
= decls
->length
; i
< n
; i
++) {
2500 ns
= XMLARRAY_MEMBER(decls
, i
, JSObject
);
2501 if (ns
&& (nsprefix
= GetPrefix(ns
)) &&
2502 JSSTRING_LENGTH(nsprefix
) == newlength
&&
2503 !memcmp(JSSTRING_CHARS(nsprefix
), bp
,
2504 newlength
* sizeof(jschar
))) {
2506 newlength
= length
+ 2 + (size_t) log10((double) n
);
2508 JS_malloc(cx
, (newlength
+ 1) * sizeof(jschar
));
2511 js_strncpy(bp
, cp
, length
);
2515 JS_ASSERT(serial
<= n
);
2516 dp
= bp
+ length
+ 2 + (size_t) log10((double) serial
);
2518 for (m
= serial
; m
!= 0; m
/= 10)
2519 *--dp
= (jschar
)('0' + m
% 10);
2521 JS_ASSERT(dp
== bp
+ length
);
2530 offset
= PTRDIFF(cp
, start
, jschar
);
2531 prefix
= js_NewDependentString(cx
, uri
, offset
, length
);
2533 prefix
= js_NewString(cx
, bp
, newlength
);
2541 namespace_match(const void *a
, const void *b
)
2543 const JSObject
*nsa
= (const JSObject
*) a
;
2544 const JSObject
*nsb
= (const JSObject
*) b
;
2545 JSString
*prefixa
, *prefixb
= GetPrefix(nsb
);
2548 prefixa
= GetPrefix(nsa
);
2549 return prefixa
&& js_EqualStrings(prefixa
, prefixb
);
2551 return js_EqualStrings(GetURI(nsa
), GetURI(nsb
));
2554 /* ECMA-357 10.2.1 and 10.2.2 */
2555 #define TO_SOURCE_FLAG 0x80000000
2558 XMLToXMLString(JSContext
*cx
, JSXML
*xml
, const JSXMLArray
*ancestorNSes
,
2561 JSBool pretty
, indentKids
;
2563 JSString
*str
, *prefix
, *kidstr
, *nsuri
;
2564 JSXMLArrayCursor cursor
;
2565 uint32 i
, n
, nextIndentLevel
;
2566 JSXMLArray empty
, decls
, ancdecls
;
2570 if (!GetBooleanXMLSetting(cx
, js_prettyPrinting_str
, &pretty
))
2573 js_InitStringBuffer(&sb
);
2575 js_RepeatChar(&sb
, ' ', indentLevel
& ~TO_SOURCE_FLAG
);
2577 if (!STRING_BUFFER_OK(&sb
)) {
2578 JS_ReportOutOfMemory(cx
);
2584 switch (xml
->xml_class
) {
2585 case JSXML_CLASS_TEXT
:
2588 str
= ChompXMLWhitespace(cx
, xml
->xml_value
);
2592 str
= xml
->xml_value
;
2594 return EscapeElementValue(cx
, &sb
, str
);
2596 case JSXML_CLASS_ATTRIBUTE
:
2598 return EscapeAttributeValue(cx
, &sb
, xml
->xml_value
,
2599 (indentLevel
& TO_SOURCE_FLAG
) != 0);
2601 case JSXML_CLASS_COMMENT
:
2603 return MakeXMLCommentString(cx
, &sb
, xml
->xml_value
);
2605 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
2607 return MakeXMLPIString(cx
, &sb
, GetLocalName(xml
->name
),
2610 case JSXML_CLASS_LIST
:
2611 /* ECMA-357 10.2.2. */
2612 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
2614 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2615 if (pretty
&& i
!= 0)
2616 js_AppendChar(&sb
, '\n');
2618 kidstr
= XMLToXMLString(cx
, kid
, ancestorNSes
, indentLevel
);
2622 js_AppendJSString(&sb
, kidstr
);
2625 XMLArrayCursorFinish(&cursor
);
2630 return cx
->runtime
->emptyString
;
2632 if (!STRING_BUFFER_OK(&sb
)) {
2633 JS_ReportOutOfMemory(cx
);
2637 str
= js_NewString(cx
, sb
.base
, STRING_BUFFER_OFFSET(&sb
));
2639 if (!str
&& STRING_BUFFER_OK(&sb
))
2640 js_FinishStringBuffer(&sb
);
2646 /* After this point, control must flow through label out: to exit. */
2647 if (!js_EnterLocalRootScope(cx
))
2650 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2651 if (!ancestorNSes
) {
2652 XMLArrayInit(cx
, &empty
, 0);
2653 ancestorNSes
= &empty
;
2655 XMLArrayInit(cx
, &decls
, 0);
2656 ancdecls
.capacity
= 0;
2658 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2659 XMLArrayCursorInit(&cursor
, &xml
->xml_namespaces
);
2660 while ((ns
= (JSObject
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2661 if (!IsDeclared(ns
))
2663 if (!XMLARRAY_HAS_MEMBER(ancestorNSes
, ns
, namespace_identity
)) {
2664 /* NOTE: may want to exclude unused namespaces here. */
2665 ns2
= NewXMLNamespace(cx
, GetPrefix(ns
), GetURI(ns
), JS_TRUE
);
2666 if (!ns2
|| !XMLARRAY_APPEND(cx
, &decls
, ns2
))
2670 XMLArrayCursorFinish(&cursor
);
2675 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2676 * not own its member references. In the spec, ancdecls has no name, but
2677 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2679 if (!XMLArrayInit(cx
, &ancdecls
, ancestorNSes
->length
+ decls
.length
))
2681 for (i
= 0, n
= ancestorNSes
->length
; i
< n
; i
++) {
2682 ns2
= XMLARRAY_MEMBER(ancestorNSes
, i
, JSObject
);
2685 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls
, ns2
, namespace_identity
));
2686 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns2
))
2689 for (i
= 0, n
= decls
.length
; i
< n
; i
++) {
2690 ns2
= XMLARRAY_MEMBER(&decls
, i
, JSObject
);
2693 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls
, ns2
, namespace_identity
));
2694 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns2
))
2698 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2699 ns
= GetNamespace(cx
, xml
->name
, &ancdecls
);
2703 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2704 prefix
= GetPrefix(ns
);
2707 * Create a namespace prefix that isn't used by any member of decls.
2708 * Assign the new prefix to a copy of ns. Flag this namespace as if
2709 * it were declared, for assertion-testing's sake later below.
2711 * Erratum: if prefix and xml->name are both null (*undefined* in
2712 * ECMA-357), we know that xml was named using the default namespace
2713 * (proof: see GetNamespace and the Namespace constructor called with
2714 * two arguments). So we ought not generate a new prefix here, when
2715 * we can declare ns as the default namespace for xml.
2717 * This helps descendants inherit the namespace instead of redundantly
2718 * redeclaring it with generated prefixes in each descendant.
2721 if (!GetPrefix(xml
->name
)) {
2722 prefix
= cx
->runtime
->emptyString
;
2724 prefix
= GeneratePrefix(cx
, nsuri
, &ancdecls
);
2728 ns
= NewXMLNamespace(cx
, prefix
, nsuri
, JS_TRUE
);
2733 * If the xml->name was unprefixed, we must remove any declared default
2734 * namespace from decls before appending ns. How can you get a default
2735 * namespace in decls that doesn't match the one from name? Apparently
2736 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2737 * to fix this is to update x's in-scope namespaces when setNamespace
2738 * is called, but that's not specified by ECMA-357.
2740 * Likely Erratum here, depending on whether the lack of update to x's
2741 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2742 * erratum or not. Note that changing setNamespace to update the list
2743 * of in-scope namespaces will change x.namespaceDeclarations().
2745 if (IS_EMPTY(prefix
)) {
2746 i
= XMLArrayFindMember(&decls
, ns
, namespace_match
);
2747 if (i
!= XML_NOT_FOUND
)
2748 XMLArrayDelete(cx
, &decls
, i
, JS_TRUE
);
2752 * In the spec, ancdecls has no name, but is always written out as
2753 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2754 * that union in ancdecls, any time we append a namespace strong
2755 * ref to decls, we must also append a weak ref to ancdecls. Order
2756 * matters here: code at label out: releases strong refs in decls.
2758 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns
) ||
2759 !XMLARRAY_APPEND(cx
, &decls
, ns
)) {
2764 /* Format the element or point-tag into sb. */
2765 js_AppendChar(&sb
, '<');
2767 if (prefix
&& !IS_EMPTY(prefix
)) {
2768 js_AppendJSString(&sb
, prefix
);
2769 js_AppendChar(&sb
, ':');
2771 js_AppendJSString(&sb
, GetLocalName(xml
->name
));
2774 * Step 16 makes a union to avoid writing two loops in step 17, to share
2775 * common attribute value appending spec-code. We prefer two loops for
2776 * faster code and less data overhead.
2779 /* Step 17(b): append attributes. */
2780 XMLArrayCursorInit(&cursor
, &xml
->xml_attrs
);
2781 while ((attr
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2782 js_AppendChar(&sb
, ' ');
2783 ns2
= GetNamespace(cx
, attr
->name
, &ancdecls
);
2787 /* 17(b)(ii): NULL means *undefined* here. */
2788 prefix
= GetPrefix(ns2
);
2790 prefix
= GeneratePrefix(cx
, GetURI(ns2
), &ancdecls
);
2794 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2795 ns2
= NewXMLNamespace(cx
, prefix
, GetURI(ns2
), JS_TRUE
);
2800 * In the spec, ancdecls has no name, but is always written out as
2801 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2802 * that union in ancdecls, any time we append a namespace strong
2803 * ref to decls, we must also append a weak ref to ancdecls. Order
2804 * matters here: code at label out: releases strong refs in decls.
2806 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns2
) ||
2807 !XMLARRAY_APPEND(cx
, &decls
, ns2
)) {
2813 if (!IS_EMPTY(prefix
)) {
2814 js_AppendJSString(&sb
, prefix
);
2815 js_AppendChar(&sb
, ':');
2819 js_AppendJSString(&sb
, GetLocalName(attr
->name
));
2822 AppendAttributeValue(cx
, &sb
, attr
->xml_value
);
2824 XMLArrayCursorFinish(&cursor
);
2828 /* Step 17(c): append XML namespace declarations. */
2829 XMLArrayCursorInit(&cursor
, &decls
);
2830 while ((ns2
= (JSObject
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2831 JS_ASSERT(IsDeclared(ns2
));
2833 js_AppendCString(&sb
, " xmlns");
2835 /* 17(c)(ii): NULL means *undefined* here. */
2836 prefix
= GetPrefix(ns2
);
2838 prefix
= GeneratePrefix(cx
, GetURI(ns2
), &ancdecls
);
2841 ns2
->fslots
[JSSLOT_PREFIX
] = STRING_TO_JSVAL(prefix
);
2845 if (!IS_EMPTY(prefix
)) {
2846 js_AppendChar(&sb
, ':');
2847 js_AppendJSString(&sb
, prefix
);
2851 AppendAttributeValue(cx
, &sb
, GetURI(ns2
));
2853 XMLArrayCursorFinish(&cursor
);
2857 /* Step 18: handle point tags. */
2858 n
= xml
->xml_kids
.length
;
2860 js_AppendCString(&sb
, "/>");
2862 /* Steps 19 through 25: handle element content, and open the end-tag. */
2863 js_AppendChar(&sb
, '>');
2864 indentKids
= n
> 1 ||
2866 (kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
)) &&
2867 kid
->xml_class
!= JSXML_CLASS_TEXT
);
2869 if (pretty
&& indentKids
) {
2870 if (!GetUint32XMLSetting(cx
, js_prettyIndent_str
, &i
))
2872 nextIndentLevel
= indentLevel
+ i
;
2874 nextIndentLevel
= indentLevel
& TO_SOURCE_FLAG
;
2877 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
2878 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2879 if (pretty
&& indentKids
)
2880 js_AppendChar(&sb
, '\n');
2882 kidstr
= XMLToXMLString(cx
, kid
, &ancdecls
, nextIndentLevel
);
2886 js_AppendJSString(&sb
, kidstr
);
2888 XMLArrayCursorFinish(&cursor
);
2892 if (pretty
&& indentKids
) {
2893 js_AppendChar(&sb
, '\n');
2894 js_RepeatChar(&sb
, ' ', indentLevel
& ~TO_SOURCE_FLAG
);
2896 js_AppendCString(&sb
, "</");
2899 prefix
= GetPrefix(ns
);
2900 if (prefix
&& !IS_EMPTY(prefix
)) {
2901 js_AppendJSString(&sb
, prefix
);
2902 js_AppendChar(&sb
, ':');
2906 js_AppendJSString(&sb
, GetLocalName(xml
->name
));
2907 js_AppendChar(&sb
, '>');
2910 if (!STRING_BUFFER_OK(&sb
)) {
2911 JS_ReportOutOfMemory(cx
);
2915 str
= js_NewString(cx
, sb
.base
, STRING_BUFFER_OFFSET(&sb
));
2917 js_LeaveLocalRootScopeWithResult(cx
, STRING_TO_JSVAL(str
));
2918 if (!str
&& STRING_BUFFER_OK(&sb
))
2919 js_FinishStringBuffer(&sb
);
2920 XMLArrayFinish(cx
, &decls
);
2921 if (ancdecls
.capacity
!= 0)
2922 XMLArrayFinish(cx
, &ancdecls
);
2928 ToXMLString(JSContext
*cx
, jsval v
, uint32 toSourceFlag
)
2934 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
2935 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2936 JSMSG_BAD_XML_CONVERSION
,
2937 JSVAL_IS_NULL(v
) ? js_null_str
: js_undefined_str
);
2941 if (JSVAL_IS_BOOLEAN(v
) || JSVAL_IS_NUMBER(v
))
2942 return js_ValueToString(cx
, v
);
2944 if (JSVAL_IS_STRING(v
))
2945 return EscapeElementValue(cx
, NULL
, JSVAL_TO_STRING(v
));
2947 obj
= JSVAL_TO_OBJECT(v
);
2948 if (!OBJECT_IS_XML(cx
, obj
)) {
2949 if (!OBJ_DEFAULT_VALUE(cx
, obj
, JSTYPE_STRING
, &v
))
2951 str
= js_ValueToString(cx
, v
);
2954 return EscapeElementValue(cx
, NULL
, str
);
2957 /* Handle non-element cases in this switch, returning from each case. */
2958 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
2959 return XMLToXMLString(cx
, xml
, NULL
, toSourceFlag
| 0);
2963 ToAttributeName(JSContext
*cx
, jsval v
)
2965 JSString
*name
, *uri
, *prefix
;
2970 if (JSVAL_IS_STRING(v
)) {
2971 name
= JSVAL_TO_STRING(v
);
2972 uri
= prefix
= cx
->runtime
->emptyString
;
2974 if (JSVAL_IS_PRIMITIVE(v
)) {
2975 js_ReportValueError(cx
, JSMSG_BAD_XML_ATTR_NAME
,
2976 JSDVG_IGNORE_STACK
, v
, NULL
);
2980 obj
= JSVAL_TO_OBJECT(v
);
2981 clasp
= OBJ_GET_CLASS(cx
, obj
);
2982 if (clasp
== &js_AttributeNameClass
)
2985 if (clasp
== &js_QNameClass
.base
) {
2988 prefix
= GetPrefix(qn
);
2989 name
= GetLocalName(qn
);
2991 if (clasp
== &js_AnyNameClass
) {
2992 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
2994 name
= js_ValueToString(cx
, v
);
2998 uri
= prefix
= cx
->runtime
->emptyString
;
3002 qn
= NewXMLQName(cx
, uri
, prefix
, name
, &js_AttributeNameClass
);
3009 ReportBadXMLName(JSContext
*cx
, jsval id
)
3011 js_ReportValueError(cx
, JSMSG_BAD_XML_NAME
, JSDVG_IGNORE_STACK
, id
, NULL
);
3015 IsFunctionQName(JSContext
*cx
, JSObject
*qn
, jsid
*funidp
)
3020 atom
= cx
->runtime
->atomState
.lazy
.functionNamespaceURIAtom
;
3023 (uri
== ATOM_TO_STRING(atom
) ||
3024 js_EqualStrings(uri
, ATOM_TO_STRING(atom
)))) {
3025 return JS_ValueToId(cx
, STRING_TO_JSVAL(GetLocalName(qn
)), funidp
);
3032 js_IsFunctionQName(JSContext
*cx
, JSObject
*obj
, jsid
*funidp
)
3034 if (OBJ_GET_CLASS(cx
, obj
) == &js_QNameClass
.base
)
3035 return IsFunctionQName(cx
, obj
, funidp
);
3041 ToXMLName(JSContext
*cx
, jsval v
, jsid
*funidp
)
3048 if (JSVAL_IS_STRING(v
)) {
3049 name
= JSVAL_TO_STRING(v
);
3051 if (JSVAL_IS_PRIMITIVE(v
)) {
3052 ReportBadXMLName(cx
, v
);
3056 obj
= JSVAL_TO_OBJECT(v
);
3057 clasp
= OBJ_GET_CLASS(cx
, obj
);
3058 if (clasp
== &js_AttributeNameClass
|| clasp
== &js_QNameClass
.base
)
3060 if (clasp
== &js_AnyNameClass
) {
3061 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
3064 name
= js_ValueToString(cx
, v
);
3070 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3072 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3074 * First, _P_ should be _s_, to refer to the given string.
3076 * Second, why does ToXMLName applied to the string type throw TypeError
3077 * only for numeric literals without any leading or trailing whitespace?
3079 * If the idea is to reject uint32 property names, then the check needs to
3080 * be stricter, to exclude hexadecimal and floating point literals.
3082 if (js_IdIsIndex(STRING_TO_JSVAL(name
), &index
))
3085 if (*JSSTRING_CHARS(name
) == '@') {
3086 name
= js_NewDependentString(cx
, name
, 1, JSSTRING_LENGTH(name
) - 1);
3090 return ToAttributeName(cx
, STRING_TO_JSVAL(name
));
3094 v
= STRING_TO_JSVAL(name
);
3095 obj
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 1, &v
);
3100 if (!IsFunctionQName(cx
, obj
, funidp
))
3105 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3107 js_ValueToPrintableString(cx
, STRING_TO_JSVAL(name
)));
3111 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3113 AddInScopeNamespace(JSContext
*cx
, JSXML
*xml
, JSObject
*ns
)
3115 JSString
*prefix
, *prefix2
;
3116 JSObject
*match
, *ns2
;
3119 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
3122 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
3123 prefix
= GetPrefix(ns
);
3126 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3127 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3128 if (ns2
&& js_EqualStrings(GetURI(ns2
), GetURI(ns
))) {
3133 if (!match
&& !XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_namespaces
, n
, ns
))
3136 if (IS_EMPTY(prefix
) && IS_EMPTY(GetURI(xml
->name
)))
3139 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3142 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3143 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3144 if (ns2
&& (prefix2
= GetPrefix(ns2
)) &&
3145 js_EqualStrings(prefix2
, prefix
)) {
3151 if (match
&& !js_EqualStrings(GetURI(match
), GetURI(ns
))) {
3152 ns2
= XMLARRAY_DELETE(cx
, &xml
->xml_namespaces
, m
, JS_TRUE
,
3154 JS_ASSERT(ns2
== match
);
3155 match
->fslots
[JSSLOT_PREFIX
] = JSVAL_VOID
;
3156 if (!AddInScopeNamespace(cx
, xml
, match
))
3159 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
3163 /* OPTION: enforce that descendants have superset namespaces. */
3167 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3169 Append(JSContext
*cx
, JSXML
*list
, JSXML
*xml
)
3174 JS_ASSERT(list
->xml_class
== JSXML_CLASS_LIST
);
3175 i
= list
->xml_kids
.length
;
3177 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3178 list
->xml_target
= xml
->xml_target
;
3179 list
->xml_targetprop
= xml
->xml_targetprop
;
3180 n
= JSXML_LENGTH(xml
);
3182 if (!XMLArraySetCapacity(cx
, &list
->xml_kids
, k
))
3184 for (j
= 0; j
< n
; j
++) {
3185 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, j
, JSXML
);
3187 XMLARRAY_SET_MEMBER(&list
->xml_kids
, i
+ j
, kid
);
3192 list
->xml_target
= xml
->parent
;
3193 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
3194 list
->xml_targetprop
= NULL
;
3196 list
->xml_targetprop
= xml
->name
;
3197 if (!XMLARRAY_ADD_MEMBER(cx
, &list
->xml_kids
, i
, xml
))
3202 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3204 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
);
3207 DeepCopy(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
, uintN flags
)
3212 /* Our caller may not be protecting newborns with a local root scope. */
3213 if (!js_EnterLocalRootScope(cx
))
3215 copy
= DeepCopyInLRS(cx
, xml
, flags
);
3218 /* Caller provided the object for this copy, hook 'em up. */
3219 ok
= JS_SetPrivate(cx
, obj
, copy
);
3223 ok
= js_GetXMLObject(cx
, copy
) != NULL
;
3228 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) copy
);
3233 * (i) We must be in a local root scope (InLRS).
3234 * (ii) parent must have a rooted object.
3235 * (iii) from's owning object must be locked if not thread-local.
3238 DeepCopySetInLRS(JSContext
*cx
, JSXMLArray
*from
, JSXMLArray
*to
, JSXML
*parent
,
3242 JSXMLArrayCursor cursor
;
3247 JS_ASSERT(cx
->localRootStack
);
3250 if (!XMLArraySetCapacity(cx
, to
, n
))
3253 XMLArrayCursorInit(&cursor
, from
);
3256 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
3257 if ((flags
& XSF_IGNORE_COMMENTS
) &&
3258 kid
->xml_class
== JSXML_CLASS_COMMENT
) {
3261 if ((flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
) &&
3262 kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
3265 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3266 (kid
->xml_flags
& XMLF_WHITESPACE_TEXT
)) {
3269 kid2
= DeepCopyInLRS(cx
, kid
, flags
);
3276 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3277 n
> 1 && kid2
->xml_class
== JSXML_CLASS_TEXT
) {
3278 str
= ChompXMLWhitespace(cx
, kid2
->xml_value
);
3284 kid2
->xml_value
= str
;
3287 XMLARRAY_SET_MEMBER(to
, j
, kid2
);
3289 if (parent
->xml_class
!= JSXML_CLASS_LIST
)
3290 kid2
->parent
= parent
;
3292 XMLArrayCursorFinish(&cursor
);
3302 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
)
3310 /* Our caller must be protecting newborn objects. */
3311 JS_ASSERT(cx
->localRootStack
);
3313 JS_CHECK_RECURSION(cx
, return NULL
);
3315 copy
= js_NewXML(cx
, (JSXMLClass
) xml
->xml_class
);
3320 qn
= NewXMLQName(cx
, GetURI(qn
), GetPrefix(qn
), GetLocalName(qn
));
3327 copy
->xml_flags
= xml
->xml_flags
;
3329 if (JSXML_HAS_VALUE(xml
)) {
3330 copy
->xml_value
= xml
->xml_value
;
3333 ok
= DeepCopySetInLRS(cx
, &xml
->xml_kids
, ©
->xml_kids
, copy
, flags
);
3337 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3338 copy
->xml_target
= xml
->xml_target
;
3339 copy
->xml_targetprop
= xml
->xml_targetprop
;
3341 n
= xml
->xml_namespaces
.length
;
3342 ok
= XMLArraySetCapacity(cx
, ©
->xml_namespaces
, n
);
3345 for (i
= 0; i
< n
; i
++) {
3346 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3349 ns2
= NewXMLNamespace(cx
, GetPrefix(ns
), GetURI(ns
),
3352 copy
->xml_namespaces
.length
= i
;
3356 XMLARRAY_SET_MEMBER(©
->xml_namespaces
, i
, ns2
);
3359 ok
= DeepCopySetInLRS(cx
, &xml
->xml_attrs
, ©
->xml_attrs
, copy
,
3372 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3374 DeleteByIndex(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3378 if (JSXML_HAS_KIDS(xml
) && index
< xml
->xml_kids
.length
) {
3379 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3382 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3386 typedef JSBool (*JSXMLNameMatcher
)(JSObject
*nameqn
, JSXML
*xml
);
3389 MatchAttrName(JSObject
*nameqn
, JSXML
*attr
)
3391 JSObject
*attrqn
= attr
->name
;
3392 JSString
*localName
= GetLocalName(nameqn
);
3395 return (IS_STAR(localName
) ||
3396 js_EqualStrings(GetLocalName(attrqn
), localName
)) &&
3397 (!(uri
= GetURI(nameqn
)) ||
3398 js_EqualStrings(GetURI(attrqn
), uri
));
3402 MatchElemName(JSObject
*nameqn
, JSXML
*elem
)
3404 JSString
*localName
= GetLocalName(nameqn
);
3407 return (IS_STAR(localName
) ||
3408 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3409 js_EqualStrings(GetLocalName(elem
->name
), localName
))) &&
3410 (!(uri
= GetURI(nameqn
)) ||
3411 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3412 js_EqualStrings(GetURI(elem
->name
), uri
)));
3415 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3417 DescendantsHelper(JSContext
*cx
, JSXML
*xml
, JSObject
*nameqn
, JSXML
*list
)
3422 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
3424 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
&&
3425 OBJ_GET_CLASS(cx
, nameqn
) == &js_AttributeNameClass
) {
3426 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
3427 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3428 if (attr
&& MatchAttrName(nameqn
, attr
)) {
3429 if (!Append(cx
, list
, attr
))
3435 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
3436 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3439 if (OBJ_GET_CLASS(cx
, nameqn
) != &js_AttributeNameClass
&&
3440 MatchElemName(nameqn
, kid
)) {
3441 if (!Append(cx
, list
, kid
))
3444 if (!DescendantsHelper(cx
, kid
, nameqn
, list
))
3451 Descendants(JSContext
*cx
, JSXML
*xml
, jsval id
)
3460 nameqn
= ToXMLName(cx
, id
, &funid
);
3464 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
3467 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
3472 * Protect nameqn's object and strings from GC by linking list to it
3473 * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj,
3474 * which protects list. Any other object allocations occuring beneath
3475 * DescendantsHelper use local roots.
3477 list
->name
= nameqn
;
3478 if (!js_EnterLocalRootScope(cx
))
3480 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3482 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
3483 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3484 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
3485 ok
= DescendantsHelper(cx
, kid
, nameqn
, list
);
3491 ok
= DescendantsHelper(cx
, xml
, nameqn
, list
);
3493 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) list
);
3501 xml_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
);
3503 /* Recursive (JSXML *) parameterized version of Equals. */
3505 XMLEquals(JSContext
*cx
, JSXML
*xml
, JSXML
*vxml
, JSBool
*bp
)
3509 JSXMLArrayCursor cursor
, vcursor
;
3510 JSXML
*kid
, *vkid
, *attr
, *vattr
;
3512 JSObject
*xobj
, *vobj
;
3515 if (xml
->xml_class
!= vxml
->xml_class
) {
3516 if (xml
->xml_class
== JSXML_CLASS_LIST
&& xml
->xml_kids
.length
== 1) {
3517 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3521 if (vxml
->xml_class
== JSXML_CLASS_LIST
&& vxml
->xml_kids
.length
== 1) {
3522 vxml
= XMLARRAY_MEMBER(&vxml
->xml_kids
, 0, JSXML
);
3534 js_EqualStrings(GetLocalName(qn
), GetLocalName(vqn
)) &&
3535 js_EqualStrings(GetURI(qn
), GetURI(vqn
));
3542 if (JSXML_HAS_VALUE(xml
)) {
3543 *bp
= js_EqualStrings(xml
->xml_value
, vxml
->xml_value
);
3544 } else if (xml
->xml_kids
.length
!= vxml
->xml_kids
.length
) {
3547 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
3548 XMLArrayCursorInit(&vcursor
, &vxml
->xml_kids
);
3550 kid
= (JSXML
*) XMLArrayCursorNext(&cursor
);
3551 vkid
= (JSXML
*) XMLArrayCursorNext(&vcursor
);
3552 if (!kid
|| !vkid
) {
3553 *bp
= !kid
&& !vkid
;
3557 xobj
= js_GetXMLObject(cx
, kid
);
3558 vobj
= js_GetXMLObject(cx
, vkid
);
3559 ok
= xobj
&& vobj
&&
3560 xml_equality(cx
, xobj
, OBJECT_TO_JSVAL(vobj
), bp
);
3564 XMLArrayCursorFinish(&vcursor
);
3565 XMLArrayCursorFinish(&cursor
);
3569 if (*bp
&& xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3570 n
= xml
->xml_attrs
.length
;
3571 if (n
!= vxml
->xml_attrs
.length
)
3573 for (i
= 0; *bp
&& i
< n
; i
++) {
3574 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3577 j
= XMLARRAY_FIND_MEMBER(&vxml
->xml_attrs
, attr
, attr_identity
);
3578 if (j
== XML_NOT_FOUND
) {
3582 vattr
= XMLARRAY_MEMBER(&vxml
->xml_attrs
, j
, JSXML
);
3585 *bp
= js_EqualStrings(attr
->xml_value
, vattr
->xml_value
);
3593 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3595 Equals(JSContext
*cx
, JSXML
*xml
, jsval v
, JSBool
*bp
)
3600 if (JSVAL_IS_PRIMITIVE(v
)) {
3602 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3603 if (xml
->xml_kids
.length
== 1) {
3604 vxml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3607 vobj
= js_GetXMLObject(cx
, vxml
);
3610 return js_XMLObjectOps
.equality(cx
, vobj
, v
, bp
);
3612 if (JSVAL_IS_VOID(v
) && xml
->xml_kids
.length
== 0)
3616 vobj
= JSVAL_TO_OBJECT(v
);
3617 if (!OBJECT_IS_XML(cx
, vobj
)) {
3620 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
3621 if (!XMLEquals(cx
, xml
, vxml
, bp
))
3629 CheckCycle(JSContext
*cx
, JSXML
*xml
, JSXML
*kid
)
3631 JS_ASSERT(kid
->xml_class
!= JSXML_CLASS_LIST
);
3635 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3636 JSMSG_CYCLIC_VALUE
, js_XML_str
);
3639 } while ((xml
= xml
->parent
) != NULL
);
3644 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3646 Insert(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3653 if (!JSXML_HAS_KIDS(xml
))
3658 if (!JSVAL_IS_PRIMITIVE(v
)) {
3659 vobj
= JSVAL_TO_OBJECT(v
);
3660 if (OBJECT_IS_XML(cx
, vobj
)) {
3661 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
3662 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3663 n
= vxml
->xml_kids
.length
;
3666 for (j
= 0; j
< n
; j
++) {
3667 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3670 if (!CheckCycle(cx
, xml
, kid
))
3673 } else if (vxml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3674 /* OPTION: enforce that descendants have superset namespaces. */
3675 if (!CheckCycle(cx
, xml
, vxml
))
3681 str
= js_ValueToString(cx
, v
);
3685 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3688 vxml
->xml_value
= str
;
3691 if (i
> xml
->xml_kids
.length
)
3692 i
= xml
->xml_kids
.length
;
3694 if (!XMLArrayInsert(cx
, &xml
->xml_kids
, i
, n
))
3697 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3698 for (j
= 0; j
< n
; j
++) {
3699 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3703 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
+ j
, kid
);
3705 /* OPTION: enforce that descendants have superset namespaces. */
3709 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
3715 IndexToIdVal(JSContext
*cx
, uint32 index
, jsval
*idvp
)
3719 if (index
<= JSVAL_INT_MAX
) {
3720 *idvp
= INT_TO_JSVAL(index
);
3722 str
= js_NumberToString(cx
, (jsdouble
) index
);
3725 *idvp
= STRING_TO_JSVAL(str
);
3730 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3732 Replace(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3739 if (!JSXML_HAS_KIDS(xml
))
3744 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3745 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3747 n
= xml
->xml_kids
.length
;
3752 if (!JSVAL_IS_PRIMITIVE(v
)) {
3753 vobj
= JSVAL_TO_OBJECT(v
);
3754 if (OBJECT_IS_XML(cx
, vobj
))
3755 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
3758 switch (vxml
? vxml
->xml_class
: JSXML_CLASS_LIMIT
) {
3759 case JSXML_CLASS_ELEMENT
:
3760 /* OPTION: enforce that descendants have superset namespaces. */
3761 if (!CheckCycle(cx
, xml
, vxml
))
3763 case JSXML_CLASS_COMMENT
:
3764 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
3765 case JSXML_CLASS_TEXT
:
3768 case JSXML_CLASS_LIST
:
3770 DeleteByIndex(cx
, xml
, i
);
3771 if (!Insert(cx
, xml
, i
, v
))
3776 str
= js_ValueToString(cx
, v
);
3780 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3783 vxml
->xml_value
= str
;
3788 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3792 if (!XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_kids
, i
, vxml
))
3800 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3802 DeleteNamedProperty(JSContext
*cx
, JSXML
*xml
, JSObject
*nameqn
,
3806 uint32 index
, deleteCount
;
3808 JSXMLNameMatcher matcher
;
3810 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3811 array
= &xml
->xml_kids
;
3812 for (index
= 0; index
< array
->length
; index
++) {
3813 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3814 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
)
3815 DeleteNamedProperty(cx
, kid
, nameqn
, attributes
);
3817 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3819 array
= &xml
->xml_attrs
;
3820 matcher
= MatchAttrName
;
3822 array
= &xml
->xml_kids
;
3823 matcher
= MatchElemName
;
3826 for (index
= 0; index
< array
->length
; index
++) {
3827 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3828 if (kid
&& matcher(nameqn
, kid
)) {
3830 XMLArrayDelete(cx
, array
, index
, JS_FALSE
);
3832 } else if (deleteCount
!= 0) {
3833 XMLARRAY_SET_MEMBER(array
,
3834 index
- deleteCount
,
3835 array
->vector
[index
]);
3838 array
->length
-= deleteCount
;
3842 /* ECMA-357 9.2.1.3 index case. */
3844 DeleteListElement(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3846 JSXML
*kid
, *parent
;
3849 JS_ASSERT(xml
->xml_class
== JSXML_CLASS_LIST
);
3851 if (index
< xml
->xml_kids
.length
) {
3852 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3854 parent
= kid
->parent
;
3856 JS_ASSERT(parent
!= xml
);
3857 JS_ASSERT(JSXML_HAS_KIDS(parent
));
3859 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
3860 DeleteNamedProperty(cx
, parent
, kid
->name
, JS_TRUE
);
3862 kidIndex
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
,
3864 JS_ASSERT(kidIndex
!= XML_NOT_FOUND
);
3865 DeleteByIndex(cx
, parent
, kidIndex
);
3868 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3874 SyncInScopeNamespaces(JSContext
*cx
, JSXML
*xml
)
3876 JSXMLArray
*nsarray
;
3880 nsarray
= &xml
->xml_namespaces
;
3881 while ((xml
= xml
->parent
) != NULL
) {
3882 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3883 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3884 if (ns
&& !XMLARRAY_HAS_MEMBER(nsarray
, ns
, namespace_identity
)) {
3885 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
3894 GetNamedProperty(JSContext
*cx
, JSXML
*xml
, JSObject
* nameqn
, JSXML
*list
)
3897 JSXMLNameMatcher matcher
;
3898 JSXMLArrayCursor cursor
;
3902 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3903 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
3904 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
3905 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
3906 !GetNamedProperty(cx
, kid
, nameqn
, list
)) {
3910 XMLArrayCursorFinish(&cursor
);
3913 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3914 attrs
= (OBJ_GET_CLASS(cx
, nameqn
) == &js_AttributeNameClass
);
3916 array
= &xml
->xml_attrs
;
3917 matcher
= MatchAttrName
;
3919 array
= &xml
->xml_kids
;
3920 matcher
= MatchElemName
;
3923 XMLArrayCursorInit(&cursor
, array
);
3924 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
3925 if (matcher(nameqn
, kid
)) {
3927 kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
3928 !SyncInScopeNamespaces(cx
, kid
)) {
3931 if (!Append(cx
, list
, kid
))
3935 XMLArrayCursorFinish(&cursor
);
3943 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3945 GetProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
3947 JSXML
*xml
, *list
, *kid
;
3949 JSObject
*kidobj
, *listobj
;
3953 JSTempValueRooter tvr
;
3955 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
3959 if (js_IdIsIndex(id
, &index
)) {
3960 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
3961 *vp
= (index
== 0) ? OBJECT_TO_JSVAL(obj
) : JSVAL_VOID
;
3964 * ECMA-357 9.2.1.1 starts here.
3966 * Erratum: 9.2 is not completely clear that indexed properties
3967 * correspond to kids, but that's what it seems to say, and it's
3968 * what any sane user would want.
3970 if (index
< xml
->xml_kids
.length
) {
3971 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3976 kidobj
= js_GetXMLObject(cx
, kid
);
3980 *vp
= OBJECT_TO_JSVAL(kidobj
);
3989 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3991 nameqn
= ToXMLName(cx
, id
, &funid
);
3995 return js_GetXMLFunction(cx
, obj
, funid
, vp
);
3997 roots
[0] = OBJECT_TO_JSVAL(nameqn
);
3998 JS_PUSH_TEMP_ROOT(cx
, 1, roots
, &tvr
);
4000 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
4002 roots
[1] = OBJECT_TO_JSVAL(listobj
);
4005 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
4006 if (!GetNamedProperty(cx
, xml
, nameqn
, list
)) {
4010 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
4011 * given list's [[TargetProperty]] to the property that is being
4012 * appended. This means that any use of the internal [[Get]]
4013 * property returns a list which, when used by e.g. [[Insert]]
4014 * duplicates the last element matched by id. See bug 336921.
4016 list
->xml_target
= xml
;
4017 list
->xml_targetprop
= nameqn
;
4018 *vp
= OBJECT_TO_JSVAL(listobj
);
4022 JS_POP_TEMP_ROOT(cx
, &tvr
);
4023 return listobj
!= NULL
;
4027 CopyOnWrite(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
)
4029 JS_ASSERT(xml
->object
!= obj
);
4031 xml
= DeepCopy(cx
, xml
, obj
, 0);
4035 JS_ASSERT(xml
->object
== obj
);
4039 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
4040 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
4043 KidToString(JSContext
*cx
, JSXML
*xml
, uint32 index
)
4048 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
4050 return cx
->runtime
->emptyString
;
4051 kidobj
= js_GetXMLObject(cx
, kid
);
4054 return js_ValueToString(cx
, OBJECT_TO_JSVAL(kidobj
));
4057 /* Forward declared -- its implementation uses other statics that call it. */
4059 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
);
4061 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
4063 PutProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4065 JSBool ok
, primitiveAssign
;
4066 enum { OBJ_ROOT
, ID_ROOT
, VAL_ROOT
};
4068 JSTempValueRooter tvr
;
4069 JSXML
*xml
, *vxml
, *rxml
, *kid
, *attr
, *parent
, *copy
, *kid2
, *match
;
4070 JSObject
*vobj
, *nameobj
, *attrobj
, *parentobj
, *kidobj
, *copyobj
;
4071 JSObject
*targetprop
, *nameqn
, *attrqn
;
4072 uint32 index
, i
, j
, k
, n
, q
, matchIndex
;
4073 jsval attrval
, nsval
;
4075 JSString
*left
, *right
, *space
, *uri
;
4078 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
4082 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
4086 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
4088 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
4089 vobj
= JSVAL_TO_OBJECT(*vp
);
4090 if (OBJECT_IS_XML(cx
, vobj
))
4091 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
4094 ok
= js_EnterLocalRootScope(cx
);
4097 MUST_FLOW_THROUGH("out");
4098 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
4099 roots
[ID_ROOT
] = id
;
4100 roots
[VAL_ROOT
] = *vp
;
4101 JS_PUSH_TEMP_ROOT(cx
, 3, roots
, &tvr
);
4103 if (js_IdIsIndex(id
, &index
)) {
4104 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
4105 /* See NOTE in spec: this variation is reserved for future use. */
4106 ReportBadXMLName(cx
, id
);
4111 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
4116 if (xml
->xml_target
) {
4117 ok
= ResolveValue(cx
, xml
->xml_target
, &rxml
);
4122 JS_ASSERT(rxml
->object
);
4128 if (index
>= xml
->xml_kids
.length
) {
4131 if (rxml
->xml_class
== JSXML_CLASS_LIST
) {
4132 if (rxml
->xml_kids
.length
!= 1)
4134 rxml
= XMLARRAY_MEMBER(&rxml
->xml_kids
, 0, JSXML
);
4137 ok
= js_GetXMLObject(cx
, rxml
) != NULL
;
4143 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
4144 * _y.[[Parent]] = r_ where _r_ is the result of
4145 * [[ResolveValue]] called on _x.[[TargetObject]] in
4146 * 2(a)(i). This can result in text parenting text:
4148 * var MYXML = new XML();
4149 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
4151 * (testcase from Werner Sharp <wsharp@macromedia.com>).
4153 * To match insertChildAfter, insertChildBefore,
4154 * prependChild, and setChildren, we should silently
4155 * do nothing in this case.
4157 if (!JSXML_HAS_KIDS(rxml
))
4161 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
4162 targetprop
= xml
->xml_targetprop
;
4163 if (!targetprop
|| IS_STAR(GetLocalName(targetprop
))) {
4164 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
4165 kid
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
4169 nameobj
= targetprop
;
4172 if (OBJ_GET_CLASS(cx
, nameobj
) == &js_AttributeNameClass
) {
4175 * Note that rxml can't be null here, because target
4176 * and targetprop are non-null.
4178 ok
= GetProperty(cx
, rxml
->object
, id
, &attrval
);
4181 if (JSVAL_IS_PRIMITIVE(attrval
)) /* no such attribute */
4183 attrobj
= JSVAL_TO_OBJECT(attrval
);
4184 attr
= (JSXML
*) JS_GetPrivate(cx
, attrobj
);
4185 if (JSXML_LENGTH(attr
) != 0)
4188 kid
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
4191 kid
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
4196 /* An important bit of 2(c)(ii). */
4197 kid
->name
= targetprop
;
4200 /* Final important bit of 2(c)(ii). */
4204 i
= xml
->xml_kids
.length
;
4205 if (kid
->xml_class
!= JSXML_CLASS_ATTRIBUTE
) {
4207 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
4208 * y.[[Parent]] is here called kid->parent, which we know
4209 * from 2(c)(ii) is _r_, here called rxml. So let's just
4210 * test that! Erratum, the spec should be simpler here.
4213 JS_ASSERT(JSXML_HAS_KIDS(rxml
));
4214 n
= rxml
->xml_kids
.length
;
4216 if (n
!= 0 && i
!= 0) {
4217 for (n
= j
, j
= 0; j
< n
; j
++) {
4218 if (rxml
->xml_kids
.vector
[j
] ==
4219 xml
->xml_kids
.vector
[i
-1]) {
4225 kidobj
= js_GetXMLObject(cx
, kid
);
4228 ok
= Insert(cx
, rxml
, j
+ 1, OBJECT_TO_JSVAL(kidobj
));
4235 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4236 * typo for [[TargetProperty]].
4239 kid
->name
= (vxml
->xml_class
== JSXML_CLASS_LIST
)
4240 ? vxml
->xml_targetprop
4246 ok
= Append(cx
, xml
, kid
);
4253 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4254 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4255 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4258 roots
[VAL_ROOT
] = *vp
;
4262 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
4265 parent
= kid
->parent
;
4266 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4267 nameobj
= kid
->name
;
4268 if (OBJ_GET_CLASS(cx
, nameobj
) != &js_AttributeNameClass
) {
4269 nameobj
= NewXMLQName(cx
, GetURI(nameobj
), GetPrefix(nameobj
),
4270 GetLocalName(nameobj
),
4271 &js_AttributeNameClass
);
4275 id
= OBJECT_TO_JSVAL(nameobj
);
4279 parentobj
= js_GetXMLObject(cx
, parent
);
4282 ok
= PutProperty(cx
, parentobj
, id
, vp
);
4287 ok
= GetProperty(cx
, parentobj
, id
, vp
);
4290 attr
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(*vp
));
4293 xml
->xml_kids
.vector
[i
] = attr
->xml_kids
.vector
[0];
4298 else if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4302 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4303 * if we do that we never change the parent of each child in the
4304 * list. Since [[Put]] when called on an XML object deeply copies
4305 * the provided list _V_, we also do so here. Perhaps the shallow
4306 * copy was a misguided optimization?
4308 copy
= DeepCopyInLRS(cx
, vxml
, 0);
4311 copyobj
= js_GetXMLObject(cx
, copy
);
4315 JS_ASSERT(parent
!= xml
);
4317 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4318 JS_ASSERT(q
!= XML_NOT_FOUND
);
4319 ok
= Replace(cx
, parent
, q
, OBJECT_TO_JSVAL(copyobj
));
4324 /* Erratum: this loop in the spec is useless. */
4325 for (j
= 0, n
= copy
->xml_kids
.length
; j
< n
; j
++) {
4326 kid2
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
+ j
, JSXML
);
4327 JS_ASSERT(XMLARRAY_MEMBER(©
->xml_kids
, j
, JSXML
)
4335 * Erratum: notice the unhandled zero-length V basis case and
4336 * the off-by-one errors for the n != 0 cases in the spec.
4338 n
= copy
->xml_kids
.length
;
4340 XMLArrayDelete(cx
, &xml
->xml_kids
, i
, JS_TRUE
);
4342 ok
= XMLArrayInsert(cx
, &xml
->xml_kids
, i
+ 1, n
- 1);
4346 for (j
= 0; j
< n
; j
++)
4347 xml
->xml_kids
.vector
[i
+ j
] = copy
->xml_kids
.vector
[j
];
4352 else if (vxml
|| JSXML_HAS_VALUE(kid
)) {
4354 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4355 JS_ASSERT(q
!= XML_NOT_FOUND
);
4356 ok
= Replace(cx
, parent
, q
, *vp
);
4360 vxml
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
, JSXML
);
4363 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4368 * Erratum: _V_ may not be of type XML, but all index-named
4369 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4370 * according to 9.2.1.1 Overview and other places in the spec.
4372 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4373 * or an XML/XMLList object. If *vp is a string, call ToXML
4374 * on it to satisfy the constraint.
4377 JS_ASSERT(JSVAL_IS_STRING(*vp
));
4378 vobj
= ToXML(cx
, *vp
);
4381 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vobj
);
4382 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
4384 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
4389 kidobj
= js_GetXMLObject(cx
, kid
);
4392 id
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
4393 ok
= PutProperty(cx
, kidobj
, id
, vp
);
4399 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4401 nameqn
= ToXMLName(cx
, id
, &funid
);
4405 ok
= js_SetProperty(cx
, obj
, funid
, vp
);
4409 roots
[ID_ROOT
] = OBJECT_TO_JSVAL(nameobj
);
4411 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4413 * Step 3 of 9.2.1.2.
4414 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4415 * or an r with r.[[Length]] != 1, throw TypeError.
4417 n
= JSXML_LENGTH(xml
);
4421 ok
= ResolveValue(cx
, xml
, &rxml
);
4424 if (!rxml
|| JSXML_LENGTH(rxml
) != 1)
4426 ok
= Append(cx
, xml
, rxml
);
4430 JS_ASSERT(JSXML_LENGTH(xml
) == 1);
4431 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
4434 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
4435 obj
= js_GetXMLObject(cx
, xml
);
4438 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
4440 /* FALL THROUGH to non-list case */
4445 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4446 * effort in ToString or [[DeepCopy]].
4449 if (JSXML_HAS_VALUE(xml
))
4453 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4454 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4455 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4459 rxml
= DeepCopyInLRS(cx
, vxml
, 0);
4460 if (!rxml
|| !js_GetXMLObject(cx
, rxml
))
4463 *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4465 roots
[VAL_ROOT
] = *vp
;
4469 * Erratum: why is this done here, so early? use is way later....
4471 ok
= js_GetDefaultXMLNamespace(cx
, &nsval
);
4475 if (OBJ_GET_CLASS(cx
, nameobj
) == &js_AttributeNameClass
) {
4477 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)))
4481 if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4482 n
= vxml
->xml_kids
.length
;
4484 *vp
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4486 left
= KidToString(cx
, vxml
, 0);
4490 space
= ATOM_TO_STRING(cx
->runtime
->atomState
.spaceAtom
);
4491 for (i
= 1; i
< n
; i
++) {
4492 left
= js_ConcatStrings(cx
, left
, space
);
4495 right
= KidToString(cx
, vxml
, i
);
4498 left
= js_ConcatStrings(cx
, left
, right
);
4503 roots
[VAL_ROOT
] = *vp
= STRING_TO_JSVAL(left
);
4506 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4509 roots
[VAL_ROOT
] = *vp
;
4514 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
4515 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
4518 attrqn
= attr
->name
;
4519 if (js_EqualStrings(GetLocalName(attrqn
),
4520 GetLocalName(nameqn
))) {
4521 uri
= GetURI(nameqn
);
4522 if (!uri
|| js_EqualStrings(GetURI(attrqn
), uri
)) {
4526 DeleteNamedProperty(cx
, xml
, attrqn
, JS_TRUE
);
4537 uri
= GetURI(nameqn
);
4539 left
= right
= cx
->runtime
->emptyString
;
4542 right
= GetPrefix(nameqn
);
4544 nameqn
= NewXMLQName(cx
, left
, right
, GetLocalName(nameqn
));
4549 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
4553 attr
->name
= nameqn
;
4556 ok
= XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_attrs
, n
, attr
);
4561 ns
= GetNamespace(cx
, nameqn
, NULL
);
4564 ok
= AddInScopeNamespace(cx
, xml
, ns
);
4570 attr
->xml_value
= JSVAL_TO_STRING(*vp
);
4575 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)) &&
4576 !IS_STAR(GetLocalName(nameqn
))) {
4582 primitiveAssign
= !vxml
&& !IS_STAR(GetLocalName(nameqn
));
4585 k
= n
= xml
->xml_kids
.length
;
4586 matchIndex
= XML_NOT_FOUND
;
4590 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, k
, JSXML
);
4591 if (kid
&& MatchElemName(nameqn
, kid
)) {
4592 if (matchIndex
!= XML_NOT_FOUND
)
4593 DeleteByIndex(cx
, xml
, matchIndex
);
4600 * Erratum: ECMA-357 specified child insertion inconsistently:
4601 * insertChildBefore and insertChildAfter insert an arbitrary XML
4602 * instance, and therefore can create cycles, but appendChild as
4603 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4604 * its argument. But the "Semantics" in 13.4.4.3 do not include
4605 * any [[DeepCopy]] call.
4607 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4608 * required adding cycle detection, and allowing duplicate kids to
4609 * be created (see comment 6 in the bug). Allowing duplicate kid
4610 * references means the loop above will delete all but the lowest
4611 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4612 * parent. Thus the need to restore parent here. This is covered
4613 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4616 JS_ASSERT(kid2
->parent
== xml
|| !kid2
->parent
);
4622 if (matchIndex
== XML_NOT_FOUND
) {
4627 if (primitiveAssign
) {
4628 uri
= GetURI(nameqn
);
4630 ns
= JSVAL_TO_OBJECT(nsval
);
4632 right
= GetPrefix(ns
);
4635 right
= GetPrefix(nameqn
);
4637 nameqn
= NewXMLQName(cx
, left
, right
, GetLocalName(nameqn
));
4642 vobj
= js_NewXMLObject(cx
, JSXML_CLASS_ELEMENT
);
4645 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
4647 vxml
->name
= nameqn
;
4650 ns
= GetNamespace(cx
, nameqn
, NULL
);
4653 ok
= Replace(cx
, xml
, matchIndex
, OBJECT_TO_JSVAL(vobj
));
4656 ok
= AddInScopeNamespace(cx
, vxml
, ns
);
4663 if (primitiveAssign
) {
4664 JSXMLArrayCursor cursor
;
4666 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
4667 cursor
.index
= matchIndex
;
4668 kid
= (JSXML
*) XMLArrayCursorItem(&cursor
);
4669 if (JSXML_HAS_KIDS(kid
)) {
4670 XMLArrayFinish(cx
, &kid
->xml_kids
);
4671 ok
= XMLArrayInit(cx
, &kid
->xml_kids
, 1);
4675 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4677 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4678 if (ok
&& !IS_EMPTY(JSVAL_TO_STRING(*vp
))) {
4679 roots
[VAL_ROOT
] = *vp
;
4680 if ((JSXML
*) XMLArrayCursorItem(&cursor
) == kid
)
4681 ok
= Replace(cx
, kid
, 0, *vp
);
4684 XMLArrayCursorFinish(&cursor
);
4687 ok
= Replace(cx
, xml
, matchIndex
, *vp
);
4692 JS_POP_TEMP_ROOT(cx
, &tvr
);
4693 js_LeaveLocalRootScope(cx
);
4697 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4698 JSMSG_BAD_XMLLIST_PUT
,
4699 js_ValueToPrintableString(cx
, id
));
4705 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4707 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
)
4709 JSXML
*target
, *base
;
4710 JSObject
*targetprop
;
4713 /* Our caller must be protecting newborn objects. */
4714 JS_ASSERT(cx
->localRootStack
);
4716 if (list
->xml_class
!= JSXML_CLASS_LIST
|| list
->xml_kids
.length
!= 0) {
4717 if (!js_GetXMLObject(cx
, list
))
4723 target
= list
->xml_target
;
4724 targetprop
= list
->xml_targetprop
;
4725 if (!target
|| !targetprop
|| IS_STAR(GetLocalName(targetprop
))) {
4730 if (OBJ_GET_CLASS(cx
, targetprop
) == &js_AttributeNameClass
) {
4735 if (!ResolveValue(cx
, target
, &base
))
4741 if (!js_GetXMLObject(cx
, base
))
4744 id
= OBJECT_TO_JSVAL(targetprop
);
4745 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4747 target
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(tv
));
4749 if (JSXML_LENGTH(target
) == 0) {
4750 if (base
->xml_class
== JSXML_CLASS_LIST
&& JSXML_LENGTH(base
) > 1) {
4754 tv
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4755 if (!PutProperty(cx
, base
->object
, id
, &tv
))
4757 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4759 target
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(tv
));
4767 HasNamedProperty(JSXML
*xml
, JSObject
*nameqn
)
4770 JSXMLArrayCursor cursor
;
4773 JSXMLNameMatcher matcher
;
4776 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4778 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
4779 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
4780 found
= HasNamedProperty(kid
, nameqn
);
4784 XMLArrayCursorFinish(&cursor
);
4788 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
4789 if (OBJ_GET_CLASS(cx
, nameqn
) == &js_AttributeNameClass
) {
4790 array
= &xml
->xml_attrs
;
4791 matcher
= MatchAttrName
;
4793 array
= &xml
->xml_kids
;
4794 matcher
= MatchElemName
;
4796 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
4797 kid
= XMLARRAY_MEMBER(array
, i
, JSXML
);
4798 if (kid
&& matcher(nameqn
, kid
))
4807 HasIndexedProperty(JSXML
*xml
, uint32 i
)
4809 if (xml
->xml_class
== JSXML_CLASS_LIST
)
4810 return i
< JSXML_LENGTH(xml
);
4812 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
)
4819 HasSimpleContent(JSXML
*xml
);
4822 HasFunctionProperty(JSContext
*cx
, JSObject
*obj
, jsid funid
, JSBool
*found
)
4827 JSTempValueRooter tvr
;
4830 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_XMLClass
);
4832 if (!js_LookupProperty(cx
, obj
, funid
, &pobj
, &prop
))
4835 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
4837 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
4838 if (HasSimpleContent(xml
)) {
4840 * Search in String.prototype to set found whenever
4841 * js_GetXMLFunction returns existing function.
4843 JS_PUSH_TEMP_ROOT_OBJECT(cx
, NULL
, &tvr
);
4844 ok
= js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(JSProto_String
),
4846 JS_ASSERT(tvr
.u
.object
);
4848 ok
= js_LookupProperty(cx
, tvr
.u
.object
, funid
, &pobj
, &prop
);
4850 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
4852 JS_POP_TEMP_ROOT(cx
, &tvr
);
4857 *found
= (prop
!= NULL
);
4861 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4863 HasProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, JSBool
*found
)
4870 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
4871 if (js_IdIsIndex(id
, &i
)) {
4872 *found
= HasIndexedProperty(xml
, i
);
4874 qn
= ToXMLName(cx
, id
, &funid
);
4878 if (!HasFunctionProperty(cx
, obj
, funid
, found
))
4881 *found
= HasNamedProperty(xml
, qn
);
4888 xml_finalize(JSContext
*cx
, JSObject
*obj
)
4892 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
4895 if (xml
->object
== obj
)
4900 xml_trace_vector(JSTracer
*trc
, JSXML
**vec
, uint32 len
)
4905 for (i
= 0; i
< len
; i
++) {
4908 JS_SET_TRACING_INDEX(trc
, "xml_vector", i
);
4909 JS_CallTracer(trc
, xml
, JSTRACE_XML
);
4915 * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
4916 * be native. Therefore, xml_lookupProperty must return a valid JSProperty
4917 * pointer parameter via *propp to signify "property found". Since the only
4918 * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
4919 * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case
4920 * in the interpreter, the only time we add a JSScopeProperty here is when an
4921 * unqualified name is being accessed or when "name in xml" is called.
4923 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4924 * giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
4926 * NB: xml_deleteProperty must take care to remove any property added here.
4928 * FIXME This clashes with the function namespace implementation which also
4929 * uses native properties. Effectively after xml_lookupProperty any property
4930 * stored previously using assignments to xml.function::name will be removed.
4931 * We partially workaround the problem in js_GetXMLFunction. There we take
4932 * advantage of the fact that typically function:: is used to access the
4933 * functions from XML.prototype. So when js_GetProperty returns a non-function
4934 * property, we assume that it represents the result of GetProperty setter
4935 * hiding the function and use an extra prototype chain lookup to recover it.
4936 * For a proper solution see bug 355257.
4939 xml_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4948 JSScopeProperty
*sprop
;
4950 v
= ID_TO_VALUE(id
);
4951 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
4952 if (js_IdIsIndex(v
, &i
)) {
4953 found
= HasIndexedProperty(xml
, i
);
4955 qn
= ToXMLName(cx
, v
, &funid
);
4959 return js_LookupProperty(cx
, obj
, funid
, objp
, propp
);
4960 found
= HasNamedProperty(xml
, qn
);
4966 sprop
= js_AddNativeProperty(cx
, obj
, id
, GetProperty
, PutProperty
,
4967 SPROP_INVALID_SLOT
, JSPROP_ENUMERATE
,
4972 JS_LOCK_OBJ(cx
, obj
);
4974 *propp
= (JSProperty
*) sprop
;
4980 xml_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval value
,
4981 JSPropertyOp getter
, JSPropertyOp setter
, uintN attrs
,
4984 if (VALUE_IS_FUNCTION(cx
, value
) || getter
|| setter
||
4985 (attrs
& JSPROP_ENUMERATE
) == 0 ||
4986 (attrs
& (JSPROP_READONLY
| JSPROP_PERMANENT
| JSPROP_SHARED
))) {
4987 return js_DefineProperty(cx
, obj
, id
, value
, getter
, setter
, attrs
,
4991 if (!PutProperty(cx
, obj
, ID_TO_VALUE(id
), &value
))
4999 xml_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5001 if (id
== JS_DEFAULT_XML_NAMESPACE_ID
) {
5006 return GetProperty(cx
, obj
, ID_TO_VALUE(id
), vp
);
5010 xml_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5012 return PutProperty(cx
, obj
, ID_TO_VALUE(id
), vp
);
5016 FoundProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
5020 return HasProperty(cx
, obj
, ID_TO_VALUE(id
), foundp
);
5027 xml_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
5032 if (!FoundProperty(cx
, obj
, id
, prop
, &found
))
5034 *attrsp
= found
? JSPROP_ENUMERATE
: 0;
5039 xml_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
5044 if (!FoundProperty(cx
, obj
, id
, prop
, &found
))
5047 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5048 JSMSG_CANT_SET_XML_ATTRS
);
5054 xml_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*rval
)
5062 idval
= ID_TO_VALUE(id
);
5063 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5064 if (js_IdIsIndex(idval
, &index
)) {
5065 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
5066 /* See NOTE in spec: this variation is reserved for future use. */
5067 ReportBadXMLName(cx
, id
);
5071 /* ECMA-357 9.2.1.3. */
5072 DeleteListElement(cx
, xml
, index
);
5074 nameqn
= ToXMLName(cx
, idval
, &funid
);
5078 return js_DeleteProperty(cx
, obj
, funid
, rval
);
5080 DeleteNamedProperty(cx
, xml
, nameqn
,
5081 OBJ_GET_CLASS(cx
, nameqn
) ==
5082 &js_AttributeNameClass
);
5086 * If this object has its own (mutable) scope, then we may have added a
5087 * property to the scope in xml_lookupProperty for it to return to mean
5088 * "found" and to provide a handle for access operations to call the
5089 * property's getter or setter. But now it's time to remove any such
5090 * property, to purge the property cache and remove the scope entry.
5092 if (OBJ_SCOPE(obj
)->object
== obj
&& !js_DeleteProperty(cx
, obj
, id
, rval
))
5100 xml_defaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, jsval
*vp
)
5104 if (hint
== JSTYPE_OBJECT
) {
5105 /* Called from for..in code in js_Interpret: return an XMLList. */
5106 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5107 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
5108 obj
= ToXMLList(cx
, OBJECT_TO_JSVAL(obj
));
5112 *vp
= OBJECT_TO_JSVAL(obj
);
5116 return JS_CallFunctionName(cx
, obj
, js_toString_str
, 0, NULL
, vp
);
5120 xml_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
5121 jsval
*statep
, jsid
*idp
)
5124 uint32 length
, index
;
5125 JSXMLArrayCursor
*cursor
;
5127 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5128 length
= JSXML_LENGTH(xml
);
5131 case JSENUMERATE_INIT
:
5135 cursor
= (JSXMLArrayCursor
*) JS_malloc(cx
, sizeof *cursor
);
5138 XMLArrayCursorInit(cursor
, &xml
->xml_kids
);
5140 *statep
= PRIVATE_TO_JSVAL(cursor
);
5142 *idp
= INT_TO_JSID(length
);
5145 case JSENUMERATE_NEXT
:
5146 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5147 if (cursor
&& cursor
->array
&& (index
= cursor
->index
) < length
) {
5148 *idp
= INT_TO_JSID(index
);
5149 cursor
->index
= index
+ 1;
5154 case JSENUMERATE_DESTROY
:
5155 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5157 XMLArrayCursorFinish(cursor
);
5158 JS_free(cx
, cursor
);
5160 *statep
= JSVAL_NULL
;
5167 xml_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
5173 xml_trace(JSTracer
*trc
, JSObject
*obj
)
5177 xml
= (JSXML
*) JS_GetPrivate(trc
->context
, obj
);
5179 JS_CALL_TRACER(trc
, xml
, JSTRACE_XML
, "private");
5183 xml_clear(JSContext
*cx
, JSObject
*obj
)
5188 HasSimpleContent(JSXML
*xml
)
5195 switch (xml
->xml_class
) {
5196 case JSXML_CLASS_COMMENT
:
5197 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
5199 case JSXML_CLASS_LIST
:
5200 if (xml
->xml_kids
.length
== 0)
5202 if (xml
->xml_kids
.length
== 1) {
5203 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5212 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5213 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5214 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5224 * 11.2.2.1 Step 3(d) onward.
5227 xml_getMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5229 JSTempValueRooter tvr
;
5231 JS_ASSERT(JS_InstanceOf(cx
, obj
, &js_XMLClass
, NULL
));
5234 * As our callers have a bad habit of passing a pointer to an unrooted
5235 * local value as vp, we use a proper root here.
5237 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
5238 if (!js_GetXMLFunction(cx
, obj
, id
, &tvr
.u
.value
))
5241 JS_POP_TEMP_ROOT(cx
, &tvr
);
5246 xml_setMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5248 return js_SetProperty(cx
, obj
, id
, vp
);
5252 xml_enumerateValues(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
5253 jsval
*statep
, jsid
*idp
, jsval
*vp
)
5256 uint32 length
, index
;
5257 JSXMLArrayCursor
*cursor
;
5260 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5261 length
= JSXML_LENGTH(xml
);
5262 JS_ASSERT(INT_FITS_IN_JSVAL(length
));
5265 case JSENUMERATE_INIT
:
5269 cursor
= (JSXMLArrayCursor
*) JS_malloc(cx
, sizeof *cursor
);
5272 XMLArrayCursorInit(cursor
, &xml
->xml_kids
);
5274 *statep
= PRIVATE_TO_JSVAL(cursor
);
5276 *idp
= INT_TO_JSID(length
);
5281 case JSENUMERATE_NEXT
:
5282 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5283 if (cursor
&& cursor
->array
&& (index
= cursor
->index
) < length
) {
5284 while (!(kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
))) {
5285 if (++index
== length
)
5288 kidobj
= js_GetXMLObject(cx
, kid
);
5291 JS_ASSERT(INT_FITS_IN_JSVAL(index
));
5292 *idp
= INT_TO_JSID(index
);
5293 *vp
= OBJECT_TO_JSVAL(kidobj
);
5294 cursor
->index
= index
+ 1;
5299 case JSENUMERATE_DESTROY
:
5300 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5303 XMLArrayCursorFinish(cursor
);
5304 JS_free(cx
, cursor
);
5306 *statep
= JSVAL_NULL
;
5313 xml_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
5318 JSString
*str
, *vstr
;
5321 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5323 if (!JSVAL_IS_PRIMITIVE(v
)) {
5324 vobj
= JSVAL_TO_OBJECT(v
);
5325 if (OBJECT_IS_XML(cx
, vobj
))
5326 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
5329 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5330 ok
= Equals(cx
, xml
, v
, bp
);
5332 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
5333 ok
= Equals(cx
, vxml
, OBJECT_TO_JSVAL(obj
), bp
);
5335 if (((xml
->xml_class
== JSXML_CLASS_TEXT
||
5336 xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5337 HasSimpleContent(vxml
)) ||
5338 ((vxml
->xml_class
== JSXML_CLASS_TEXT
||
5339 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5340 HasSimpleContent(xml
))) {
5341 ok
= js_EnterLocalRootScope(cx
);
5343 str
= js_ValueToString(cx
, OBJECT_TO_JSVAL(obj
));
5344 vstr
= js_ValueToString(cx
, v
);
5347 *bp
= js_EqualStrings(str
, vstr
);
5348 js_LeaveLocalRootScope(cx
);
5351 ok
= XMLEquals(cx
, xml
, vxml
, bp
);
5355 ok
= js_EnterLocalRootScope(cx
);
5357 if (HasSimpleContent(xml
)) {
5358 str
= js_ValueToString(cx
, OBJECT_TO_JSVAL(obj
));
5359 vstr
= js_ValueToString(cx
, v
);
5362 *bp
= js_EqualStrings(str
, vstr
);
5363 } else if (JSVAL_IS_STRING(v
) || JSVAL_IS_NUMBER(v
)) {
5364 str
= js_ValueToString(cx
, OBJECT_TO_JSVAL(obj
));
5367 } else if (JSVAL_IS_STRING(v
)) {
5368 *bp
= js_EqualStrings(str
, JSVAL_TO_STRING(v
));
5370 ok
= JS_ValueToNumber(cx
, STRING_TO_JSVAL(str
), &d
);
5372 d2
= JSVAL_IS_INT(v
) ? JSVAL_TO_INT(v
)
5373 : *JSVAL_TO_DOUBLE(v
);
5374 *bp
= JSDOUBLE_COMPARE(d
, ==, d2
, JS_FALSE
);
5380 js_LeaveLocalRootScope(cx
);
5387 xml_concatenate(JSContext
*cx
, JSObject
*obj
, jsval v
, jsval
*vp
)
5390 JSObject
*listobj
, *robj
;
5391 JSXML
*list
, *lxml
, *rxml
;
5393 ok
= js_EnterLocalRootScope(cx
);
5397 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5403 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
5404 lxml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5405 ok
= Append(cx
, list
, lxml
);
5409 if (VALUE_IS_XML(cx
, v
)) {
5410 rxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5412 robj
= ToXML(cx
, v
);
5417 rxml
= (JSXML
*) JS_GetPrivate(cx
, robj
);
5419 ok
= Append(cx
, list
, rxml
);
5423 *vp
= OBJECT_TO_JSVAL(listobj
);
5425 js_LeaveLocalRootScopeWithResult(cx
, *vp
);
5429 /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */
5430 JS_FRIEND_DATA(JSXMLObjectOps
) js_XMLObjectOps
= {
5431 { js_NewObjectMap
, js_DestroyObjectMap
,
5432 xml_lookupProperty
, xml_defineProperty
,
5433 xml_getProperty
, xml_setProperty
,
5434 xml_getAttributes
, xml_setAttributes
,
5435 xml_deleteProperty
, xml_defaultValue
,
5436 xml_enumerate
, js_CheckAccess
,
5439 NULL
, xml_hasInstance
,
5440 js_SetProtoOrParent
, js_SetProtoOrParent
,
5441 js_TraceObject
, xml_clear
,
5443 xml_getMethod
, xml_setMethod
,
5444 xml_enumerateValues
, xml_equality
,
5448 static JSObjectOps
*
5449 xml_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
5451 return &js_XMLObjectOps
.base
;
5454 JS_FRIEND_DATA(JSClass
) js_XMLClass
= {
5456 JSCLASS_HAS_PRIVATE
| JSCLASS_MARK_IS_TRACE
|
5457 JSCLASS_HAS_CACHED_PROTO(JSProto_XML
),
5458 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
5459 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, xml_finalize
,
5460 xml_getObjectOps
, NULL
, NULL
, NULL
,
5461 NULL
, NULL
, JS_CLASS_TRACE(xml_trace
), NULL
5465 StartNonListXMLMethod(JSContext
*cx
, jsval
*vp
, JSObject
**objp
)
5471 JS_ASSERT(VALUE_IS_FUNCTION(cx
, *vp
));
5473 *objp
= JS_THIS_OBJECT(cx
, vp
);
5474 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, *objp
, &js_XMLClass
, vp
+ 2);
5475 if (!xml
|| xml
->xml_class
!= JSXML_CLASS_LIST
)
5478 if (xml
->xml_kids
.length
== 1) {
5479 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5481 *objp
= js_GetXMLObject(cx
, xml
);
5484 vp
[1] = OBJECT_TO_JSVAL(*objp
);
5489 fun
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
));
5490 JS_snprintf(numBuf
, sizeof numBuf
, "%u", xml
->xml_kids
.length
);
5491 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5492 JSMSG_NON_LIST_XML_METHOD
,
5493 JS_GetFunctionName(fun
), numBuf
);
5497 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5498 #define XML_METHOD_PROLOG \
5499 JSObject *obj = JS_THIS_OBJECT(cx, vp); \
5500 JSXML *xml = (JSXML *)JS_GetInstancePrivate(cx, obj, &js_XMLClass, vp+2); \
5504 #define NON_LIST_XML_METHOD_PROLOG \
5506 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5509 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5512 xml_addNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
5516 NON_LIST_XML_METHOD_PROLOG
;
5517 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
5519 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5523 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
5525 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
5527 ns
= JSVAL_TO_OBJECT(*vp
);
5528 if (!AddInScopeNamespace(cx
, xml
, ns
))
5530 ns
->fslots
[JSSLOT_DECLARED
] = JSVAL_TRUE
;
5533 *vp
= OBJECT_TO_JSVAL(obj
);
5538 xml_appendChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
5544 NON_LIST_XML_METHOD_PROLOG
;
5545 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5549 if (!js_GetAnyName(cx
, &name
))
5552 if (!GetProperty(cx
, obj
, name
, &v
))
5555 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5556 vobj
= JSVAL_TO_OBJECT(v
);
5557 JS_ASSERT(OBJECT_IS_XML(cx
, vobj
));
5558 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
5559 JS_ASSERT(vxml
->xml_class
== JSXML_CLASS_LIST
);
5561 if (!IndexToIdVal(cx
, vxml
->xml_kids
.length
, &name
))
5563 *vp
= (argc
!= 0) ? vp
[2] : JSVAL_VOID
;
5564 if (!PutProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
))
5567 *vp
= OBJECT_TO_JSVAL(obj
);
5571 /* XML and XMLList */
5573 xml_attribute(JSContext
*cx
, uintN argc
, jsval
*vp
)
5578 js_ReportMissingArg(cx
, vp
, 0);
5582 qn
= ToAttributeName(cx
, vp
[2]);
5585 vp
[2] = OBJECT_TO_JSVAL(qn
); /* local root */
5587 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), vp
[2], vp
);
5590 /* XML and XMLList */
5592 xml_attributes(JSContext
*cx
, uintN argc
, jsval
*vp
)
5596 JSTempValueRooter tvr
;
5599 name
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
5600 qn
= ToAttributeName(cx
, name
);
5603 name
= OBJECT_TO_JSVAL(qn
);
5604 JS_PUSH_SINGLE_TEMP_ROOT(cx
, name
, &tvr
);
5605 ok
= GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), name
, vp
);
5606 JS_POP_TEMP_ROOT(cx
, &tvr
);
5611 xml_list_helper(JSContext
*cx
, JSXML
*xml
, jsval
*rval
)
5616 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5620 *rval
= OBJECT_TO_JSVAL(listobj
);
5621 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
5622 list
->xml_target
= xml
;
5627 xml_child_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval name
,
5634 /* ECMA-357 13.4.4.6 */
5635 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
5637 if (js_IdIsIndex(name
, &index
)) {
5638 if (index
>= JSXML_LENGTH(xml
)) {
5641 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
5645 kidobj
= js_GetXMLObject(cx
, kid
);
5648 *rval
= OBJECT_TO_JSVAL(kidobj
);
5654 return GetProperty(cx
, obj
, name
, rval
);
5657 /* XML and XMLList */
5659 xml_child(JSContext
*cx
, uintN argc
, jsval
*vp
)
5662 JSXML
*list
, *kid
, *vxml
;
5663 JSXMLArrayCursor cursor
;
5667 name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5668 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5669 /* ECMA-357 13.5.4.4 */
5670 list
= xml_list_helper(cx
, xml
, vp
);
5674 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
5675 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
5676 kidobj
= js_GetXMLObject(cx
, kid
);
5679 if (!xml_child_helper(cx
, kidobj
, kid
, name
, &v
))
5681 if (JSVAL_IS_VOID(v
)) {
5682 /* The property didn't exist in this kid. */
5686 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5687 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5688 if ((!JSXML_HAS_KIDS(vxml
) || vxml
->xml_kids
.length
!= 0) &&
5689 !Append(cx
, list
, vxml
)) {
5693 XMLArrayCursorFinish(&cursor
);
5697 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5698 if (!xml_child_helper(cx
, obj
, xml
, name
, vp
))
5700 if (JSVAL_IS_VOID(*vp
) && !xml_list_helper(cx
, xml
, vp
))
5706 xml_childIndex(JSContext
*cx
, uintN argc
, jsval
*vp
)
5711 NON_LIST_XML_METHOD_PROLOG
;
5712 parent
= xml
->parent
;
5713 if (!parent
|| xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
5714 *vp
= DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
5717 for (i
= 0, n
= JSXML_LENGTH(parent
); i
< n
; i
++) {
5718 if (XMLARRAY_MEMBER(&parent
->xml_kids
, i
, JSXML
) == xml
)
5722 return js_NewNumberInRootedValue(cx
, i
, vp
);
5725 /* XML and XMLList */
5727 xml_children(JSContext
*cx
, uintN argc
, jsval
*vp
)
5731 name
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
5732 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), name
, vp
);
5735 /* XML and XMLList */
5737 xml_comments_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
5739 JSXML
*list
, *kid
, *vxml
;
5745 list
= xml_list_helper(cx
, xml
, vp
);
5751 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5752 /* 13.5.4.6 Step 2. */
5753 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5754 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5755 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5756 ok
= js_EnterLocalRootScope(cx
);
5759 kidobj
= js_GetXMLObject(cx
, kid
);
5761 ok
= xml_comments_helper(cx
, kidobj
, kid
, &v
);
5766 js_LeaveLocalRootScopeWithResult(cx
, v
);
5769 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5770 if (JSXML_LENGTH(vxml
) != 0) {
5771 ok
= Append(cx
, list
, vxml
);
5778 /* 13.4.4.9 Step 2. */
5779 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5780 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5781 if (kid
&& kid
->xml_class
== JSXML_CLASS_COMMENT
) {
5782 ok
= Append(cx
, list
, kid
);
5793 xml_comments(JSContext
*cx
, uintN argc
, jsval
*vp
)
5796 return xml_comments_helper(cx
, obj
, xml
, vp
);
5799 /* XML and XMLList */
5801 xml_contains(JSContext
*cx
, uintN argc
, jsval
*vp
)
5805 JSXMLArrayCursor cursor
;
5810 value
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5811 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5813 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
5814 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
5815 kidobj
= js_GetXMLObject(cx
, kid
);
5816 if (!kidobj
|| !xml_equality(cx
, kidobj
, value
, &eq
))
5821 XMLArrayCursorFinish(&cursor
);
5825 if (!xml_equality(cx
, obj
, value
, &eq
))
5828 *vp
= BOOLEAN_TO_JSVAL(eq
);
5832 /* XML and XMLList */
5834 xml_copy(JSContext
*cx
, uintN argc
, jsval
*vp
)
5839 copy
= DeepCopy(cx
, xml
, NULL
, 0);
5842 *vp
= OBJECT_TO_JSVAL(copy
->object
);
5846 /* XML and XMLList */
5848 xml_descendants(JSContext
*cx
, uintN argc
, jsval
*vp
)
5854 name
= (argc
== 0) ? ATOM_KEY(cx
->runtime
->atomState
.starAtom
) : vp
[2];
5855 list
= Descendants(cx
, xml
, name
);
5858 *vp
= OBJECT_TO_JSVAL(list
->object
);
5862 /* XML and XMLList */
5864 xml_elements_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
5865 JSObject
*nameqn
, jsval
*vp
)
5867 JSXML
*list
, *kid
, *vxml
;
5870 JSXMLArrayCursor cursor
;
5874 list
= xml_list_helper(cx
, xml
, vp
);
5878 list
->xml_targetprop
= nameqn
;
5881 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5883 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
5884 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
5885 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5886 ok
= js_EnterLocalRootScope(cx
);
5889 kidobj
= js_GetXMLObject(cx
, kid
);
5891 ok
= xml_elements_helper(cx
, kidobj
, kid
, nameqn
, &v
);
5896 js_LeaveLocalRootScopeWithResult(cx
, v
);
5899 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5900 if (JSXML_LENGTH(vxml
) != 0) {
5901 ok
= Append(cx
, list
, vxml
);
5907 XMLArrayCursorFinish(&cursor
);
5909 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5910 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5911 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
5912 MatchElemName(nameqn
, kid
)) {
5913 ok
= Append(cx
, list
, kid
);
5924 xml_elements(JSContext
*cx
, uintN argc
, jsval
*vp
)
5932 name
= (argc
== 0) ? ATOM_KEY(cx
->runtime
->atomState
.starAtom
) : vp
[2];
5933 nameqn
= ToXMLName(cx
, name
, &funid
);
5936 vp
[2] = OBJECT_TO_JSVAL(nameqn
);
5939 return xml_list_helper(cx
, xml
, vp
) != NULL
;
5941 return xml_elements_helper(cx
, obj
, xml
, nameqn
, vp
);
5944 /* XML and XMLList */
5946 xml_hasOwnProperty(JSContext
*cx
, uintN argc
, jsval
*vp
)
5952 obj
= JS_THIS_OBJECT(cx
, vp
);
5953 if (!JS_InstanceOf(cx
, obj
, &js_XMLClass
, vp
+ 2))
5956 name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5957 if (!HasProperty(cx
, obj
, name
, &found
))
5963 return js_HasOwnPropertyHelper(cx
, js_LookupProperty
, argc
, vp
);
5966 /* XML and XMLList */
5968 xml_hasComplexContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
5976 switch (xml
->xml_class
) {
5977 case JSXML_CLASS_ATTRIBUTE
:
5978 case JSXML_CLASS_COMMENT
:
5979 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
5980 case JSXML_CLASS_TEXT
:
5983 case JSXML_CLASS_LIST
:
5984 if (xml
->xml_kids
.length
== 0) {
5986 } else if (xml
->xml_kids
.length
== 1) {
5987 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5989 kidobj
= js_GetXMLObject(cx
, kid
);
5993 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
6000 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6001 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6002 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6012 /* XML and XMLList */
6014 xml_hasSimpleContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6017 *vp
= BOOLEAN_TO_JSVAL(HasSimpleContent(xml
));
6021 typedef struct JSTempRootedNSArray
{
6022 JSTempValueRooter tvr
;
6024 jsval value
; /* extra root for temporaries */
6025 } JSTempRootedNSArray
;
6028 TraceObjectVector(JSTracer
*trc
, JSObject
**vec
, uint32 len
)
6033 for (i
= 0; i
< len
; i
++) {
6036 JS_SET_TRACING_INDEX(trc
, "vector", i
);
6037 JS_CallTracer(trc
, obj
, JSTRACE_OBJECT
);
6043 trace_temp_ns_array(JSTracer
*trc
, JSTempValueRooter
*tvr
)
6045 JSTempRootedNSArray
*tmp
= (JSTempRootedNSArray
*)tvr
;
6047 TraceObjectVector(trc
,
6048 (JSObject
**) tmp
->array
.vector
,
6050 XMLArrayCursorTrace(trc
, tmp
->array
.cursors
);
6051 JS_CALL_VALUE_TRACER(trc
, tmp
->value
, "temp_ns_array_value");
6055 InitTempNSArray(JSContext
*cx
, JSTempRootedNSArray
*tmp
)
6057 XMLArrayInit(cx
, &tmp
->array
, 0);
6058 tmp
->value
= JSVAL_NULL
;
6059 JS_PUSH_TEMP_ROOT_TRACE(cx
, trace_temp_ns_array
, &tmp
->tvr
);
6063 FinishTempNSArray(JSContext
*cx
, JSTempRootedNSArray
*tmp
)
6065 JS_ASSERT(tmp
->tvr
.u
.trace
== trace_temp_ns_array
);
6066 JS_POP_TEMP_ROOT(cx
, &tmp
->tvr
);
6067 XMLArrayFinish(cx
, &tmp
->array
);
6071 * Populate a new JS array with elements of JSTempRootedNSArray.array and
6072 * place the result into rval. rval must point to a rooted location.
6075 TempNSArrayToJSArray(JSContext
*cx
, JSTempRootedNSArray
*tmp
, jsval
*rval
)
6081 arrayobj
= js_NewArrayObject(cx
, 0, NULL
);
6084 *rval
= OBJECT_TO_JSVAL(arrayobj
);
6085 for (i
= 0, n
= tmp
->array
.length
; i
< n
; i
++) {
6086 ns
= XMLARRAY_MEMBER(&tmp
->array
, i
, JSObject
);
6089 tmp
->value
= OBJECT_TO_JSVAL(ns
);
6090 if (!OBJ_SET_PROPERTY(cx
, arrayobj
, INT_TO_JSID(i
), &tmp
->value
))
6097 FindInScopeNamespaces(JSContext
*cx
, JSXML
*xml
, JSXMLArray
*nsarray
)
6099 uint32 length
, i
, j
, n
;
6101 JSString
*prefix
, *prefix2
;
6103 length
= nsarray
->length
;
6105 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6107 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
6108 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
6112 prefix
= GetPrefix(ns
);
6113 for (j
= 0; j
< length
; j
++) {
6114 ns2
= XMLARRAY_MEMBER(nsarray
, j
, JSObject
);
6116 prefix2
= GetPrefix(ns2
);
6117 if ((prefix2
&& prefix
)
6118 ? js_EqualStrings(prefix2
, prefix
)
6119 : js_EqualStrings(GetURI(ns2
), GetURI(ns
))) {
6126 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
6131 } while ((xml
= xml
->parent
) != NULL
);
6132 JS_ASSERT(length
== nsarray
->length
);
6138 xml_inScopeNamespaces(JSContext
*cx
, uintN argc
, jsval
*vp
)
6140 JSTempRootedNSArray namespaces
;
6143 NON_LIST_XML_METHOD_PROLOG
;
6145 InitTempNSArray(cx
, &namespaces
);
6146 ok
= FindInScopeNamespaces(cx
, xml
, &namespaces
.array
) &&
6147 TempNSArrayToJSArray(cx
, &namespaces
, vp
);
6148 FinishTempNSArray(cx
, &namespaces
);
6153 xml_insertChildAfter(JSContext
*cx
, uintN argc
, jsval
*vp
)
6159 NON_LIST_XML_METHOD_PROLOG
;
6160 *vp
= OBJECT_TO_JSVAL(obj
);
6161 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
6165 if (JSVAL_IS_NULL(arg
)) {
6169 if (!VALUE_IS_XML(cx
, arg
))
6171 kid
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(arg
));
6172 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
6173 if (i
== XML_NOT_FOUND
)
6178 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6181 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
6185 xml_insertChildBefore(JSContext
*cx
, uintN argc
, jsval
*vp
)
6191 NON_LIST_XML_METHOD_PROLOG
;
6192 *vp
= OBJECT_TO_JSVAL(obj
);
6193 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
6197 if (JSVAL_IS_NULL(arg
)) {
6199 i
= xml
->xml_kids
.length
;
6201 if (!VALUE_IS_XML(cx
, arg
))
6203 kid
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(arg
));
6204 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
6205 if (i
== XML_NOT_FOUND
)
6209 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6212 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
6215 /* XML and XMLList */
6217 xml_length(JSContext
*cx
, uintN argc
, jsval
*vp
)
6220 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
6223 if (!js_NewNumberInRootedValue(cx
, xml
->xml_kids
.length
, vp
))
6230 xml_localName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6232 NON_LIST_XML_METHOD_PROLOG
;
6233 *vp
= xml
->name
? xml
->name
->fslots
[JSSLOT_LOCAL_NAME
] : JSVAL_NULL
;
6238 xml_name(JSContext
*cx
, uintN argc
, jsval
*vp
)
6240 NON_LIST_XML_METHOD_PROLOG
;
6241 *vp
= OBJECT_TO_JSVAL(xml
->name
);
6246 xml_namespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6248 JSString
*prefix
, *nsprefix
;
6249 JSTempRootedNSArray inScopeNSes
;
6254 NON_LIST_XML_METHOD_PROLOG
;
6255 if (argc
== 0 && !JSXML_HAS_NAME(xml
)) {
6263 prefix
= js_ValueToString(cx
, vp
[2]);
6266 vp
[2] = STRING_TO_JSVAL(prefix
); /* local root */
6269 InitTempNSArray(cx
, &inScopeNSes
);
6270 MUST_FLOW_THROUGH("out");
6271 ok
= FindInScopeNamespaces(cx
, xml
, &inScopeNSes
.array
);
6276 ns
= GetNamespace(cx
, xml
->name
, &inScopeNSes
.array
);
6283 for (i
= 0, length
= inScopeNSes
.array
.length
; i
< length
; i
++) {
6284 ns
= XMLARRAY_MEMBER(&inScopeNSes
.array
, i
, JSObject
);
6286 nsprefix
= GetPrefix(ns
);
6287 if (nsprefix
&& js_EqualStrings(nsprefix
, prefix
))
6294 *vp
= (!ns
) ? JSVAL_VOID
: OBJECT_TO_JSVAL(ns
);
6297 FinishTempNSArray(cx
, &inScopeNSes
);
6302 xml_namespaceDeclarations(JSContext
*cx
, uintN argc
, jsval
*vp
)
6305 JSTempRootedNSArray ancestors
, declared
;
6310 NON_LIST_XML_METHOD_PROLOG
;
6311 if (JSXML_HAS_VALUE(xml
))
6314 /* From here, control flow must goto out to finish these arrays. */
6316 InitTempNSArray(cx
, &ancestors
);
6317 InitTempNSArray(cx
, &declared
);
6320 while ((yml
= yml
->parent
) != NULL
) {
6321 JS_ASSERT(yml
->xml_class
== JSXML_CLASS_ELEMENT
);
6322 for (i
= 0, n
= yml
->xml_namespaces
.length
; i
< n
; i
++) {
6323 ns
= XMLARRAY_MEMBER(&yml
->xml_namespaces
, i
, JSObject
);
6325 !XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
6326 ok
= XMLARRAY_APPEND(cx
, &ancestors
.array
, ns
);
6333 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
6334 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
6337 if (!IsDeclared(ns
))
6339 if (!XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
6340 ok
= XMLARRAY_APPEND(cx
, &declared
.array
, ns
);
6346 ok
= TempNSArrayToJSArray(cx
, &declared
, vp
);
6349 /* Finishing must be in reverse order of initialization to follow LIFO. */
6350 FinishTempNSArray(cx
, &declared
);
6351 FinishTempNSArray(cx
, &ancestors
);
6355 static const char js_attribute_str
[] = "attribute";
6356 static const char js_text_str
[] = "text";
6358 /* Exported to jsgc.c #ifdef DEBUG. */
6359 const char *js_xml_class_str
[] = {
6363 "processing-instruction",
6369 xml_nodeKind(JSContext
*cx
, uintN argc
, jsval
*vp
)
6373 NON_LIST_XML_METHOD_PROLOG
;
6374 str
= JS_InternString(cx
, js_xml_class_str
[xml
->xml_class
]);
6377 *vp
= STRING_TO_JSVAL(str
);
6382 NormalizingDelete(JSContext
*cx
, JSXML
*xml
, uint32 index
)
6384 if (xml
->xml_class
== JSXML_CLASS_LIST
)
6385 DeleteListElement(cx
, xml
, index
);
6387 DeleteByIndex(cx
, xml
, index
);
6390 /* XML and XMLList */
6392 xml_normalize_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
)
6399 if (!JSXML_HAS_KIDS(xml
))
6402 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6406 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6407 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6410 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6411 kidobj
= js_GetXMLObject(cx
, kid
);
6412 if (!kidobj
|| !xml_normalize_helper(cx
, kidobj
, kid
))
6414 } else if (kid
->xml_class
== JSXML_CLASS_TEXT
) {
6416 (kid2
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
+ 1, JSXML
)) &&
6417 kid2
->xml_class
== JSXML_CLASS_TEXT
) {
6418 str
= js_ConcatStrings(cx
, kid
->xml_value
, kid2
->xml_value
);
6421 NormalizingDelete(cx
, xml
, i
+ 1);
6422 n
= xml
->xml_kids
.length
;
6423 kid
->xml_value
= str
;
6425 if (IS_EMPTY(kid
->xml_value
)) {
6426 NormalizingDelete(cx
, xml
, i
);
6427 n
= xml
->xml_kids
.length
;
6437 xml_normalize(JSContext
*cx
, uintN argc
, jsval
*vp
)
6440 *vp
= OBJECT_TO_JSVAL(obj
);
6441 return xml_normalize_helper(cx
, obj
, xml
);
6444 /* XML and XMLList */
6446 xml_parent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6448 JSXML
*parent
, *kid
;
6450 JSObject
*parentobj
;
6453 parent
= xml
->parent
;
6454 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6456 n
= xml
->xml_kids
.length
;
6460 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
6463 parent
= kid
->parent
;
6464 for (i
= 1; i
< n
; i
++) {
6465 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6466 if (kid
&& kid
->parent
!= parent
)
6476 parentobj
= js_GetXMLObject(cx
, parent
);
6479 *vp
= OBJECT_TO_JSVAL(parentobj
);
6483 /* XML and XMLList */
6485 xml_processingInstructions_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
6486 JSObject
*nameqn
, jsval
*vp
)
6488 JSXML
*list
, *kid
, *vxml
;
6490 JSXMLArrayCursor cursor
;
6494 JSString
*localName
;
6496 list
= xml_list_helper(cx
, xml
, vp
);
6500 list
->xml_targetprop
= nameqn
;
6503 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6504 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6505 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
6506 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
6507 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6508 ok
= js_EnterLocalRootScope(cx
);
6511 kidobj
= js_GetXMLObject(cx
, kid
);
6513 ok
= xml_processingInstructions_helper(cx
, kidobj
, kid
,
6519 js_LeaveLocalRootScopeWithResult(cx
, v
);
6522 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
6523 if (JSXML_LENGTH(vxml
) != 0) {
6524 ok
= Append(cx
, list
, vxml
);
6530 XMLArrayCursorFinish(&cursor
);
6532 /* 13.4.4.28 Step 4. */
6533 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6534 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6535 if (kid
&& kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
6536 localName
= GetLocalName(nameqn
);
6537 if (IS_STAR(localName
) ||
6538 js_EqualStrings(localName
, GetLocalName(kid
->name
))) {
6539 ok
= Append(cx
, list
, kid
);
6551 xml_processingInstructions(JSContext
*cx
, uintN argc
, jsval
*vp
)
6559 name
= (argc
== 0) ? ATOM_KEY(cx
->runtime
->atomState
.starAtom
) : vp
[2];
6560 nameqn
= ToXMLName(cx
, name
, &funid
);
6563 vp
[2] = OBJECT_TO_JSVAL(nameqn
);
6566 return xml_list_helper(cx
, xml
, vp
) != NULL
;
6568 return xml_processingInstructions_helper(cx
, obj
, xml
, nameqn
, vp
);
6572 xml_prependChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
6574 NON_LIST_XML_METHOD_PROLOG
;
6575 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6578 *vp
= OBJECT_TO_JSVAL(obj
);
6579 return Insert(cx
, xml
, 0, argc
!= 0 ? vp
[2] : JSVAL_VOID
);
6582 /* XML and XMLList */
6584 xml_propertyIsEnumerable(JSContext
*cx
, uintN argc
, jsval
*vp
)
6590 if (argc
!= 0 && js_IdIsIndex(vp
[2], &index
)) {
6591 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6593 *vp
= BOOLEAN_TO_JSVAL(index
< xml
->xml_kids
.length
);
6596 *vp
= BOOLEAN_TO_JSVAL(index
== 0);
6603 namespace_full_match(const void *a
, const void *b
)
6605 const JSObject
*nsa
= (const JSObject
*) a
;
6606 const JSObject
*nsb
= (const JSObject
*) b
;
6607 JSString
*prefixa
= GetPrefix(nsa
);
6611 prefixb
= GetPrefix(nsb
);
6612 if (prefixb
&& !js_EqualStrings(prefixa
, prefixb
))
6615 return js_EqualStrings(GetURI(nsa
), GetURI(nsb
));
6619 xml_removeNamespace_helper(JSContext
*cx
, JSXML
*xml
, JSObject
*ns
)
6621 JSObject
*thisns
, *attrns
;
6625 thisns
= GetNamespace(cx
, xml
->name
, &xml
->xml_namespaces
);
6630 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
6631 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
6634 attrns
= GetNamespace(cx
, attr
->name
, &xml
->xml_namespaces
);
6640 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_namespaces
, ns
, namespace_full_match
);
6641 if (i
!= XML_NOT_FOUND
)
6642 XMLArrayDelete(cx
, &xml
->xml_namespaces
, i
, JS_TRUE
);
6644 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6645 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6646 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6647 if (!xml_removeNamespace_helper(cx
, kid
, ns
))
6655 xml_removeNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6659 NON_LIST_XML_METHOD_PROLOG
;
6660 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6662 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6666 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
6668 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6669 ns
= JSVAL_TO_OBJECT(*vp
);
6671 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6672 if (!xml_removeNamespace_helper(cx
, xml
, ns
))
6675 *vp
= OBJECT_TO_JSVAL(obj
);
6680 xml_replace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6687 NON_LIST_XML_METHOD_PROLOG
;
6688 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6692 value
= STRING_TO_JSVAL(ATOM_TO_STRING(cx
->runtime
->atomState
.
6693 typeAtoms
[JSTYPE_VOID
]));
6696 vxml
= VALUE_IS_XML(cx
, value
)
6697 ? (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(value
))
6700 if (!JS_ConvertValue(cx
, value
, JSTYPE_STRING
, &vp
[3]))
6704 vxml
= DeepCopy(cx
, vxml
, NULL
, 0);
6707 value
= vp
[3] = OBJECT_TO_JSVAL(vxml
->object
);
6711 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6715 if (argc
== 0 || !js_IdIsIndex(vp
[2], &index
)) {
6717 * Call function QName per spec, not ToXMLName, to avoid attribute
6720 if (!QNameHelper(cx
, NULL
, &js_QNameClass
.base
, argc
== 0 ? -1 : 1,
6724 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6725 nameqn
= JSVAL_TO_OBJECT(*vp
);
6727 i
= xml
->xml_kids
.length
;
6728 index
= XML_NOT_FOUND
;
6731 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6732 if (kid
&& MatchElemName(nameqn
, kid
)) {
6733 if (i
!= XML_NOT_FOUND
)
6734 DeleteByIndex(cx
, xml
, i
);
6739 if (index
== XML_NOT_FOUND
)
6743 if (!Replace(cx
, xml
, index
, value
))
6747 *vp
= OBJECT_TO_JSVAL(obj
);
6752 xml_setChildren(JSContext
*cx
, uintN argc
, jsval
*vp
)
6756 if (!StartNonListXMLMethod(cx
, vp
, &obj
))
6759 *vp
= argc
!= 0 ? vp
[2] : JSVAL_VOID
; /* local root */
6760 if (!PutProperty(cx
, obj
, ATOM_KEY(cx
->runtime
->atomState
.starAtom
), vp
))
6763 *vp
= OBJECT_TO_JSVAL(obj
);
6768 xml_setLocalName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6774 NON_LIST_XML_METHOD_PROLOG
;
6775 if (!JSXML_HAS_NAME(xml
))
6779 namestr
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6782 if (!JSVAL_IS_PRIMITIVE(name
) &&
6783 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(name
)) == &js_QNameClass
.base
) {
6784 nameqn
= JSVAL_TO_OBJECT(name
);
6785 namestr
= GetLocalName(nameqn
);
6787 if (!JS_ConvertValue(cx
, name
, JSTYPE_STRING
, &vp
[2]))
6790 namestr
= JSVAL_TO_STRING(name
);
6794 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6797 xml
->name
->fslots
[JSSLOT_LOCAL_NAME
] = namestr
6798 ? STRING_TO_JSVAL(namestr
)
6804 xml_setName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6809 JSXMLArray
*nsarray
;
6813 NON_LIST_XML_METHOD_PROLOG
;
6814 if (!JSXML_HAS_NAME(xml
))
6818 name
= STRING_TO_JSVAL(ATOM_TO_STRING(cx
->runtime
->atomState
.
6819 typeAtoms
[JSTYPE_VOID
]));
6822 if (!JSVAL_IS_PRIMITIVE(name
) &&
6823 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(name
)) == &js_QNameClass
.base
&&
6824 !GetURI(nameqn
= JSVAL_TO_OBJECT(name
))) {
6825 name
= vp
[2] = nameqn
->fslots
[JSSLOT_LOCAL_NAME
];
6829 nameqn
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 1, &name
);
6833 /* ECMA-357 13.4.4.35 Step 4. */
6834 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
6835 nameqn
->fslots
[JSSLOT_URI
] = STRING_TO_JSVAL(cx
->runtime
->emptyString
);
6837 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6843 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6844 * in-scope namespaces, either by finding an in-scope namespace with a
6845 * matching uri and setting the new name's prefix to that namespace's
6846 * prefix, or by extending the in-scope namespaces for xml (which are in
6847 * xml->parent if xml is an attribute or a PI).
6849 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
6852 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
6854 nsowner
= xml
->parent
;
6857 if (GetPrefix(nameqn
)) {
6859 * The name being set has a prefix, which originally came from some
6860 * namespace object (which may be the null namespace, where both the
6861 * prefix and uri are the empty string). We must go through a full
6862 * GetNamespace in case that namespace is in-scope in nsowner.
6864 * If we find such an in-scope namespace, we return true right away,
6865 * in this block. Otherwise, we fall through to the final return of
6866 * AddInScopeNamespace(cx, nsowner, ns).
6868 ns
= GetNamespace(cx
, nameqn
, &nsowner
->xml_namespaces
);
6872 /* XXXbe have to test membership to see whether GetNamespace added */
6873 if (XMLARRAY_HAS_MEMBER(&nsowner
->xml_namespaces
, ns
, NULL
))
6877 * At this point, we know prefix of nameqn is null, so its uri can't
6878 * be the empty string (the null namespace always uses the empty string
6879 * for both prefix and uri).
6881 * This means we must inline GetNamespace and specialize it to match
6882 * uri only, never prefix. If we find a namespace with nameqn's uri
6883 * already in nsowner->xml_namespaces, then all that we need do is set
6884 * prefix of nameqn to that namespace's prefix.
6886 * If no such namespace exists, we can create one without going through
6887 * the constructor, because we know uri of nameqn is non-empty (so
6888 * prefix does not need to be converted from null to empty by QName).
6890 JS_ASSERT(!IS_EMPTY(GetURI(nameqn
)));
6892 nsarray
= &nsowner
->xml_namespaces
;
6893 for (i
= 0, n
= nsarray
->length
; i
< n
; i
++) {
6894 ns
= XMLARRAY_MEMBER(nsarray
, i
, JSObject
);
6895 if (ns
&& js_EqualStrings(GetURI(ns
), GetURI(nameqn
))) {
6896 nameqn
->fslots
[JSSLOT_PREFIX
] = ns
->fslots
[JSSLOT_PREFIX
];
6901 ns
= NewXMLNamespace(cx
, NULL
, GetURI(nameqn
), JS_TRUE
);
6906 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
6913 xml_setNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6920 NON_LIST_XML_METHOD_PROLOG
;
6921 if (!JSXML_HAS_NAME(xml
))
6924 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6928 ns
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, obj
,
6929 argc
== 0 ? 0 : 1, vp
+ 2);
6932 vp
[0] = OBJECT_TO_JSVAL(ns
);
6933 ns
->fslots
[JSSLOT_DECLARED
] = JSVAL_TRUE
;
6935 qnargv
[0] = vp
[2] = OBJECT_TO_JSVAL(ns
);
6936 qnargv
[1] = OBJECT_TO_JSVAL(xml
->name
);
6937 qn
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 2, qnargv
);
6944 * Erratum: the spec fails to update the governing in-scope namespaces.
6945 * See the erratum noted in xml_setName, above.
6947 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
6950 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
6952 nsowner
= xml
->parent
;
6954 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
6960 /* XML and XMLList */
6962 xml_text_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
6964 JSXML
*list
, *kid
, *vxml
;
6970 list
= xml_list_helper(cx
, xml
, vp
);
6974 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6976 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6977 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6978 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6979 ok
= js_EnterLocalRootScope(cx
);
6982 kidobj
= js_GetXMLObject(cx
, kid
);
6984 ok
= xml_text_helper(cx
, kidobj
, kid
, &v
);
6989 js_LeaveLocalRootScopeWithResult(cx
, v
);
6992 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
6993 if (JSXML_LENGTH(vxml
) != 0 && !Append(cx
, list
, vxml
))
6998 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6999 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
7000 if (kid
&& kid
->xml_class
== JSXML_CLASS_TEXT
) {
7001 if (!Append(cx
, list
, kid
))
7010 xml_text(JSContext
*cx
, uintN argc
, jsval
*vp
)
7013 return xml_text_helper(cx
, obj
, xml
, vp
);
7016 /* XML and XMLList */
7018 xml_toString_helper(JSContext
*cx
, JSXML
*xml
)
7020 JSString
*str
, *kidstr
;
7022 JSXMLArrayCursor cursor
;
7024 if (xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
||
7025 xml
->xml_class
== JSXML_CLASS_TEXT
) {
7026 return xml
->xml_value
;
7029 if (!HasSimpleContent(xml
))
7030 return ToXMLString(cx
, OBJECT_TO_JSVAL(xml
->object
), 0);
7032 str
= cx
->runtime
->emptyString
;
7033 if (!js_EnterLocalRootScope(cx
))
7035 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
7036 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
7037 if (kid
->xml_class
!= JSXML_CLASS_COMMENT
&&
7038 kid
->xml_class
!= JSXML_CLASS_PROCESSING_INSTRUCTION
) {
7039 kidstr
= xml_toString_helper(cx
, kid
);
7044 str
= js_ConcatStrings(cx
, str
, kidstr
);
7049 XMLArrayCursorFinish(&cursor
);
7050 js_LeaveLocalRootScopeWithResult(cx
, STRING_TO_JSVAL(str
));
7055 xml_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
7060 thisv
= JS_THIS(cx
, vp
);
7061 if (JSVAL_IS_NULL(thisv
))
7063 str
= ToXMLString(cx
, thisv
, TO_SOURCE_FLAG
);
7066 *vp
= STRING_TO_JSVAL(str
);
7071 xml_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
7076 str
= xml_toString_helper(cx
, xml
);
7079 *vp
= STRING_TO_JSVAL(str
);
7083 /* XML and XMLList */
7085 xml_toXMLString(JSContext
*cx
, uintN argc
, jsval
*vp
)
7090 thisv
= JS_THIS(cx
, vp
);
7091 if (JSVAL_IS_NULL(thisv
))
7093 str
= ToXMLString(cx
, thisv
, 0);
7096 *vp
= STRING_TO_JSVAL(str
);
7100 /* XML and XMLList */
7102 xml_valueOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
7104 *vp
= JS_THIS(cx
, vp
);
7105 return !JSVAL_IS_NULL(*vp
);
7108 static JSFunctionSpec xml_methods
[] = {
7109 JS_FN("addNamespace", xml_addNamespace
, 1,0),
7110 JS_FN("appendChild", xml_appendChild
, 1,0),
7111 JS_FN(js_attribute_str
, xml_attribute
, 1,0),
7112 JS_FN("attributes", xml_attributes
, 0,0),
7113 JS_FN("child", xml_child
, 1,0),
7114 JS_FN("childIndex", xml_childIndex
, 0,0),
7115 JS_FN("children", xml_children
, 0,0),
7116 JS_FN("comments", xml_comments
, 0,0),
7117 JS_FN("contains", xml_contains
, 1,0),
7118 JS_FN("copy", xml_copy
, 0,0),
7119 JS_FN("descendants", xml_descendants
, 1,0),
7120 JS_FN("elements", xml_elements
, 1,0),
7121 JS_FN("hasOwnProperty", xml_hasOwnProperty
, 1,0),
7122 JS_FN("hasComplexContent", xml_hasComplexContent
, 1,0),
7123 JS_FN("hasSimpleContent", xml_hasSimpleContent
, 1,0),
7124 JS_FN("inScopeNamespaces", xml_inScopeNamespaces
, 0,0),
7125 JS_FN("insertChildAfter", xml_insertChildAfter
, 2,0),
7126 JS_FN("insertChildBefore", xml_insertChildBefore
, 2,0),
7127 JS_FN(js_length_str
, xml_length
, 0,0),
7128 JS_FN(js_localName_str
, xml_localName
, 0,0),
7129 JS_FN(js_name_str
, xml_name
, 0,0),
7130 JS_FN(js_namespace_str
, xml_namespace
, 1,0),
7131 JS_FN("namespaceDeclarations", xml_namespaceDeclarations
, 0,0),
7132 JS_FN("nodeKind", xml_nodeKind
, 0,0),
7133 JS_FN("normalize", xml_normalize
, 0,0),
7134 JS_FN(js_xml_parent_str
, xml_parent
, 0,0),
7135 JS_FN("processingInstructions",xml_processingInstructions
,1,0),
7136 JS_FN("prependChild", xml_prependChild
, 1,0),
7137 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable
, 1,0),
7138 JS_FN("removeNamespace", xml_removeNamespace
, 1,0),
7139 JS_FN("replace", xml_replace
, 2,0),
7140 JS_FN("setChildren", xml_setChildren
, 1,0),
7141 JS_FN("setLocalName", xml_setLocalName
, 1,0),
7142 JS_FN("setName", xml_setName
, 1,0),
7143 JS_FN("setNamespace", xml_setNamespace
, 1,0),
7144 JS_FN(js_text_str
, xml_text
, 0,0),
7145 JS_FN(js_toSource_str
, xml_toSource
, 0,0),
7146 JS_FN(js_toString_str
, xml_toString
, 0,0),
7147 JS_FN(js_toXMLString_str
, xml_toXMLString
, 0,0),
7148 JS_FN(js_valueOf_str
, xml_valueOf
, 0,0),
7153 CopyXMLSettings(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
7159 for (i
= XML_IGNORE_COMMENTS
; i
< XML_PRETTY_INDENT
; i
++) {
7160 name
= xml_static_props
[i
].name
;
7161 if (!JS_GetProperty(cx
, from
, name
, &v
))
7163 if (JSVAL_IS_BOOLEAN(v
) && !JS_SetProperty(cx
, to
, name
, &v
))
7167 name
= xml_static_props
[i
].name
;
7168 if (!JS_GetProperty(cx
, from
, name
, &v
))
7170 if (JSVAL_IS_NUMBER(v
) && !JS_SetProperty(cx
, to
, name
, &v
))
7176 SetDefaultXMLSettings(JSContext
*cx
, JSObject
*obj
)
7181 for (i
= XML_IGNORE_COMMENTS
; i
< XML_PRETTY_INDENT
; i
++) {
7183 if (!JS_SetProperty(cx
, obj
, xml_static_props
[i
].name
, &v
))
7186 v
= INT_TO_JSVAL(2);
7187 return JS_SetProperty(cx
, obj
, xml_static_props
[i
].name
, &v
);
7191 xml_settings(JSContext
*cx
, uintN argc
, jsval
*vp
)
7196 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
7199 *vp
= OBJECT_TO_JSVAL(settings
);
7200 obj
= JS_THIS_OBJECT(cx
, vp
);
7201 return obj
&& CopyXMLSettings(cx
, obj
, settings
);
7205 xml_setSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
7207 JSObject
*obj
, *settings
;
7211 obj
= JS_THIS_OBJECT(cx
, vp
);
7214 v
= (argc
== 0) ? JSVAL_VOID
: vp
[2];
7215 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
7216 cx
->xmlSettingFlags
= 0;
7217 ok
= SetDefaultXMLSettings(cx
, obj
);
7219 if (JSVAL_IS_PRIMITIVE(v
))
7221 settings
= JSVAL_TO_OBJECT(v
);
7222 cx
->xmlSettingFlags
= 0;
7223 ok
= CopyXMLSettings(cx
, settings
, obj
);
7226 cx
->xmlSettingFlags
|= XSF_CACHE_VALID
;
7231 xml_defaultSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
7235 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
7238 *vp
= OBJECT_TO_JSVAL(settings
);
7239 return SetDefaultXMLSettings(cx
, settings
);
7242 static JSFunctionSpec xml_static_methods
[] = {
7243 JS_FN("settings", xml_settings
, 0,0),
7244 JS_FN("setSettings", xml_setSettings
, 1,0),
7245 JS_FN("defaultSettings", xml_defaultSettings
, 0,0),
7250 XML(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
7254 JSObject
*xobj
, *vobj
;
7258 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
7259 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
7261 xobj
= ToXML(cx
, v
);
7264 *rval
= OBJECT_TO_JSVAL(xobj
);
7265 xml
= (JSXML
*) JS_GetPrivate(cx
, xobj
);
7267 if (JS_IsConstructing(cx
) && !JSVAL_IS_PRIMITIVE(v
)) {
7268 vobj
= JSVAL_TO_OBJECT(v
);
7269 clasp
= OBJ_GET_CLASS(cx
, vobj
);
7270 if (clasp
== &js_XMLClass
||
7271 (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
)) {
7272 /* No need to lock obj, it's newly constructed and thread local. */
7273 copy
= DeepCopy(cx
, xml
, obj
, 0);
7276 JS_ASSERT(copy
->object
== obj
);
7277 *rval
= OBJECT_TO_JSVAL(obj
);
7285 XMLList(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
7288 JSObject
*vobj
, *listobj
;
7292 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
7293 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
7295 if (JS_IsConstructing(cx
) && !JSVAL_IS_PRIMITIVE(v
)) {
7296 vobj
= JSVAL_TO_OBJECT(v
);
7297 if (OBJECT_IS_XML(cx
, vobj
)) {
7298 xml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
7299 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7300 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
7303 *rval
= OBJECT_TO_JSVAL(listobj
);
7305 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
7306 if (!Append(cx
, list
, xml
))
7313 /* Toggle on XML support since the script has explicitly requested it. */
7314 listobj
= ToXMLList(cx
, v
);
7318 *rval
= OBJECT_TO_JSVAL(listobj
);
7322 #define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar))
7323 #define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLElemVar))
7324 #define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *))
7326 static size_t sizeof_JSXML
[JSXML_CLASS_LIMIT
] = {
7327 JSXML_LIST_SIZE
, /* JSXML_CLASS_LIST */
7328 JSXML_ELEMENT_SIZE
, /* JSXML_CLASS_ELEMENT */
7329 JSXML_LEAF_SIZE
, /* JSXML_CLASS_ATTRIBUTE */
7330 JSXML_LEAF_SIZE
, /* JSXML_CLASS_PROCESSING_INSTRUCTION */
7331 JSXML_LEAF_SIZE
, /* JSXML_CLASS_TEXT */
7332 JSXML_LEAF_SIZE
/* JSXML_CLASS_COMMENT */
7336 JSCList xml_leaks
= JS_INIT_STATIC_CLIST(&xml_leaks
);
7341 js_NewXML(JSContext
*cx
, JSXMLClass xml_class
)
7345 xml
= (JSXML
*) js_NewGCThing(cx
, GCX_XML
, sizeof_JSXML
[xml_class
]);
7350 xml
->domnode
= NULL
;
7353 xml
->xml_class
= xml_class
;
7355 if (JSXML_CLASS_HAS_VALUE(xml_class
)) {
7356 xml
->xml_value
= cx
->runtime
->emptyString
;
7358 XMLArrayInit(cx
, &xml
->xml_kids
, 0);
7359 if (xml_class
== JSXML_CLASS_LIST
) {
7360 xml
->xml_target
= NULL
;
7361 xml
->xml_targetprop
= NULL
;
7363 XMLArrayInit(cx
, &xml
->xml_namespaces
, 0);
7364 XMLArrayInit(cx
, &xml
->xml_attrs
, 0);
7369 JS_APPEND_LINK(&xml
->links
, &xml_leaks
);
7370 xml
->serial
= xml_serial
++;
7372 METER(xml_stats
.xml
);
7377 js_TraceXML(JSTracer
*trc
, JSXML
*xml
)
7380 JS_CALL_OBJECT_TRACER(trc
, xml
->object
, "object");
7382 JS_CALL_OBJECT_TRACER(trc
, xml
->name
, "name");
7384 JS_CALL_TRACER(trc
, xml
->parent
, JSTRACE_XML
, "xml_parent");
7386 if (JSXML_HAS_VALUE(xml
)) {
7388 JS_CALL_STRING_TRACER(trc
, xml
->xml_value
, "value");
7392 xml_trace_vector(trc
,
7393 (JSXML
**) xml
->xml_kids
.vector
,
7394 xml
->xml_kids
.length
);
7395 XMLArrayCursorTrace(trc
, xml
->xml_kids
.cursors
);
7396 if (IS_GC_MARKING_TRACER(trc
))
7397 XMLArrayTrim(&xml
->xml_kids
);
7399 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7400 if (xml
->xml_target
)
7401 JS_CALL_TRACER(trc
, xml
->xml_target
, JSTRACE_XML
, "target");
7402 if (xml
->xml_targetprop
)
7403 JS_CALL_OBJECT_TRACER(trc
, xml
->xml_targetprop
, "targetprop");
7405 TraceObjectVector(trc
,
7406 (JSObject
**) xml
->xml_namespaces
.vector
,
7407 xml
->xml_namespaces
.length
);
7408 XMLArrayCursorTrace(trc
, xml
->xml_namespaces
.cursors
);
7409 if (IS_GC_MARKING_TRACER(trc
))
7410 XMLArrayTrim(&xml
->xml_namespaces
);
7412 xml_trace_vector(trc
,
7413 (JSXML
**) xml
->xml_attrs
.vector
,
7414 xml
->xml_attrs
.length
);
7415 XMLArrayCursorTrace(trc
, xml
->xml_attrs
.cursors
);
7416 if (IS_GC_MARKING_TRACER(trc
))
7417 XMLArrayTrim(&xml
->xml_attrs
);
7422 js_FinalizeXML(JSContext
*cx
, JSXML
*xml
)
7424 if (JSXML_HAS_KIDS(xml
)) {
7425 XMLArrayFinish(cx
, &xml
->xml_kids
);
7426 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
7427 XMLArrayFinish(cx
, &xml
->xml_namespaces
);
7428 XMLArrayFinish(cx
, &xml
->xml_attrs
);
7433 JS_REMOVE_LINK(&xml
->links
);
7438 js_ParseNodeToXMLObject(JSContext
*cx
, JSParseContext
*pc
, JSParseNode
*pn
)
7445 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
7447 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval
));
7448 ns
= JSVAL_TO_OBJECT(nsval
);
7450 if (!XMLArrayInit(cx
, &nsarray
, 1))
7453 XMLARRAY_APPEND(cx
, &nsarray
, ns
);
7454 xml
= ParseNodeToXML(cx
, pc
, pn
, &nsarray
, XSF_PRECOMPILED_ROOT
);
7455 XMLArrayFinish(cx
, &nsarray
);
7463 js_NewXMLObject(JSContext
*cx
, JSXMLClass xml_class
)
7467 JSTempValueRooter tvr
;
7469 xml
= js_NewXML(cx
, xml_class
);
7472 JS_PUSH_TEMP_ROOT_XML(cx
, xml
, &tvr
);
7473 obj
= js_GetXMLObject(cx
, xml
);
7474 JS_POP_TEMP_ROOT(cx
, &tvr
);
7479 NewXMLObject(JSContext
*cx
, JSXML
*xml
)
7483 obj
= js_NewObject(cx
, &js_XMLClass
, NULL
, NULL
, 0);
7484 if (!obj
|| !JS_SetPrivate(cx
, obj
, xml
)) {
7485 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
7488 METER(xml_stats
.xmlobj
);
7493 js_GetXMLObject(JSContext
*cx
, JSXML
*xml
)
7499 JS_ASSERT(JS_GetPrivate(cx
, obj
) == xml
);
7504 * A JSXML cannot be shared among threads unless it has an object.
7505 * A JSXML cannot be given an object unless:
7506 * (a) it has no parent; or
7507 * (b) its parent has no object (therefore is thread-private); or
7508 * (c) its parent's object is locked.
7510 * Once given an object, a JSXML is immutable.
7512 JS_ASSERT(!xml
->parent
||
7513 !xml
->parent
->object
||
7514 JS_IS_OBJ_LOCKED(cx
, xml
->parent
->object
));
7516 obj
= NewXMLObject(cx
, xml
);
7524 js_InitNamespaceClass(JSContext
*cx
, JSObject
*obj
)
7526 return JS_InitClass(cx
, obj
, NULL
, &js_NamespaceClass
.base
, Namespace
, 2,
7527 namespace_props
, namespace_methods
, NULL
, NULL
);
7531 js_InitQNameClass(JSContext
*cx
, JSObject
*obj
)
7533 return JS_InitClass(cx
, obj
, NULL
, &js_QNameClass
.base
, QName
, 2,
7534 qname_props
, qname_methods
, NULL
, NULL
);
7538 js_InitAttributeNameClass(JSContext
*cx
, JSObject
*obj
)
7540 return JS_InitClass(cx
, obj
, NULL
, &js_AttributeNameClass
, AttributeName
, 2,
7541 qname_props
, qname_methods
, NULL
, NULL
);
7545 js_InitAnyNameClass(JSContext
*cx
, JSObject
*obj
)
7549 if (!js_GetAnyName(cx
, &v
))
7551 return JSVAL_TO_OBJECT(v
);
7555 js_InitXMLClass(JSContext
*cx
, JSObject
*obj
)
7557 JSObject
*proto
, *pobj
;
7561 JSScopeProperty
*sprop
;
7564 /* Define the isXMLName function. */
7565 if (!JS_DefineFunction(cx
, obj
, js_isXMLName_str
, xml_isXMLName
, 1, 0))
7568 /* Define the XML class constructor and prototype. */
7569 proto
= JS_InitClass(cx
, obj
, NULL
, &js_XMLClass
, XML
, 1,
7571 xml_static_props
, xml_static_methods
);
7575 xml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
7576 if (!xml
|| !JS_SetPrivate(cx
, proto
, xml
))
7578 xml
->object
= proto
;
7579 METER(xml_stats
.xmlobj
);
7582 * Prepare to set default settings on the XML constructor we just made.
7583 * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY,
7584 * which is xml_getProperty, which creates a new XMLList every time! We
7585 * must instead call js_LookupProperty directly.
7587 if (!js_LookupProperty(cx
, proto
,
7588 ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
7593 sprop
= (JSScopeProperty
*) prop
;
7594 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop
, OBJ_SCOPE(pobj
)));
7595 cval
= OBJ_GET_SLOT(cx
, pobj
, sprop
->slot
);
7596 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
7597 JS_ASSERT(VALUE_IS_FUNCTION(cx
, cval
));
7599 /* Set default settings. */
7603 if (!xml_setSettings(cx
, 1, vp
))
7606 /* Define the XMLList function and give it the same prototype as XML. */
7607 fun
= JS_DefineFunction(cx
, obj
, js_XMLList_str
, XMLList
, 1, 0);
7610 if (!js_SetClassPrototype(cx
, FUN_OBJECT(fun
), proto
,
7611 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
7618 js_InitXMLClasses(JSContext
*cx
, JSObject
*obj
)
7620 if (!js_InitNamespaceClass(cx
, obj
))
7622 if (!js_InitQNameClass(cx
, obj
))
7624 if (!js_InitAttributeNameClass(cx
, obj
))
7626 if (!js_InitAnyNameClass(cx
, obj
))
7628 if (!js_InitXMLFilterClass(cx
, obj
))
7630 return js_InitXMLClass(cx
, obj
);
7634 js_GetFunctionNamespace(JSContext
*cx
, jsval
*vp
)
7639 JSString
*prefix
, *uri
;
7641 /* An invalid URI, for internal use only, guaranteed not to collide. */
7642 static const char anti_uri
[] = "@mozilla.org/js/function";
7644 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7646 obj
= rt
->functionNamespaceObject
;
7649 obj
= rt
->functionNamespaceObject
;
7654 * Note that any race to atomize anti_uri here is resolved by
7655 * the atom table code, such that at most one atom for anti_uri
7656 * is created. We store in rt->atomState.lazy unconditionally,
7657 * since we are guaranteed to overwrite either null or the same
7660 atom
= js_Atomize(cx
, anti_uri
, sizeof anti_uri
- 1, ATOM_PINNED
);
7663 rt
->atomState
.lazy
.functionNamespaceURIAtom
= atom
;
7665 prefix
= ATOM_TO_STRING(rt
->atomState
.typeAtoms
[JSTYPE_FUNCTION
]);
7666 uri
= ATOM_TO_STRING(atom
);
7667 obj
= NewXMLNamespace(cx
, prefix
, uri
, JS_FALSE
);
7672 * Avoid entraining any in-scope Object.prototype. The loss of
7673 * Namespace.prototype is not detectable, as there is no way to
7674 * refer to this instance in scripts. When used to qualify method
7675 * names, its prefix and uri references are copied to the QName.
7677 OBJ_CLEAR_PROTO(cx
, obj
);
7678 OBJ_CLEAR_PARENT(cx
, obj
);
7681 if (!rt
->functionNamespaceObject
)
7682 rt
->functionNamespaceObject
= obj
;
7684 obj
= rt
->functionNamespaceObject
;
7688 *vp
= OBJECT_TO_JSVAL(obj
);
7693 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7694 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7695 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a
7696 * lightweight function activation). There's no requirement that fp->varobj
7697 * lie directly on fp->scopeChain, although it should be reachable using the
7698 * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h).
7700 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7701 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7702 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7703 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7704 * the default XML namespace will be set to ("", n.uri). So the uri string
7705 * is really the only usefully stored value of the default namespace.
7708 js_GetDefaultXMLNamespace(JSContext
*cx
, jsval
*vp
)
7711 JSObject
*ns
, *obj
, *tmp
;
7714 fp
= js_GetTopStackFrame(cx
);
7715 ns
= fp
->xmlNamespace
;
7717 *vp
= OBJECT_TO_JSVAL(ns
);
7722 for (tmp
= fp
->scopeChain
; tmp
; tmp
= OBJ_GET_PARENT(cx
, obj
)) {
7724 if (!OBJ_GET_PROPERTY(cx
, obj
, JS_DEFAULT_XML_NAMESPACE_ID
, &v
))
7726 if (!JSVAL_IS_PRIMITIVE(v
)) {
7727 fp
->xmlNamespace
= JSVAL_TO_OBJECT(v
);
7733 ns
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, obj
, 0, NULL
);
7736 v
= OBJECT_TO_JSVAL(ns
);
7738 !OBJ_DEFINE_PROPERTY(cx
, obj
, JS_DEFAULT_XML_NAMESPACE_ID
, v
,
7739 JS_PropertyStub
, JS_PropertyStub
,
7740 JSPROP_PERMANENT
, NULL
)) {
7743 fp
->xmlNamespace
= ns
;
7749 js_SetDefaultXMLNamespace(JSContext
*cx
, jsval v
)
7752 JSObject
*ns
, *varobj
;
7755 argv
[0] = STRING_TO_JSVAL(cx
->runtime
->emptyString
);
7757 ns
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
, 2, argv
);
7760 v
= OBJECT_TO_JSVAL(ns
);
7762 fp
= js_GetTopStackFrame(cx
);
7763 varobj
= fp
->varobj
;
7765 if (!OBJ_DEFINE_PROPERTY(cx
, varobj
, JS_DEFAULT_XML_NAMESPACE_ID
, v
,
7766 JS_PropertyStub
, JS_PropertyStub
,
7767 JSPROP_PERMANENT
, NULL
)) {
7771 JS_ASSERT(fp
->fun
&& !JSFUN_HEAVYWEIGHT_TEST(fp
->fun
->flags
));
7773 fp
->xmlNamespace
= JSVAL_TO_OBJECT(v
);
7778 js_ToAttributeName(JSContext
*cx
, jsval
*vp
)
7782 qn
= ToAttributeName(cx
, *vp
);
7785 *vp
= OBJECT_TO_JSVAL(qn
);
7790 js_EscapeAttributeValue(JSContext
*cx
, JSString
*str
, JSBool quote
)
7792 return EscapeAttributeValue(cx
, NULL
, str
, quote
);
7796 js_AddAttributePart(JSContext
*cx
, JSBool isName
, JSString
*str
, JSString
*str2
)
7798 size_t len
, len2
, newlen
;
7799 jschar
*chars
, *chars2
;
7801 JSSTRING_CHARS_AND_LENGTH(str
, chars
, len
);
7802 if (!JSSTRING_IS_MUTABLE(str
)) {
7803 str
= js_NewStringCopyN(cx
, chars
, len
);
7806 chars
= JSFLATSTR_CHARS(str
);
7809 * Reallocating str (because we know it has no other references)
7810 * requires purging any deflated string cached for it.
7812 js_PurgeDeflatedStringCache(cx
->runtime
, str
);
7815 JSSTRING_CHARS_AND_LENGTH(str2
, chars2
, len2
);
7816 newlen
= (isName
) ? len
+ 1 + len2
: len
+ 2 + len2
+ 1;
7817 chars
= (jschar
*) JS_realloc(cx
, chars
, (newlen
+1) * sizeof(jschar
));
7821 JSFLATSTR_INIT(str
, chars
, newlen
);
7825 js_strncpy(chars
, chars2
, len2
);
7830 js_strncpy(chars
, chars2
, len2
);
7839 js_EscapeElementValue(JSContext
*cx
, JSString
*str
)
7841 return EscapeElementValue(cx
, NULL
, str
);
7845 js_ValueToXMLString(JSContext
*cx
, jsval v
)
7847 return ToXMLString(cx
, v
, 0);
7851 anyname_toString(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
7854 *rval
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
7859 js_GetAnyName(JSContext
*cx
, jsval
*vp
)
7865 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7867 obj
= rt
->anynameObject
;
7870 obj
= rt
->anynameObject
;
7875 * Protect multiple newborns created below, in the do-while(0)
7876 * loop used to ensure that we leave this local root scope.
7878 ok
= js_EnterLocalRootScope(cx
);
7883 obj
= js_NewObjectWithGivenProto(cx
, &js_AnyNameClass
, NULL
,
7889 InitXMLQName(obj
, rt
->emptyString
, rt
->emptyString
,
7890 ATOM_TO_STRING(rt
->atomState
.starAtom
));
7891 METER(xml_stats
.qname
);
7894 * Avoid entraining any Object.prototype found via cx's scope
7895 * chain or global object. This loses the default toString,
7896 * but no big deal: we want to customize toString anyway for
7897 * clearer diagnostics.
7899 if (!JS_DefineFunction(cx
, obj
, js_toString_str
,
7900 anyname_toString
, 0, 0)) {
7904 JS_ASSERT(!OBJ_GET_PROTO(cx
, obj
));
7905 JS_ASSERT(!OBJ_GET_PARENT(cx
, obj
));
7908 js_LeaveLocalRootScopeWithResult(cx
, OBJECT_TO_JSVAL(obj
));
7913 if (!rt
->anynameObject
)
7914 rt
->anynameObject
= obj
;
7916 obj
= rt
->anynameObject
;
7920 *vp
= OBJECT_TO_JSVAL(obj
);
7925 js_FindXMLProperty(JSContext
*cx
, jsval nameval
, JSObject
**objp
, jsid
*idp
)
7931 JSObject
*obj
, *target
, *proto
, *pobj
;
7935 const char *printable
;
7937 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nameval
));
7938 nameobj
= JSVAL_TO_OBJECT(nameval
);
7939 if (OBJ_GET_CLASS(cx
, nameobj
) == &js_AnyNameClass
) {
7940 v
= STRING_TO_JSVAL(ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
));
7941 nameobj
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 1,
7946 JS_ASSERT(OBJ_GET_CLASS(cx
, nameobj
) == &js_AttributeNameClass
||
7947 OBJ_GET_CLASS(cx
, nameobj
) == &js_QNameClass
.base
);
7951 if (!IsFunctionQName(cx
, qn
, &funid
))
7954 obj
= js_GetTopStackFrame(cx
)->scopeChain
;
7956 /* Skip any With object that can wrap XML. */
7958 while (OBJ_GET_CLASS(cx
, target
) == &js_WithClass
) {
7959 proto
= OBJ_GET_PROTO(cx
, target
);
7965 if (OBJECT_IS_XML(cx
, target
)) {
7967 xml
= (JSXML
*) JS_GetPrivate(cx
, target
);
7968 found
= HasNamedProperty(xml
, qn
);
7970 if (!HasFunctionProperty(cx
, target
, funid
, &found
))
7974 *idp
= OBJECT_TO_JSID(nameobj
);
7978 } else if (funid
!= 0) {
7979 if (!OBJ_LOOKUP_PROPERTY(cx
, target
, funid
, &pobj
, &prop
))
7982 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
7988 } while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
);
7990 printable
= js_ValueToPrintableString(cx
, OBJECT_TO_JSVAL(nameobj
));
7992 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
7993 js_GetErrorMessage
, NULL
,
7994 JSMSG_UNDEFINED_XML_NAME
, printable
);
8000 js_GetXMLFunction(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
8004 JSTempValueRooter tvr
;
8007 JS_ASSERT(OBJECT_IS_XML(cx
, obj
));
8009 MUST_FLOW_THROUGH("out");
8010 JS_PUSH_TEMP_ROOT_OBJECT(cx
, NULL
, &tvr
);
8013 * See comments before xml_lookupProperty about the need for the proto
8018 ok
= js_GetProperty(cx
, target
, id
, vp
);
8021 if (VALUE_IS_FUNCTION(cx
, *vp
)) {
8025 target
= OBJ_GET_PROTO(cx
, target
);
8028 tvr
.u
.object
= target
;
8031 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8032 if (HasSimpleContent(xml
)) {
8033 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
8034 ok
= js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(JSProto_String
),
8038 JS_ASSERT(tvr
.u
.object
);
8039 ok
= OBJ_GET_PROPERTY(cx
, tvr
.u
.object
, id
, vp
);
8043 JS_POP_TEMP_ROOT(cx
, &tvr
);
8048 GetPrivate(JSContext
*cx
, JSObject
*obj
, const char *method
)
8052 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
8054 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
8055 JSMSG_INCOMPATIBLE_METHOD
,
8056 js_XML_str
, method
, OBJ_GET_CLASS(cx
, obj
)->name
);
8062 js_GetXMLDescendants(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
8066 xml
= GetPrivate(cx
, obj
, "descendants internal method");
8070 list
= Descendants(cx
, xml
, id
);
8073 *vp
= OBJECT_TO_JSVAL(list
->object
);
8078 js_DeleteXMLListElements(JSContext
*cx
, JSObject
*listobj
)
8083 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
8084 for (n
= list
->xml_kids
.length
; n
!= 0; --n
)
8085 DeleteListElement(cx
, list
, 0);
8090 typedef struct JSXMLFilter
{
8094 JSXMLArrayCursor cursor
;
8099 xmlfilter_trace(JSTracer
*trc
, JSObject
*obj
)
8101 JSXMLFilter
*filter
;
8103 filter
= (JSXMLFilter
*) JS_GetPrivate(trc
->context
, obj
);
8107 JS_ASSERT(filter
->list
);
8108 JS_CALL_TRACER(trc
, filter
->list
, JSTRACE_XML
, "list");
8110 JS_CALL_TRACER(trc
, filter
->result
, JSTRACE_XML
, "result");
8112 JS_CALL_TRACER(trc
, filter
->kid
, JSTRACE_XML
, "kid");
8115 * We do not need to trace the cursor as that would be done when
8116 * tracing the filter->list.
8121 xmlfilter_finalize(JSContext
*cx
, JSObject
*obj
)
8123 JSXMLFilter
*filter
;
8125 filter
= (JSXMLFilter
*) JS_GetPrivate(cx
, obj
);
8129 XMLArrayCursorFinish(&filter
->cursor
);
8130 JS_free(cx
, filter
);
8133 JSClass js_XMLFilterClass
= {
8135 JSCLASS_HAS_PRIVATE
|
8136 JSCLASS_IS_ANONYMOUS
|
8137 JSCLASS_MARK_IS_TRACE
|
8138 JSCLASS_HAS_CACHED_PROTO(JSProto_XMLFilter
),
8139 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
8140 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, xmlfilter_finalize
,
8141 NULL
, NULL
, NULL
, NULL
,
8142 NULL
, NULL
, JS_CLASS_TRACE(xmlfilter_trace
), NULL
8146 js_InitXMLFilterClass(JSContext
*cx
, JSObject
*obj
)
8150 proto
= JS_InitClass(cx
, obj
, NULL
, &js_XMLFilterClass
, NULL
, 0, NULL
,
8155 OBJ_CLEAR_PROTO(cx
, proto
);
8160 js_StepXMLListFilter(JSContext
*cx
, JSBool initialized
)
8163 JSObject
*obj
, *filterobj
, *resobj
, *kidobj
;
8165 JSXMLFilter
*filter
;
8167 sp
= js_GetTopStackFrame(cx
)->regs
->sp
;
8170 * We haven't iterated yet, so initialize the filter based on the
8171 * value stored in sp[-2].
8173 if (!VALUE_IS_XML(cx
, sp
[-2])) {
8174 js_ReportValueError(cx
, JSMSG_NON_XML_FILTER
, -2, sp
[-2], NULL
);
8177 obj
= JSVAL_TO_OBJECT(sp
[-2]);
8178 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8180 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
8183 obj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
8188 * Root just-created obj. sp[-2] cannot be used yet for rooting
8189 * as it may be the only root holding xml.
8191 sp
[-1] = OBJECT_TO_JSVAL(obj
);
8192 list
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8193 if (!Append(cx
, list
, xml
))
8197 filterobj
= js_NewObject(cx
, &js_XMLFilterClass
, NULL
, NULL
, 0);
8201 filter
= (JSXMLFilter
*) JS_malloc(cx
, sizeof *filter
);
8206 * Init all filter fields before JS_SetPrivate exposes it to
8207 * xmlfilter_trace or xmlfilter_finalize.
8209 filter
->list
= list
;
8210 filter
->result
= NULL
;
8212 XMLArrayCursorInit(&filter
->cursor
, &list
->xml_kids
);
8213 JS_SetPrivate(cx
, filterobj
, filter
);
8215 /* Store filterobj to use in the later iterations. */
8216 sp
[-2] = OBJECT_TO_JSVAL(filterobj
);
8218 resobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
8222 /* This also roots resobj. */
8223 filter
->result
= (JSXML
*) JS_GetPrivate(cx
, resobj
);
8225 /* We have iterated at least once. */
8226 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp
[-2]));
8227 JS_ASSERT(OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(sp
[-2])) ==
8228 &js_XMLFilterClass
);
8229 filter
= (JSXMLFilter
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(sp
[-2]));
8230 JS_ASSERT(filter
->kid
);
8232 /* Check if the filter expression wants to append the element. */
8233 if (js_ValueToBoolean(sp
[-1]) &&
8234 !Append(cx
, filter
->result
, filter
->kid
)) {
8239 /* Do the iteration. */
8240 filter
->kid
= (JSXML
*) XMLArrayCursorNext(&filter
->cursor
);
8243 * Do not defer finishing the cursor until the next GC cycle to avoid
8244 * accumulation of dead cursors associated with filter->list.
8246 XMLArrayCursorFinish(&filter
->cursor
);
8247 JS_ASSERT(filter
->result
->object
);
8248 sp
[-2] = OBJECT_TO_JSVAL(filter
->result
->object
);
8251 kidobj
= js_GetXMLObject(cx
, filter
->kid
);
8256 /* Null as kidobj at sp[-1] signals filter termination. */
8257 sp
[-1] = OBJECT_TO_JSVAL(kidobj
);
8262 js_ValueToXMLObject(JSContext
*cx
, jsval v
)
8264 return ToXML(cx
, v
);
8268 js_ValueToXMLListObject(JSContext
*cx
, jsval v
)
8270 return ToXMLList(cx
, v
);
8274 js_CloneXMLObject(JSContext
*cx
, JSObject
*obj
)
8279 if (!GetXMLSettingFlags(cx
, &flags
))
8281 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8282 if (flags
& (XSF_IGNORE_COMMENTS
|
8283 XSF_IGNORE_PROCESSING_INSTRUCTIONS
|
8284 XSF_IGNORE_WHITESPACE
)) {
8285 xml
= DeepCopy(cx
, xml
, NULL
, flags
);
8290 return NewXMLObject(cx
, xml
);
8294 js_NewXMLSpecialObject(JSContext
*cx
, JSXMLClass xml_class
, JSString
*name
,
8302 if (!GetXMLSettingFlags(cx
, &flags
))
8305 if ((xml_class
== JSXML_CLASS_COMMENT
&&
8306 (flags
& XSF_IGNORE_COMMENTS
)) ||
8307 (xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
&&
8308 (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
))) {
8309 return js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
8312 obj
= js_NewXMLObject(cx
, xml_class
);
8315 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8317 qn
= NewXMLQName(cx
, cx
->runtime
->emptyString
, NULL
, name
);
8322 xml
->xml_value
= value
;
8327 js_MakeXMLCDATAString(JSContext
*cx
, JSString
*str
)
8329 return MakeXMLCDATAString(cx
, NULL
, str
);
8333 js_MakeXMLCommentString(JSContext
*cx
, JSString
*str
)
8335 return MakeXMLCommentString(cx
, NULL
, str
);
8339 js_MakeXMLPIString(JSContext
*cx
, JSString
*name
, JSString
*str
)
8341 return MakeXMLPIString(cx
, NULL
, name
, str
);
8344 #endif /* JS_HAS_XML_SUPPORT */