1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is SpiderMonkey E4X code, released August, 2004.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
43 #if JS_HAS_XML_SUPPORT
72 #include <string.h> /* for #ifdef DEBUG memset calls */
77 * - in the js shell, you must use the -x command line option, or call
78 * options('xml') before compiling anything that uses XML literals
82 * - Fuse objects and their JSXML* private data into single GC-things
83 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
84 * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
85 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
86 * - JS_TypeOfValue sure could use a cleaner interface to "types"
94 jsrefcount liveqnameobj
;
96 jsrefcount namespaceobj
;
97 jsrefcount livenamespace
;
98 jsrefcount livenamespaceobj
;
102 jsrefcount livexmlobj
;
105 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
106 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
108 #define METER(x) /* nothing */
109 #define UNMETER(x) /* nothing */
113 * Random utilities and global functions.
115 const char js_isXMLName_str
[] = "isXMLName";
116 const char js_XMLList_str
[] = "XMLList";
117 const char js_localName_str
[] = "localName";
118 const char js_xml_parent_str
[] = "parent";
119 const char js_prefix_str
[] = "prefix";
120 const char js_toXMLString_str
[] = "toXMLString";
121 const char js_uri_str
[] = "uri";
123 const char js_amp_entity_str
[] = "&";
124 const char js_gt_entity_str
[] = ">";
125 const char js_lt_entity_str
[] = "<";
126 const char js_quot_entity_str
[] = """;
128 #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
129 #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
132 xml_isXMLName(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
135 *rval
= BOOLEAN_TO_JSVAL(js_IsXMLName(cx
, argv
[0]));
140 * Namespace class and library functions.
142 enum namespace_tinyid
{
143 NAMESPACE_PREFIX
= -1,
148 namespace_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
152 if (!JSVAL_IS_INT(id
))
155 ns
= (JSXMLNamespace
*)
156 JS_GetInstancePrivate(cx
, obj
, &js_NamespaceClass
.base
, NULL
);
160 switch (JSVAL_TO_INT(id
)) {
161 case NAMESPACE_PREFIX
:
162 *vp
= ns
->prefix
? STRING_TO_JSVAL(ns
->prefix
) : JSVAL_VOID
;
165 *vp
= STRING_TO_JSVAL(ns
->uri
);
172 namespace_finalize(JSContext
*cx
, JSObject
*obj
)
177 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, obj
);
180 JS_ASSERT(ns
->object
== obj
);
182 UNMETER(xml_stats
.livenamespaceobj
);
185 if (rt
->functionNamespaceObject
== obj
)
186 rt
->functionNamespaceObject
= NULL
;
190 namespace_trace_vector(JSTracer
*trc
, JSXMLNamespace
**vec
,
196 for (i
= 0; i
< len
; i
++) {
199 JS_SET_TRACING_INDEX(trc
, "namespace_vector", i
);
200 JS_CallTracer(trc
, ns
, JSTRACE_NAMESPACE
);
206 namespace_trace(JSTracer
*trc
, JSObject
*obj
)
210 ns
= (JSXMLNamespace
*) JS_GetPrivate(trc
->context
, obj
);
212 JS_CALL_TRACER(trc
, ns
, JSTRACE_NAMESPACE
, "private");
216 namespace_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
218 JSXMLNamespace
*ns
, *ns2
;
221 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, obj
);
222 JS_ASSERT(JSVAL_IS_OBJECT(v
));
223 obj2
= JSVAL_TO_OBJECT(v
);
224 if (!obj2
|| OBJ_GET_CLASS(cx
, obj2
) != &js_NamespaceClass
.base
) {
227 ns2
= (JSXMLNamespace
*) JS_GetPrivate(cx
, obj2
);
228 *bp
= js_EqualStrings(ns
->uri
, ns2
->uri
);
233 JS_FRIEND_DATA(JSExtendedClass
) js_NamespaceClass
= {
235 JSCLASS_HAS_PRIVATE
| JSCLASS_CONSTRUCT_PROTOTYPE
| JSCLASS_IS_EXTENDED
|
236 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace
),
237 JS_PropertyStub
, JS_PropertyStub
, namespace_getProperty
, NULL
,
238 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, namespace_finalize
,
239 NULL
, NULL
, NULL
, NULL
,
240 NULL
, NULL
, JS_CLASS_TRACE(namespace_trace
), NULL
},
241 namespace_equality
,NULL
, NULL
, NULL
,
242 NULL
, NULL
, NULL
, NULL
245 #define NAMESPACE_ATTRS \
246 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
248 static JSPropertySpec namespace_props
[] = {
249 {js_prefix_str
, NAMESPACE_PREFIX
, NAMESPACE_ATTRS
, 0, 0},
250 {js_uri_str
, NAMESPACE_URI
, NAMESPACE_ATTRS
, 0, 0},
255 namespace_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
259 ns
= (JSXMLNamespace
*)
260 JS_GetInstancePrivate(cx
,
261 JS_THIS_OBJECT(cx
, vp
),
262 &js_NamespaceClass
.base
,
267 *vp
= STRING_TO_JSVAL(ns
->uri
);
271 static JSFunctionSpec namespace_methods
[] = {
272 JS_FN(js_toString_str
, namespace_toString
, 0,0),
277 js_NewXMLNamespace(JSContext
*cx
, JSString
*prefix
, JSString
*uri
,
282 ns
= (JSXMLNamespace
*)
283 js_NewGCThing(cx
, GCX_NAMESPACE
, sizeof(JSXMLNamespace
));
289 ns
->declared
= declared
;
290 METER(xml_stats
.namespace);
291 METER(xml_stats
.livenamespace
);
296 js_TraceXMLNamespace(JSTracer
*trc
, JSXMLNamespace
*ns
)
299 JS_CALL_OBJECT_TRACER(trc
, ns
->object
, "object");
301 JS_CALL_STRING_TRACER(trc
, ns
->prefix
, "prefix");
303 JS_CALL_STRING_TRACER(trc
, ns
->uri
, "uri");
307 js_FinalizeXMLNamespace(JSContext
*cx
, JSXMLNamespace
*ns
)
309 UNMETER(xml_stats
.livenamespace
);
313 js_NewXMLNamespaceObject(JSContext
*cx
, JSString
*prefix
, JSString
*uri
,
317 JSTempValueRooter tvr
;
320 ns
= js_NewXMLNamespace(cx
, prefix
, uri
, declared
);
324 JS_PUSH_TEMP_ROOT_NAMESPACE(cx
, ns
, &tvr
);
325 obj
= js_GetXMLNamespaceObject(cx
, ns
);
326 JS_POP_TEMP_ROOT(cx
, &tvr
);
331 js_GetXMLNamespaceObject(JSContext
*cx
, JSXMLNamespace
*ns
)
337 JS_ASSERT(JS_GetPrivate(cx
, obj
) == ns
);
340 obj
= js_NewObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
, 0);
341 if (!obj
|| !JS_SetPrivate(cx
, obj
, ns
)) {
342 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
346 METER(xml_stats
.namespaceobj
);
347 METER(xml_stats
.livenamespaceobj
);
352 * QName class and library functions.
360 qname_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
364 if (!JSVAL_IS_INT(id
))
368 JS_GetInstancePrivate(cx
, obj
, &js_QNameClass
.base
, NULL
);
372 switch (JSVAL_TO_INT(id
)) {
374 *vp
= qn
->uri
? STRING_TO_JSVAL(qn
->uri
) : JSVAL_NULL
;
376 case QNAME_LOCALNAME
:
377 *vp
= STRING_TO_JSVAL(qn
->localName
);
384 qname_finalize(JSContext
*cx
, JSObject
*obj
)
388 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
391 JS_ASSERT(qn
->object
== obj
);
393 UNMETER(xml_stats
.liveqnameobj
);
397 anyname_finalize(JSContext
* cx
, JSObject
* obj
)
401 /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
403 if (rt
->anynameObject
== obj
)
404 rt
->anynameObject
= NULL
;
406 qname_finalize(cx
, obj
);
410 qname_trace(JSTracer
*trc
, JSObject
*obj
)
414 qn
= (JSXMLQName
*) JS_GetPrivate(trc
->context
, obj
);
416 JS_CALL_TRACER(trc
, qn
, JSTRACE_QNAME
, "private");
420 qname_identity(JSXMLQName
*qna
, JSXMLQName
*qnb
)
422 if (!qna
->uri
^ !qnb
->uri
)
424 if (qna
->uri
&& !js_EqualStrings(qna
->uri
, qnb
->uri
))
426 return js_EqualStrings(qna
->localName
, qnb
->localName
);
430 qname_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
432 JSXMLQName
*qn
, *qn2
;
435 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
436 JS_ASSERT(JSVAL_IS_OBJECT(v
));
437 obj2
= JSVAL_TO_OBJECT(v
);
438 if (!obj2
|| OBJ_GET_CLASS(cx
, obj2
) != &js_QNameClass
.base
) {
441 qn2
= (JSXMLQName
*) JS_GetPrivate(cx
, obj2
);
442 *bp
= qname_identity(qn
, qn2
);
447 JS_FRIEND_DATA(JSExtendedClass
) js_QNameClass
= {
449 JSCLASS_HAS_PRIVATE
| JSCLASS_CONSTRUCT_PROTOTYPE
| JSCLASS_IS_EXTENDED
|
450 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_QName
),
451 JS_PropertyStub
, JS_PropertyStub
, qname_getProperty
, NULL
,
452 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, qname_finalize
,
453 NULL
, NULL
, NULL
, NULL
,
454 NULL
, NULL
, JS_CLASS_TRACE(qname_trace
), NULL
},
455 qname_equality
, NULL
, NULL
, NULL
,
456 NULL
, NULL
, NULL
, NULL
460 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
461 * are like QName, except that they have no property getters. They share the
462 * qname_toString method, and therefore are exposed as constructable objects
463 * in this implementation.
465 JS_FRIEND_DATA(JSClass
) js_AttributeNameClass
= {
466 js_AttributeName_str
,
467 JSCLASS_HAS_PRIVATE
| JSCLASS_CONSTRUCT_PROTOTYPE
|
468 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName
),
469 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
470 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, qname_finalize
,
471 NULL
, NULL
, NULL
, NULL
,
472 NULL
, NULL
, JS_CLASS_TRACE(qname_trace
), NULL
475 JS_FRIEND_DATA(JSClass
) js_AnyNameClass
= {
477 JSCLASS_HAS_PRIVATE
| JSCLASS_CONSTRUCT_PROTOTYPE
|
478 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName
),
479 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
480 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, anyname_finalize
,
481 NULL
, NULL
, NULL
, NULL
,
482 NULL
, NULL
, JS_CLASS_TRACE(qname_trace
), NULL
485 #define QNAME_ATTRS \
486 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
488 static JSPropertySpec qname_props
[] = {
489 {js_uri_str
, QNAME_URI
, QNAME_ATTRS
, 0, 0},
490 {js_localName_str
, QNAME_LOCALNAME
, QNAME_ATTRS
, 0, 0},
495 qname_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
500 JSString
*str
, *qualstr
;
504 obj
= JS_THIS_OBJECT(cx
, vp
);
507 clasp
= OBJ_GET_CLASS(cx
, obj
);
508 if (clasp
== &js_AttributeNameClass
|| clasp
== &js_AnyNameClass
) {
509 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
512 JS_GetInstancePrivate(cx
, obj
, &js_QNameClass
.base
, vp
+ 2);
518 /* No uri means wildcard qualifier. */
519 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.starQualifierAtom
);
520 } else if (IS_EMPTY(qn
->uri
)) {
521 /* Empty string for uri means localName is in no namespace. */
522 str
= cx
->runtime
->emptyString
;
524 qualstr
= ATOM_TO_STRING(cx
->runtime
->atomState
.qualifierAtom
);
525 str
= js_ConcatStrings(cx
, qn
->uri
, qualstr
);
529 str
= js_ConcatStrings(cx
, str
, qn
->localName
);
533 if (str
&& clasp
== &js_AttributeNameClass
) {
534 length
= JSSTRING_LENGTH(str
);
535 chars
= (jschar
*) JS_malloc(cx
, (length
+ 2) * sizeof(jschar
));
539 js_strncpy(chars
+ 1, JSSTRING_CHARS(str
), length
);
541 str
= js_NewString(cx
, chars
, length
);
548 *vp
= STRING_TO_JSVAL(str
);
552 static JSFunctionSpec qname_methods
[] = {
553 JS_FN(js_toString_str
, qname_toString
, 0,0),
558 js_NewXMLQName(JSContext
*cx
, JSString
*uri
, JSString
*prefix
,
563 qn
= (JSXMLQName
*) js_NewGCThing(cx
, GCX_QNAME
, sizeof(JSXMLQName
));
569 qn
->localName
= localName
;
570 METER(xml_stats
.qname
);
571 METER(xml_stats
.liveqname
);
576 js_TraceXMLQName(JSTracer
*trc
, JSXMLQName
*qn
)
579 JS_CALL_OBJECT_TRACER(trc
, qn
->object
, "object");
581 JS_CALL_STRING_TRACER(trc
, qn
->uri
, "uri");
583 JS_CALL_STRING_TRACER(trc
, qn
->prefix
, "prefix");
585 JS_CALL_STRING_TRACER(trc
, qn
->localName
, "localName");
589 js_FinalizeXMLQName(JSContext
*cx
, JSXMLQName
*qn
)
591 UNMETER(xml_stats
.liveqname
);
595 js_NewXMLQNameObject(JSContext
*cx
, JSString
*uri
, JSString
*prefix
,
599 JSTempValueRooter tvr
;
602 qn
= js_NewXMLQName(cx
, uri
, prefix
, localName
);
605 JS_PUSH_TEMP_ROOT_QNAME(cx
, qn
, &tvr
);
606 obj
= js_GetXMLQNameObject(cx
, qn
);
607 JS_POP_TEMP_ROOT(cx
, &tvr
);
612 js_GetXMLQNameObject(JSContext
*cx
, JSXMLQName
*qn
)
618 JS_ASSERT(JS_GetPrivate(cx
, obj
) == qn
);
621 obj
= js_NewObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 0);
622 if (!obj
|| !JS_SetPrivate(cx
, obj
, qn
)) {
623 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
627 METER(xml_stats
.qnameobj
);
628 METER(xml_stats
.liveqnameobj
);
633 js_GetAttributeNameObject(JSContext
*cx
, JSXMLQName
*qn
)
639 if (OBJ_GET_CLASS(cx
, obj
) == &js_AttributeNameClass
)
641 qn
= js_NewXMLQName(cx
, qn
->uri
, qn
->prefix
, qn
->localName
);
646 obj
= js_NewObject(cx
, &js_AttributeNameClass
, NULL
, NULL
, 0);
647 if (!obj
|| !JS_SetPrivate(cx
, obj
, qn
)) {
648 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
653 METER(xml_stats
.qnameobj
);
654 METER(xml_stats
.liveqnameobj
);
659 js_ConstructXMLQNameObject(JSContext
*cx
, jsval nsval
, jsval lnval
)
665 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
666 * production, step 2.
668 if (!JSVAL_IS_PRIMITIVE(nsval
) &&
669 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(nsval
)) == &js_AnyNameClass
) {
675 return js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 2, argv
);
679 IsXMLName(const jschar
*cp
, size_t n
)
685 if (n
!= 0 && JS_ISXMLNSSTART(*cp
)) {
697 js_IsXMLName(JSContext
*cx
, jsval v
)
702 JSErrorReporter older
;
705 * Inline specialization of the QName constructor called with v passed as
706 * the only argument, to compute the localName for the constructed qname,
707 * without actually allocating the object or computing its uri and prefix.
708 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
710 if (!JSVAL_IS_PRIMITIVE(v
) &&
711 (clasp
= OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)),
712 clasp
== &js_QNameClass
.base
||
713 clasp
== &js_AttributeNameClass
||
714 clasp
== &js_AnyNameClass
)) {
715 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
716 name
= qn
->localName
;
718 older
= JS_SetErrorReporter(cx
, NULL
);
719 name
= js_ValueToString(cx
, v
);
720 JS_SetErrorReporter(cx
, older
);
722 JS_ClearPendingException(cx
);
727 return IsXMLName(JSSTRING_CHARS(name
), JSSTRING_LENGTH(name
));
731 * When argc is -1, it indicates argv is empty but the code should behave as
732 * if argc is 1 and argv[0] is JSVAL_VOID.
735 NamespaceHelper(JSContext
*cx
, JSObject
*obj
, intN argc
, jsval
*argv
,
738 jsval urival
, prefixval
;
740 JSBool isNamespace
, isQName
;
742 JSString
*empty
, *prefix
;
743 JSXMLNamespace
*ns
, *ns2
;
746 isNamespace
= isQName
= JS_FALSE
;
747 #ifdef __GNUC__ /* suppress bogus gcc warnings */
753 urival
= argv
[argc
> 1];
754 if (!JSVAL_IS_PRIMITIVE(urival
)) {
755 uriobj
= JSVAL_TO_OBJECT(urival
);
756 clasp
= OBJ_GET_CLASS(cx
, uriobj
);
757 isNamespace
= (clasp
== &js_NamespaceClass
.base
);
758 isQName
= (clasp
== &js_QNameClass
.base
);
763 /* Namespace called as function. */
764 if (argc
== 1 && isNamespace
) {
765 /* Namespace called with one Namespace argument is identity. */
770 /* Create and return a new QName object exactly as if constructed. */
771 obj
= js_NewObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
, 0);
774 *rval
= OBJECT_TO_JSVAL(obj
);
776 METER(xml_stats
.namespaceobj
);
777 METER(xml_stats
.livenamespaceobj
);
780 * Create and connect private data to rooted obj early, so we don't have
781 * to worry about rooting string newborns hanging off of the private data
784 empty
= cx
->runtime
->emptyString
;
785 ns
= js_NewXMLNamespace(cx
, empty
, empty
, JS_FALSE
);
788 if (!JS_SetPrivate(cx
, obj
, ns
))
792 if (argc
== 1 || argc
== -1) {
794 ns2
= (JSXMLNamespace
*) JS_GetPrivate(cx
, uriobj
);
796 ns
->prefix
= ns2
->prefix
;
797 } else if (isQName
&&
798 (qn
= (JSXMLQName
*) JS_GetPrivate(cx
, uriobj
))->uri
) {
800 ns
->prefix
= qn
->prefix
;
802 ns
->uri
= js_ValueToString(cx
, urival
);
806 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
807 if (!IS_EMPTY(ns
->uri
))
810 } else if (argc
== 2) {
812 (qn
= (JSXMLQName
*) JS_GetPrivate(cx
, uriobj
))->uri
) {
815 ns
->uri
= js_ValueToString(cx
, urival
);
821 if (IS_EMPTY(ns
->uri
)) {
822 if (!JSVAL_IS_VOID(prefixval
)) {
823 prefix
= js_ValueToString(cx
, prefixval
);
826 if (!IS_EMPTY(prefix
)) {
827 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
828 JSMSG_BAD_XML_NAMESPACE
,
829 js_ValueToPrintableString(cx
,
830 STRING_TO_JSVAL(prefix
)));
834 } else if (JSVAL_IS_VOID(prefixval
) || !js_IsXMLName(cx
, prefixval
)) {
835 /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */
838 prefix
= js_ValueToString(cx
, prefixval
);
849 Namespace(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
851 return NamespaceHelper(cx
,
852 (cx
->fp
->flags
& JSFRAME_CONSTRUCTING
) ? obj
: NULL
,
857 * When argc is -1, it indicates argv is empty but the code should behave as
858 * if argc is 1 and argv[0] is JSVAL_VOID.
861 QNameHelper(JSContext
*cx
, JSObject
*obj
, JSClass
*clasp
, intN argc
,
862 jsval
*argv
, jsval
*rval
)
864 jsval nameval
, nsval
;
865 JSBool isQName
, isNamespace
;
867 JSString
*uri
, *prefix
, *name
;
871 JS_ASSERT(clasp
== &js_QNameClass
.base
||
872 clasp
== &js_AttributeNameClass
);
874 nameval
= JSVAL_VOID
;
877 nameval
= argv
[argc
> 1];
879 !JSVAL_IS_PRIMITIVE(nameval
) &&
880 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(nameval
)) == &js_QNameClass
.base
;
884 /* QName called as function. */
885 if (argc
== 1 && isQName
) {
886 /* QName called with one QName argument is identity. */
892 * Create and return a new QName or AttributeName object exactly as if
895 obj
= js_NewObject(cx
, clasp
, NULL
, NULL
, 0);
898 *rval
= OBJECT_TO_JSVAL(obj
);
900 METER(xml_stats
.qnameobj
);
901 METER(xml_stats
.liveqnameobj
);
904 /* If namespace is not specified and name is a QName, clone it. */
905 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(nameval
));
909 name
= qn
->localName
;
913 /* Namespace and qname were passed -- use the qname's localName. */
914 nameval
= STRING_TO_JSVAL(qn
->localName
);
918 name
= cx
->runtime
->emptyString
;
919 } else if (argc
< 0) {
920 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
922 name
= js_ValueToString(cx
, nameval
);
925 argv
[argc
> 1] = STRING_TO_JSVAL(name
);
928 if (argc
> 1 && !JSVAL_IS_VOID(argv
[0])) {
930 } else if (IS_STAR(name
)) {
933 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
935 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval
));
936 JS_ASSERT(OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(nsval
)) ==
937 &js_NamespaceClass
.base
);
940 if (JSVAL_IS_NULL(nsval
)) {
941 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
945 * Inline specialization of the Namespace constructor called with
946 * nsval passed as the only argument, to compute the uri and prefix
947 * for the constructed namespace, without actually allocating the
948 * object or computing other members. See ECMA-357 13.3.2 6(a) and
951 isNamespace
= isQName
= JS_FALSE
;
952 if (!JSVAL_IS_PRIMITIVE(nsval
)) {
953 nsobj
= JSVAL_TO_OBJECT(nsval
);
954 clasp
= OBJ_GET_CLASS(cx
, nsobj
);
955 isNamespace
= (clasp
== &js_NamespaceClass
.base
);
956 isQName
= (clasp
== &js_QNameClass
.base
);
958 #ifdef __GNUC__ /* suppress bogus gcc warnings */
963 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, nsobj
);
966 } else if (isQName
&&
967 (qn
= (JSXMLQName
*) JS_GetPrivate(cx
, nsobj
))->uri
) {
973 uri
= js_ValueToString(cx
, nsval
);
976 argv
[0] = STRING_TO_JSVAL(uri
); /* local root */
978 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
979 prefix
= IS_EMPTY(uri
) ? cx
->runtime
->emptyString
: NULL
;
984 qn
= js_NewXMLQName(cx
, uri
, prefix
, name
);
987 obj
->fslots
[JSSLOT_PRIVATE
] = PRIVATE_TO_JSVAL(qn
);
993 QName(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
995 return QNameHelper(cx
, (cx
->fp
->flags
& JSFRAME_CONSTRUCTING
) ? obj
: NULL
,
996 &js_QNameClass
.base
, argc
, argv
, rval
);
1000 AttributeName(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
1003 return QNameHelper(cx
, (cx
->fp
->flags
& JSFRAME_CONSTRUCTING
) ? obj
: NULL
,
1004 &js_AttributeNameClass
, argc
, argv
, rval
);
1008 * XMLArray library functions.
1011 namespace_identity(const void *a
, const void *b
)
1013 const JSXMLNamespace
*nsa
= (const JSXMLNamespace
*) a
;
1014 const JSXMLNamespace
*nsb
= (const JSXMLNamespace
*) b
;
1016 if (nsa
->prefix
&& nsb
->prefix
) {
1017 if (!js_EqualStrings(nsa
->prefix
, nsb
->prefix
))
1020 if (nsa
->prefix
|| nsb
->prefix
)
1023 return js_EqualStrings(nsa
->uri
, nsb
->uri
);
1027 attr_identity(const void *a
, const void *b
)
1029 const JSXML
*xmla
= (const JSXML
*) a
;
1030 const JSXML
*xmlb
= (const JSXML
*) b
;
1032 return qname_identity(xmla
->name
, xmlb
->name
);
1036 XMLArrayCursorInit(JSXMLArrayCursor
*cursor
, JSXMLArray
*array
)
1038 JSXMLArrayCursor
*next
;
1040 cursor
->array
= array
;
1042 next
= cursor
->next
= array
->cursors
;
1044 next
->prevp
= &cursor
->next
;
1045 cursor
->prevp
= &array
->cursors
;
1046 array
->cursors
= cursor
;
1047 cursor
->root
= NULL
;
1051 XMLArrayCursorFinish(JSXMLArrayCursor
*cursor
)
1053 JSXMLArrayCursor
*next
;
1057 next
= cursor
->next
;
1059 next
->prevp
= cursor
->prevp
;
1060 *cursor
->prevp
= next
;
1061 cursor
->array
= NULL
;
1065 XMLArrayCursorNext(JSXMLArrayCursor
*cursor
)
1069 array
= cursor
->array
;
1070 if (!array
|| cursor
->index
>= array
->length
)
1072 return cursor
->root
= array
->vector
[cursor
->index
++];
1076 XMLArrayCursorItem(JSXMLArrayCursor
*cursor
)
1080 array
= cursor
->array
;
1081 if (!array
|| cursor
->index
>= array
->length
)
1083 return cursor
->root
= array
->vector
[cursor
->index
];
1087 XMLArrayCursorTrace(JSTracer
*trc
, JSXMLArrayCursor
*cursor
)
1094 for (; cursor
; cursor
= cursor
->next
) {
1095 root
= cursor
->root
;
1096 JS_SET_TRACING_INDEX(trc
, "cursor_root", index
++);
1097 js_CallValueTracerIfGCThing(trc
, (jsval
)root
);
1101 /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
1103 XMLArraySetCapacity(JSContext
*cx
, JSXMLArray
*array
, uint32 capacity
)
1107 if (capacity
== 0) {
1108 /* We could let realloc(p, 0) free this, but purify gets confused. */
1110 free(array
->vector
);
1114 #if JS_BITS_PER_WORD == 32
1115 (size_t)capacity
> ~(size_t)0 / sizeof(void *) ||
1117 !(vector
= (void **)
1118 realloc(array
->vector
, capacity
* sizeof(void *)))) {
1120 JS_ReportOutOfMemory(cx
);
1124 array
->capacity
= JSXML_PRESET_CAPACITY
| capacity
;
1125 array
->vector
= vector
;
1130 XMLArrayTrim(JSXMLArray
*array
)
1132 if (array
->capacity
& JSXML_PRESET_CAPACITY
)
1134 if (array
->length
< array
->capacity
)
1135 XMLArraySetCapacity(NULL
, array
, array
->length
);
1139 XMLArrayInit(JSContext
*cx
, JSXMLArray
*array
, uint32 capacity
)
1141 array
->length
= array
->capacity
= 0;
1142 array
->vector
= NULL
;
1143 array
->cursors
= NULL
;
1144 return capacity
== 0 || XMLArraySetCapacity(cx
, array
, capacity
);
1148 XMLArrayFinish(JSContext
*cx
, JSXMLArray
*array
)
1150 JSXMLArrayCursor
*cursor
;
1152 JS_free(cx
, array
->vector
);
1154 while ((cursor
= array
->cursors
) != NULL
)
1155 XMLArrayCursorFinish(cursor
);
1158 memset(array
, 0xd5, sizeof *array
);
1162 #define XML_NOT_FOUND ((uint32) -1)
1165 XMLArrayFindMember(const JSXMLArray
*array
, void *elt
, JSIdentityOp identity
)
1170 /* The identity op must not reallocate array->vector. */
1171 vector
= array
->vector
;
1173 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
1174 if (identity(vector
[i
], elt
))
1178 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
1179 if (vector
[i
] == elt
)
1183 return XML_NOT_FOUND
;
1187 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1188 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1189 * should be greater than increment.
1191 #define LINEAR_THRESHOLD 256
1192 #define LINEAR_INCREMENT 32
1195 XMLArrayAddMember(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, void *elt
)
1201 if (index
>= array
->length
) {
1202 if (index
>= JSXML_CAPACITY(array
)) {
1203 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1204 capacity
= index
+ 1;
1205 if (index
>= LINEAR_THRESHOLD
) {
1206 capacity
= JS_ROUNDUP(capacity
, LINEAR_INCREMENT
);
1208 JS_CEILING_LOG2(log2
, capacity
);
1209 capacity
= JS_BIT(log2
);
1212 #if JS_BITS_PER_WORD == 32
1213 (size_t)capacity
> ~(size_t)0 / sizeof(void *) ||
1215 !(vector
= (void **)
1216 realloc(array
->vector
, capacity
* sizeof(void *)))) {
1217 JS_ReportOutOfMemory(cx
);
1220 array
->capacity
= capacity
;
1221 array
->vector
= vector
;
1222 for (i
= array
->length
; i
< index
; i
++)
1225 array
->length
= index
+ 1;
1228 array
->vector
[index
] = elt
;
1233 XMLArrayInsert(JSContext
*cx
, JSXMLArray
*array
, uint32 i
, uint32 n
)
1236 JSXMLArrayCursor
*cursor
;
1240 if (!XMLArraySetCapacity(cx
, array
, j
+ n
))
1243 array
->length
= j
+ n
;
1244 JS_ASSERT(n
!= (uint32
)-1);
1247 array
->vector
[j
+ n
] = array
->vector
[j
];
1250 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1251 if (cursor
->index
> i
)
1258 XMLArrayDelete(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, JSBool compress
)
1261 void **vector
, *elt
;
1262 JSXMLArrayCursor
*cursor
;
1264 length
= array
->length
;
1265 if (index
>= length
)
1268 vector
= array
->vector
;
1269 elt
= vector
[index
];
1271 while (++index
< length
)
1272 vector
[index
-1] = vector
[index
];
1273 array
->length
= length
- 1;
1274 array
->capacity
= JSXML_CAPACITY(array
);
1276 vector
[index
] = NULL
;
1279 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1280 if (cursor
->index
> index
)
1287 XMLArrayTruncate(JSContext
*cx
, JSXMLArray
*array
, uint32 length
)
1291 JS_ASSERT(!array
->cursors
);
1292 if (length
>= array
->length
)
1297 free(array
->vector
);
1300 vector
= (void **) realloc(array
->vector
, length
* sizeof(void *));
1305 if (array
->length
> length
)
1306 array
->length
= length
;
1307 array
->capacity
= length
;
1308 array
->vector
= vector
;
1311 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1312 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1314 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1315 ? (t *) (a)->vector[i] \
1317 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1318 if ((a)->length <= (i)) \
1319 (a)->length = (i) + 1; \
1320 ((a)->vector[i] = (void *)(e)); \
1322 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1323 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1324 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1325 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1326 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1329 * Define XML setting property strings and constants early, so everyone can
1330 * use the same names and their magic numbers (tinyids, flags).
1332 static const char js_ignoreComments_str
[] = "ignoreComments";
1333 static const char js_ignoreProcessingInstructions_str
[]
1334 = "ignoreProcessingInstructions";
1335 static const char js_ignoreWhitespace_str
[] = "ignoreWhitespace";
1336 static const char js_prettyPrinting_str
[] = "prettyPrinting";
1337 static const char js_prettyIndent_str
[] = "prettyIndent";
1340 * NB: These XML static property tinyids must
1341 * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1342 * (b) index their corresponding xml_static_props array elements.
1345 enum xml_static_tinyid
{
1346 XML_IGNORE_COMMENTS
,
1347 XML_IGNORE_PROCESSING_INSTRUCTIONS
,
1348 XML_IGNORE_WHITESPACE
,
1349 XML_PRETTY_PRINTING
,
1354 xml_setting_getter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1360 xml_setting_setter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1364 JS_ASSERT(JSVAL_IS_INT(id
));
1366 flag
= JS_BIT(JSVAL_TO_INT(id
));
1367 if (js_ValueToBoolean(*vp
))
1368 cx
->xmlSettingFlags
|= flag
;
1370 cx
->xmlSettingFlags
&= ~flag
;
1374 static JSPropertySpec xml_static_props
[] = {
1375 {js_ignoreComments_str
, XML_IGNORE_COMMENTS
, JSPROP_PERMANENT
,
1376 xml_setting_getter
, xml_setting_setter
},
1377 {js_ignoreProcessingInstructions_str
,
1378 XML_IGNORE_PROCESSING_INSTRUCTIONS
, JSPROP_PERMANENT
,
1379 xml_setting_getter
, xml_setting_setter
},
1380 {js_ignoreWhitespace_str
, XML_IGNORE_WHITESPACE
, JSPROP_PERMANENT
,
1381 xml_setting_getter
, xml_setting_setter
},
1382 {js_prettyPrinting_str
, XML_PRETTY_PRINTING
, JSPROP_PERMANENT
,
1383 xml_setting_getter
, xml_setting_setter
},
1384 {js_prettyIndent_str
, XML_PRETTY_INDENT
, JSPROP_PERMANENT
,
1385 xml_setting_getter
, NULL
},
1389 /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1390 #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1391 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1392 JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1393 #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1394 #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1395 #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1398 * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1399 * This flag means a couple of things:
1401 * - The top JSXML created for a parse tree must have an object owning it.
1403 * - That the default namespace normally inherited from the temporary
1404 * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1405 * string must, in the case of a precompiled XML object tree, inherit via
1406 * ad-hoc code in ParseNodeToXML.
1408 * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1410 #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1412 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1413 #define IS_XML(str) \
1414 (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
1416 #define IS_XMLNS(str) \
1417 (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1419 #define IS_XML_CHARS(chars) \
1420 (JS_TOLOWER((chars)[0]) == 'x' && \
1421 JS_TOLOWER((chars)[1]) == 'm' && \
1422 JS_TOLOWER((chars)[2]) == 'l')
1424 #define HAS_NS_AFTER_XML(chars) \
1425 (JS_TOLOWER((chars)[3]) == 'n' && \
1426 JS_TOLOWER((chars)[4]) == 's')
1428 #define IS_XMLNS_CHARS(chars) \
1429 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1431 #define STARTS_WITH_XML(chars,length) \
1432 (length >= 3 && IS_XML_CHARS(chars))
1434 static const char xml_namespace_str
[] = "http://www.w3.org/XML/1998/namespace";
1435 static const char xmlns_namespace_str
[] = "http://www.w3.org/2000/xmlns/";
1438 ParseNodeToQName(JSContext
*cx
, JSParseContext
*pc
, JSParseNode
*pn
,
1439 JSXMLArray
*inScopeNSes
, JSBool isAttributeName
)
1441 JSString
*str
, *uri
, *prefix
, *localName
;
1442 size_t length
, offset
;
1443 const jschar
*start
, *limit
, *colon
;
1447 JS_ASSERT(pn
->pn_arity
== PN_NULLARY
);
1448 str
= ATOM_TO_STRING(pn
->pn_atom
);
1449 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
1450 JS_ASSERT(length
!= 0 && *start
!= '@');
1451 JS_ASSERT(length
!= 1 || *start
!= '*');
1453 uri
= cx
->runtime
->emptyString
;
1454 limit
= start
+ length
;
1455 colon
= js_strchr_limit(start
, ':', limit
);
1457 offset
= PTRDIFF(colon
, start
, jschar
);
1458 prefix
= js_NewDependentString(cx
, str
, 0, offset
);
1462 if (STARTS_WITH_XML(start
, offset
)) {
1464 uri
= JS_InternString(cx
, xml_namespace_str
);
1467 } else if (offset
== 5 && HAS_NS_AFTER_XML(start
)) {
1468 uri
= JS_InternString(cx
, xmlns_namespace_str
);
1476 n
= inScopeNSes
->length
;
1479 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSXMLNamespace
);
1480 if (ns
->prefix
&& js_EqualStrings(ns
->prefix
, prefix
)) {
1488 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
,
1490 JSMSG_BAD_XML_NAMESPACE
,
1491 js_ValueToPrintableString(cx
,
1492 STRING_TO_JSVAL(prefix
)));
1496 localName
= js_NewStringCopyN(cx
, colon
+ 1, length
- (offset
+ 1));
1500 if (isAttributeName
) {
1502 * An unprefixed attribute is not in any namespace, so set prefix
1503 * as well as uri to the empty string.
1508 * Loop from back to front looking for the closest declared default
1511 n
= inScopeNSes
->length
;
1514 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSXMLNamespace
);
1515 if (!ns
->prefix
|| IS_EMPTY(ns
->prefix
)) {
1520 prefix
= IS_EMPTY(uri
) ? cx
->runtime
->emptyString
: NULL
;
1525 return js_NewXMLQName(cx
, uri
, prefix
, localName
);
1529 ChompXMLWhitespace(JSContext
*cx
, JSString
*str
)
1531 size_t length
, newlength
, offset
;
1532 const jschar
*cp
, *start
, *end
;
1535 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
1536 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
1538 if (!JS_ISXMLSPACE(c
))
1543 if (!JS_ISXMLSPACE(c
))
1547 newlength
= PTRDIFF(end
, cp
, jschar
);
1548 if (newlength
== length
)
1550 offset
= PTRDIFF(cp
, start
, jschar
);
1551 return js_NewDependentString(cx
, str
, offset
, newlength
);
1555 ParseNodeToXML(JSContext
*cx
, JSParseContext
*pc
, JSParseNode
*pn
,
1556 JSXMLArray
*inScopeNSes
, uintN flags
)
1558 JSXML
*xml
, *kid
, *attr
, *attrj
;
1560 uint32 length
, n
, i
, j
;
1561 JSParseNode
*pn2
, *pn3
, *head
, **pnp
;
1563 JSXMLQName
*qn
, *attrjqn
;
1564 JSXMLClass xml_class
;
1567 if (!JS_CHECK_STACK_SIZE(cx
, stackDummy
)) {
1568 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
, JSREPORT_ERROR
,
1569 JSMSG_OVER_RECURSED
);
1573 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1576 * Cases return early to avoid common code that gets an outermost xml's
1577 * object, which protects GC-things owned by xml and its descendants from
1578 * garbage collection.
1581 if (!js_EnterLocalRootScope(cx
))
1583 switch (pn
->pn_type
) {
1585 length
= inScopeNSes
->length
;
1587 xml
= ParseNodeToXML(cx
, pc
, pn2
, inScopeNSes
, flags
);
1591 flags
&= ~XSF_PRECOMPILED_ROOT
;
1595 if (!XMLArraySetCapacity(cx
, &xml
->xml_kids
, n
))
1599 while ((pn2
= pn2
->pn_next
) != NULL
) {
1600 if (!pn2
->pn_next
) {
1601 /* Don't append the end tag! */
1602 JS_ASSERT(pn2
->pn_type
== TOK_XMLETAGO
);
1606 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1607 n
> 1 && pn2
->pn_type
== TOK_XMLSPACE
) {
1612 kid
= ParseNodeToXML(cx
, pc
, pn2
, inScopeNSes
, flags
);
1613 if (kid
== PN2X_SKIP_CHILD
) {
1621 /* Store kid in xml right away, to protect it from GC. */
1622 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1626 /* XXX where is this documented in an XML spec, or in E4X? */
1627 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1628 n
> 1 && kid
->xml_class
== JSXML_CLASS_TEXT
) {
1629 str
= ChompXMLWhitespace(cx
, kid
->xml_value
);
1632 kid
->xml_value
= str
;
1637 if (n
< pn
->pn_count
- 2)
1638 XMLArrayTrim(&xml
->xml_kids
);
1639 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1643 xml
= js_NewXML(cx
, JSXML_CLASS_LIST
);
1648 if (!XMLArraySetCapacity(cx
, &xml
->xml_kids
, n
))
1652 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
1654 * Always ignore insignificant whitespace in lists -- we shouldn't
1655 * condition this on an XML.ignoreWhitespace setting when the list
1656 * constructor is XMLList (note XML/XMLList unification hazard).
1658 if (pn2
->pn_type
== TOK_XMLSPACE
) {
1663 kid
= ParseNodeToXML(cx
, pc
, pn2
, inScopeNSes
, flags
);
1664 if (kid
== PN2X_SKIP_CHILD
) {
1672 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1676 if (n
< pn
->pn_count
)
1677 XMLArrayTrim(&xml
->xml_kids
);
1682 length
= inScopeNSes
->length
;
1684 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
);
1685 if (pn2
->pn_arity
== PN_LIST
)
1688 xml
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
1692 /* First pass: check syntax and process namespace declarations. */
1693 JS_ASSERT(pn
->pn_count
>= 1);
1694 n
= pn
->pn_count
- 1;
1695 pnp
= &pn2
->pn_next
;
1697 while ((pn2
= *pnp
) != NULL
) {
1699 const jschar
*chars
;
1701 if (pn2
->pn_type
!= TOK_XMLNAME
|| pn2
->pn_arity
!= PN_NULLARY
)
1704 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1705 for (pn3
= head
; pn3
!= pn2
; pn3
= pn3
->pn_next
->pn_next
) {
1706 if (pn3
->pn_atom
== pn2
->pn_atom
) {
1707 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn2
,
1709 JSMSG_DUPLICATE_XML_ATTR
,
1710 js_ValueToPrintableString(cx
,
1711 ATOM_KEY(pn2
->pn_atom
)));
1716 str
= ATOM_TO_STRING(pn2
->pn_atom
);
1719 if (pn2
->pn_type
!= TOK_XMLATTR
)
1722 JSSTRING_CHARS_AND_LENGTH(str
, chars
, length
);
1724 IS_XMLNS_CHARS(chars
) &&
1725 (length
== 5 || chars
[5] == ':')) {
1726 JSString
*uri
, *prefix
;
1728 uri
= ATOM_TO_STRING(pn2
->pn_atom
);
1730 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1731 prefix
= cx
->runtime
->emptyString
;
1733 prefix
= js_NewStringCopyN(cx
, chars
+ 6, length
- 6);
1739 * Once the new ns is appended to xml->xml_namespaces, it is
1740 * protected from GC by the object that owns xml -- which is
1741 * either xml->object if outermost, or the object owning xml's
1742 * oldest ancestor if !outermost.
1744 ns
= js_NewXMLNamespace(cx
, prefix
, uri
, JS_TRUE
);
1749 * Don't add a namespace that's already in scope. If someone
1750 * extracts a child property from its parent via [[Get]], then
1751 * we enforce the invariant, noted many times in ECMA-357, that
1752 * the child's namespaces form a possibly-improper superset of
1753 * its ancestors' namespaces.
1755 if (!XMLARRAY_HAS_MEMBER(inScopeNSes
, ns
, namespace_identity
)) {
1756 if (!XMLARRAY_APPEND(cx
, inScopeNSes
, ns
) ||
1757 !XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
)) {
1764 *pnp
= pn2
->pn_next
;
1765 /* XXXbe recycle pn2 */
1769 pnp
= &pn2
->pn_next
;
1773 * If called from js_ParseNodeToXMLObject, emulate the effect of the
1774 * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1775 * the String Type" (ECMA-357 10.3.1).
1777 if (flags
& XSF_PRECOMPILED_ROOT
) {
1778 JS_ASSERT(length
>= 1);
1779 ns
= XMLARRAY_MEMBER(inScopeNSes
, 0, JSXMLNamespace
);
1780 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml
->xml_namespaces
, ns
,
1781 namespace_identity
));
1782 ns
= js_NewXMLNamespace(cx
, ns
->prefix
, ns
->uri
, JS_FALSE
);
1785 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
1788 XMLArrayTrim(&xml
->xml_namespaces
);
1790 /* Second pass: process tag name and attributes, using namespaces. */
1792 qn
= ParseNodeToQName(cx
, pc
, pn2
, inScopeNSes
, JS_FALSE
);
1797 JS_ASSERT((n
& 1) == 0);
1799 if (!XMLArraySetCapacity(cx
, &xml
->xml_attrs
, n
))
1802 for (i
= 0; (pn2
= pn2
->pn_next
) != NULL
; i
++) {
1803 qn
= ParseNodeToQName(cx
, pc
, pn2
, inScopeNSes
, JS_TRUE
);
1805 xml
->xml_attrs
.length
= i
;
1810 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1811 * this time checking local name and namespace URI.
1813 for (j
= 0; j
< i
; j
++) {
1814 attrj
= XMLARRAY_MEMBER(&xml
->xml_attrs
, j
, JSXML
);
1815 attrjqn
= attrj
->name
;
1816 if (js_EqualStrings(attrjqn
->uri
, qn
->uri
) &&
1817 js_EqualStrings(attrjqn
->localName
, qn
->localName
)) {
1818 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn2
,
1820 JSMSG_DUPLICATE_XML_ATTR
,
1821 js_ValueToPrintableString(cx
,
1822 ATOM_KEY(pn2
->pn_atom
)));
1829 JS_ASSERT(pn2
->pn_type
== TOK_XMLATTR
);
1831 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
1835 XMLARRAY_SET_MEMBER(&xml
->xml_attrs
, i
, attr
);
1838 attr
->xml_value
= ATOM_TO_STRING(pn2
->pn_atom
);
1841 /* Point tag closes its own namespace scope. */
1842 if (pn
->pn_type
== TOK_XMLPTAGC
)
1843 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1849 case TOK_XMLCOMMENT
:
1851 str
= ATOM_TO_STRING(pn
->pn_atom
);
1853 if (pn
->pn_type
== TOK_XMLCOMMENT
) {
1854 if (flags
& XSF_IGNORE_COMMENTS
)
1856 xml_class
= JSXML_CLASS_COMMENT
;
1857 } else if (pn
->pn_type
== TOK_XMLPI
) {
1859 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
,
1862 js_ValueToPrintableString(cx
,
1863 STRING_TO_JSVAL(str
)));
1867 if (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
)
1870 qn
= ParseNodeToQName(cx
, pc
, pn
, inScopeNSes
, JS_FALSE
);
1875 ? ATOM_TO_STRING(pn
->pn_atom2
)
1876 : cx
->runtime
->emptyString
;
1877 xml_class
= JSXML_CLASS_PROCESSING_INSTRUCTION
;
1879 /* CDATA section content, or element text. */
1880 xml_class
= JSXML_CLASS_TEXT
;
1883 xml
= js_NewXML(cx
, xml_class
);
1887 if (pn
->pn_type
== TOK_XMLSPACE
)
1888 xml
->xml_flags
|= XMLF_WHITESPACE_TEXT
;
1889 xml
->xml_value
= str
;
1896 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) xml
);
1897 if ((flags
& XSF_PRECOMPILED_ROOT
) && !js_GetXMLObject(cx
, xml
))
1902 js_LeaveLocalRootScope(cx
);
1903 return PN2X_SKIP_CHILD
;
1905 #undef PN2X_SKIP_CHILD
1908 js_ReportCompileErrorNumber(cx
, &pc
->tokenStream
, pn
, JSREPORT_ERROR
,
1909 JSMSG_BAD_XML_MARKUP
);
1911 js_LeaveLocalRootScope(cx
);
1916 * XML helper, object-ops, and library functions. We start with the helpers,
1917 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1920 GetXMLSetting(JSContext
*cx
, const char *name
, jsval
*vp
)
1924 if (!js_FindClassObject(cx
, NULL
, INT_TO_JSID(JSProto_XML
), &v
))
1926 if (!VALUE_IS_FUNCTION(cx
, v
)) {
1930 return JS_GetProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
);
1934 FillSettingsCache(JSContext
*cx
)
1940 /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1941 for (i
= XML_IGNORE_COMMENTS
; i
< XML_PRETTY_INDENT
; i
++) {
1942 name
= xml_static_props
[i
].name
;
1943 if (!GetXMLSetting(cx
, name
, &v
))
1945 if (js_ValueToBoolean(v
))
1946 cx
->xmlSettingFlags
|= JS_BIT(i
);
1948 cx
->xmlSettingFlags
&= ~JS_BIT(i
);
1951 cx
->xmlSettingFlags
|= XSF_CACHE_VALID
;
1956 GetBooleanXMLSetting(JSContext
*cx
, const char *name
, JSBool
*bp
)
1960 if (!(cx
->xmlSettingFlags
& XSF_CACHE_VALID
) && !FillSettingsCache(cx
))
1963 for (i
= 0; xml_static_props
[i
].name
; i
++) {
1964 if (!strcmp(xml_static_props
[i
].name
, name
)) {
1965 *bp
= (cx
->xmlSettingFlags
& JS_BIT(i
)) != 0;
1974 GetUint32XMLSetting(JSContext
*cx
, const char *name
, uint32
*uip
)
1978 return GetXMLSetting(cx
, name
, &v
) && JS_ValueToECMAUint32(cx
, v
, uip
);
1982 GetXMLSettingFlags(JSContext
*cx
, uintN
*flagsp
)
1986 /* Just get the first flag to validate the setting flags cache. */
1987 if (!GetBooleanXMLSetting(cx
, js_ignoreComments_str
, &flag
))
1989 *flagsp
= cx
->xmlSettingFlags
;
1994 ParseXMLSource(JSContext
*cx
, JSString
*src
)
1998 size_t urilen
, srclen
, length
, offset
, dstlen
;
2000 const jschar
*srcp
, *endp
;
2003 const char *filename
;
2011 static const char prefix
[] = "<parent xmlns='";
2012 static const char middle
[] = "'>";
2013 static const char suffix
[] = "</parent>";
2015 #define constrlen(constr) (sizeof(constr) - 1)
2017 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
2019 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(nsval
));
2021 urilen
= JSSTRING_LENGTH(ns
->uri
);
2022 srclen
= JSSTRING_LENGTH(src
);
2023 length
= constrlen(prefix
) + urilen
+ constrlen(middle
) + srclen
+
2026 chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
2031 js_InflateStringToBuffer(cx
, prefix
, constrlen(prefix
), chars
, &dstlen
);
2033 js_strncpy(chars
+ offset
, JSSTRING_CHARS(ns
->uri
), urilen
);
2035 dstlen
= length
- offset
+ 1;
2036 js_InflateStringToBuffer(cx
, middle
, constrlen(middle
), chars
+ offset
,
2039 srcp
= JSSTRING_CHARS(src
);
2040 js_strncpy(chars
+ offset
, srcp
, srclen
);
2042 dstlen
= length
- offset
+ 1;
2043 js_InflateStringToBuffer(cx
, suffix
, constrlen(suffix
), chars
+ offset
,
2045 chars
[offset
+ dstlen
] = 0;
2048 for (fp
= cx
->fp
; fp
&& !fp
->regs
; fp
= fp
->down
)
2049 JS_ASSERT(!fp
->script
);
2053 op
= (JSOp
) *fp
->regs
->pc
;
2054 if (op
== JSOP_TOXML
|| op
== JSOP_TOXMLLIST
) {
2055 filename
= fp
->script
->filename
;
2056 lineno
= js_PCToLineNumber(cx
, fp
->script
, fp
->regs
->pc
);
2057 for (endp
= srcp
+ srclen
; srcp
< endp
; srcp
++) {
2064 if (!js_InitParseContext(cx
, &pc
, NULL
, chars
, length
, NULL
,
2067 pn
= js_ParseXMLText(cx
, cx
->fp
->scopeChain
, &pc
, JS_FALSE
);
2068 if (pn
&& XMLArrayInit(cx
, &nsarray
, 1)) {
2069 if (GetXMLSettingFlags(cx
, &flags
))
2070 xml
= ParseNodeToXML(cx
, &pc
, pn
, &nsarray
, flags
);
2072 XMLArrayFinish(cx
, &nsarray
);
2074 js_FinishParseContext(cx
, &pc
);
2084 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
2086 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
2089 * for all x belonging to XML:
2090 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
2092 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
2093 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
2095 * Same goes for 10.4.1 Step 7(a).
2097 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
2098 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
2099 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
2100 * undeclared namespaces associated with x not belonging to ancestorNS.
2103 OrphanXMLChild(JSContext
*cx
, JSXML
*xml
, uint32 i
)
2107 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, 0, JSXMLNamespace
);
2108 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
2111 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
2112 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
2114 ns
->declared
= JS_FALSE
;
2121 ToXML(JSContext
*cx
, jsval v
)
2129 if (JSVAL_IS_PRIMITIVE(v
)) {
2130 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
2133 obj
= JSVAL_TO_OBJECT(v
);
2134 if (OBJECT_IS_XML(cx
, obj
)) {
2135 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
2136 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
2137 if (xml
->xml_kids
.length
!= 1)
2139 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
2141 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
2142 return js_GetXMLObject(cx
, xml
);
2148 clasp
= OBJ_GET_CLASS(cx
, obj
);
2149 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
2153 if (clasp
!= &js_StringClass
&&
2154 clasp
!= &js_NumberClass
&&
2155 clasp
!= &js_BooleanClass
) {
2160 str
= js_ValueToString(cx
, v
);
2163 if (IS_EMPTY(str
)) {
2165 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2169 xml
= ParseXMLSource(cx
, str
);
2172 length
= JSXML_LENGTH(xml
);
2176 obj
= js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
2179 } else if (length
== 1) {
2180 xml
= OrphanXMLChild(cx
, xml
, 0);
2183 obj
= js_GetXMLObject(cx
, xml
);
2187 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_SYNTAX_ERROR
);
2193 js_ReportValueError(cx
, JSMSG_BAD_XML_CONVERSION
,
2194 JSDVG_IGNORE_STACK
, v
, NULL
);
2199 Append(JSContext
*cx
, JSXML
*list
, JSXML
*kid
);
2202 ToXMLList(JSContext
*cx
, jsval v
)
2204 JSObject
*obj
, *listobj
;
2205 JSXML
*xml
, *list
, *kid
;
2210 if (JSVAL_IS_PRIMITIVE(v
)) {
2211 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
2214 obj
= JSVAL_TO_OBJECT(v
);
2215 if (OBJECT_IS_XML(cx
, obj
)) {
2216 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
2217 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
2218 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
2221 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
2222 if (!Append(cx
, list
, xml
))
2229 clasp
= OBJ_GET_CLASS(cx
, obj
);
2230 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
2234 if (clasp
!= &js_StringClass
&&
2235 clasp
!= &js_NumberClass
&&
2236 clasp
!= &js_BooleanClass
) {
2241 str
= js_ValueToString(cx
, v
);
2244 if (IS_EMPTY(str
)) {
2248 if (!js_EnterLocalRootScope(cx
))
2250 xml
= ParseXMLSource(cx
, str
);
2252 js_LeaveLocalRootScope(cx
);
2255 length
= JSXML_LENGTH(xml
);
2258 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
2260 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
2261 for (i
= 0; i
< length
; i
++) {
2262 kid
= OrphanXMLChild(cx
, xml
, i
);
2263 if (!kid
|| !Append(cx
, list
, kid
)) {
2271 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) listobj
);
2275 js_ReportValueError(cx
, JSMSG_BAD_XMLLIST_CONVERSION
,
2276 JSDVG_IGNORE_STACK
, v
, NULL
);
2281 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2282 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2283 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2284 * MakeXMLSpecialString subroutine.
2286 * These functions take ownership of sb->base, if sb is non-null, in all cases
2287 * of success or failure.
2290 MakeXMLSpecialString(JSContext
*cx
, JSStringBuffer
*sb
,
2291 JSString
*str
, JSString
*str2
,
2292 const jschar
*prefix
, size_t prefixlength
,
2293 const jschar
*suffix
, size_t suffixlength
)
2295 JSStringBuffer localSB
;
2296 size_t length
, length2
, newlength
;
2301 js_InitStringBuffer(sb
);
2304 length
= JSSTRING_LENGTH(str
);
2305 length2
= str2
? JSSTRING_LENGTH(str2
) : 0;
2306 newlength
= STRING_BUFFER_OFFSET(sb
) +
2307 prefixlength
+ length
+ ((length2
!= 0) ? 1 + length2
: 0) +
2309 bp
= base
= (jschar
*)
2310 JS_realloc(cx
, sb
->base
, (newlength
+ 1) * sizeof(jschar
));
2312 js_FinishStringBuffer(sb
);
2316 bp
+= STRING_BUFFER_OFFSET(sb
);
2317 js_strncpy(bp
, prefix
, prefixlength
);
2319 js_strncpy(bp
, JSSTRING_CHARS(str
), length
);
2322 *bp
++ = (jschar
) ' ';
2323 js_strncpy(bp
, JSSTRING_CHARS(str2
), length2
);
2326 js_strncpy(bp
, suffix
, suffixlength
);
2327 bp
[suffixlength
] = 0;
2329 str
= js_NewString(cx
, base
, newlength
);
2336 MakeXMLCDATAString(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
)
2338 static const jschar cdata_prefix_ucNstr
[] = {'<', '!', '[',
2339 'C', 'D', 'A', 'T', 'A',
2341 static const jschar cdata_suffix_ucNstr
[] = {']', ']', '>'};
2343 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2344 cdata_prefix_ucNstr
, 9,
2345 cdata_suffix_ucNstr
, 3);
2349 MakeXMLCommentString(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
)
2351 static const jschar comment_prefix_ucNstr
[] = {'<', '!', '-', '-'};
2352 static const jschar comment_suffix_ucNstr
[] = {'-', '-', '>'};
2354 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2355 comment_prefix_ucNstr
, 4,
2356 comment_suffix_ucNstr
, 3);
2360 MakeXMLPIString(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*name
,
2363 static const jschar pi_prefix_ucNstr
[] = {'<', '?'};
2364 static const jschar pi_suffix_ucNstr
[] = {'?', '>'};
2366 return MakeXMLSpecialString(cx
, sb
, name
, value
,
2367 pi_prefix_ucNstr
, 2,
2368 pi_suffix_ucNstr
, 2);
2372 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2373 * equals, a double quote, an attribute value, and a closing double quote.
2376 AppendAttributeValue(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*valstr
)
2378 js_AppendChar(sb
, '=');
2379 valstr
= js_EscapeAttributeValue(cx
, valstr
, JS_TRUE
);
2381 if (STRING_BUFFER_OK(sb
)) {
2383 sb
->base
= STRING_BUFFER_ERROR_BASE
;
2387 js_AppendJSString(sb
, valstr
);
2391 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2393 * This function takes ownership of sb->base, if sb is non-null, in all cases
2394 * of success or failure.
2397 EscapeElementValue(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
)
2399 size_t length
, newlength
;
2400 const jschar
*cp
, *start
, *end
;
2403 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
2405 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
2407 if (c
== '<' || c
== '>')
2412 if (newlength
< length
) {
2413 js_ReportAllocationOverflow(cx
);
2417 if ((sb
&& STRING_BUFFER_OFFSET(sb
) != 0) || newlength
> length
) {
2418 JSStringBuffer localSB
;
2421 js_InitStringBuffer(sb
);
2423 if (!sb
->grow(sb
, newlength
)) {
2424 JS_ReportOutOfMemory(cx
);
2427 for (cp
= start
; cp
< end
; cp
++) {
2430 js_AppendCString(sb
, js_lt_entity_str
);
2432 js_AppendCString(sb
, js_gt_entity_str
);
2434 js_AppendCString(sb
, js_amp_entity_str
);
2436 js_AppendChar(sb
, c
);
2438 JS_ASSERT(STRING_BUFFER_OK(sb
));
2439 str
= js_NewString(cx
, sb
->base
, STRING_BUFFER_OFFSET(sb
));
2441 js_FinishStringBuffer(sb
);
2447 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2448 * This function takes ownership of sb->base, if sb is non-null, in all cases.
2451 EscapeAttributeValue(JSContext
*cx
, JSStringBuffer
*sb
, JSString
*str
,
2454 size_t length
, newlength
;
2455 const jschar
*cp
, *start
, *end
;
2458 JSSTRING_CHARS_AND_LENGTH(str
, start
, length
);
2459 newlength
= length
+ (quote
? 2 : 0);
2460 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
2466 else if (c
== '&' || c
== '\n' || c
== '\r' || c
== '\t')
2469 if (newlength
< length
) {
2470 js_ReportAllocationOverflow(cx
);
2474 if ((sb
&& STRING_BUFFER_OFFSET(sb
) != 0) || newlength
> length
) {
2475 JSStringBuffer localSB
;
2478 js_InitStringBuffer(sb
);
2480 if (!sb
->grow(sb
, newlength
)) {
2481 JS_ReportOutOfMemory(cx
);
2485 js_AppendChar(sb
, '"');
2486 for (cp
= start
; cp
< end
; cp
++) {
2489 js_AppendCString(sb
, js_quot_entity_str
);
2491 js_AppendCString(sb
, js_lt_entity_str
);
2493 js_AppendCString(sb
, js_amp_entity_str
);
2495 js_AppendCString(sb
, "
");
2497 js_AppendCString(sb
, "
");
2499 js_AppendCString(sb
, "	");
2501 js_AppendChar(sb
, c
);
2504 js_AppendChar(sb
, '"');
2505 JS_ASSERT(STRING_BUFFER_OK(sb
));
2506 str
= js_NewString(cx
, sb
->base
, STRING_BUFFER_OFFSET(sb
));
2508 js_FinishStringBuffer(sb
);
2513 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2514 static JSXMLNamespace
*
2515 GetNamespace(JSContext
*cx
, JSXMLQName
*qn
, const JSXMLArray
*inScopeNSes
)
2517 JSXMLNamespace
*match
, *ns
;
2524 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2525 JSMSG_BAD_XML_NAMESPACE
,
2527 ? js_ValueToPrintableString(cx
,
2528 STRING_TO_JSVAL(qn
->prefix
))
2529 : js_undefined_str
);
2533 /* Look for a matching namespace in inScopeNSes, if provided. */
2536 for (i
= 0, n
= inScopeNSes
->length
; i
< n
; i
++) {
2537 ns
= XMLARRAY_MEMBER(inScopeNSes
, i
, JSXMLNamespace
);
2542 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2543 * If we preserve prefixes, we must match null qn->prefix against
2544 * an empty ns->prefix, in order to avoid generating redundant
2545 * prefixed and default namespaces for cases such as:
2547 * x = <t xmlns="http://foo.com"/>
2548 * print(x.toXMLString());
2550 * Per 10.3.2.1, the namespace attribute in t has an empty string
2551 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2553 * 1. If the [local name] property of a is "xmlns"
2554 * a. Map ns.prefix to the empty string
2556 * But t's name has a null prefix in this implementation, meaning
2557 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2558 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2559 * saying how "no value" maps to an ECMA-357 value -- but it must
2560 * map to the *undefined* prefix value).
2562 * Since "" != undefined (or null, in the current implementation)
2563 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2564 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2565 * This spec bug leads to ToXMLString results that duplicate the
2566 * declared namespace.
2568 if (js_EqualStrings(ns
->uri
, qn
->uri
) &&
2569 (ns
->prefix
== qn
->prefix
||
2570 ((ns
->prefix
&& qn
->prefix
)
2571 ? js_EqualStrings(ns
->prefix
, qn
->prefix
)
2572 : IS_EMPTY(ns
->prefix
? ns
->prefix
: qn
->prefix
)))) {
2579 /* If we didn't match, make a new namespace from qn. */
2581 argv
[0] = qn
->prefix
? STRING_TO_JSVAL(qn
->prefix
) : JSVAL_VOID
;
2582 argv
[1] = STRING_TO_JSVAL(qn
->uri
);
2583 nsobj
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
,
2587 match
= (JSXMLNamespace
*) JS_GetPrivate(cx
, nsobj
);
2593 GeneratePrefix(JSContext
*cx
, JSString
*uri
, JSXMLArray
*decls
)
2595 const jschar
*cp
, *start
, *end
;
2596 size_t length
, newlength
, offset
;
2597 uint32 i
, n
, m
, serial
;
2603 JS_ASSERT(!IS_EMPTY(uri
));
2606 * If there are no *declared* namespaces, skip all collision detection and
2607 * return a short prefix quickly; an example of such a situation:
2610 * var n = new Namespace("http://example.com/");
2611 * x.@n::att = "val";
2614 * This is necessary for various log10 uses below to be valid.
2616 if (decls
->length
== 0)
2617 return JS_NewStringCopyZ(cx
, "a");
2620 * Try peeling off the last filename suffix or pathname component till
2621 * we have a valid XML name. This heuristic will prefer "xul" given
2622 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2623 * likely URI of the form ".../xbl2/2005".
2625 JSSTRING_CHARS_AND_END(uri
, start
, end
);
2627 while (--cp
> start
) {
2628 if (*cp
== '.' || *cp
== '/' || *cp
== ':') {
2630 length
= PTRDIFF(end
, cp
, jschar
);
2631 if (IsXMLName(cp
, length
) && !STARTS_WITH_XML(cp
, length
))
2636 length
= PTRDIFF(end
, cp
, jschar
);
2639 * If the namespace consisted only of non-XML names or names that begin
2640 * case-insensitively with "xml", arbitrarily create a prefix consisting
2641 * of 'a's of size length (allowing dp-calculating code to work with or
2642 * without this branch executing) plus the space for storing a hyphen and
2643 * the serial number (avoiding reallocation if a collision happens).
2647 if (STARTS_WITH_XML(cp
, length
) || !IsXMLName(cp
, length
)) {
2648 newlength
= length
+ 2 + (size_t) log10((double) decls
->length
);
2650 JS_malloc(cx
, (newlength
+ 1) * sizeof(jschar
));
2655 for (i
= 0; i
< newlength
; i
++)
2660 * Now search through decls looking for a collision. If we collide with
2661 * an existing prefix, start tacking on a hyphen and a serial number.
2666 for (i
= 0, n
= decls
->length
; i
< n
; i
++) {
2667 ns
= XMLARRAY_MEMBER(decls
, i
, JSXMLNamespace
);
2668 if (ns
&& ns
->prefix
&&
2669 JSSTRING_LENGTH(ns
->prefix
) == newlength
&&
2670 !memcmp(JSSTRING_CHARS(ns
->prefix
), bp
,
2671 newlength
* sizeof(jschar
))) {
2673 newlength
= length
+ 2 + (size_t) log10((double) n
);
2675 JS_malloc(cx
, (newlength
+ 1) * sizeof(jschar
));
2678 js_strncpy(bp
, cp
, length
);
2682 JS_ASSERT(serial
<= n
);
2683 dp
= bp
+ length
+ 2 + (size_t) log10((double) serial
);
2685 for (m
= serial
; m
!= 0; m
/= 10)
2686 *--dp
= (jschar
)('0' + m
% 10);
2688 JS_ASSERT(dp
== bp
+ length
);
2697 offset
= PTRDIFF(cp
, start
, jschar
);
2698 prefix
= js_NewDependentString(cx
, uri
, offset
, length
);
2700 prefix
= js_NewString(cx
, bp
, newlength
);
2708 namespace_match(const void *a
, const void *b
)
2710 const JSXMLNamespace
*nsa
= (const JSXMLNamespace
*) a
;
2711 const JSXMLNamespace
*nsb
= (const JSXMLNamespace
*) b
;
2714 return nsa
->prefix
&& js_EqualStrings(nsa
->prefix
, nsb
->prefix
);
2715 return js_EqualStrings(nsa
->uri
, nsb
->uri
);
2718 /* ECMA-357 10.2.1 and 10.2.2 */
2719 #define TO_SOURCE_FLAG 0x80000000
2722 XMLToXMLString(JSContext
*cx
, JSXML
*xml
, const JSXMLArray
*ancestorNSes
,
2725 JSBool pretty
, indentKids
;
2727 JSString
*str
, *prefix
, *kidstr
;
2728 JSXMLArrayCursor cursor
;
2729 uint32 i
, n
, nextIndentLevel
;
2730 JSXMLArray empty
, decls
, ancdecls
;
2731 JSXMLNamespace
*ns
, *ns2
;
2734 if (!GetBooleanXMLSetting(cx
, js_prettyPrinting_str
, &pretty
))
2737 js_InitStringBuffer(&sb
);
2739 js_RepeatChar(&sb
, ' ', indentLevel
& ~TO_SOURCE_FLAG
);
2741 if (!STRING_BUFFER_OK(&sb
)) {
2742 JS_ReportOutOfMemory(cx
);
2748 switch (xml
->xml_class
) {
2749 case JSXML_CLASS_TEXT
:
2752 str
= ChompXMLWhitespace(cx
, xml
->xml_value
);
2756 str
= xml
->xml_value
;
2758 return EscapeElementValue(cx
, &sb
, str
);
2760 case JSXML_CLASS_ATTRIBUTE
:
2762 return EscapeAttributeValue(cx
, &sb
, xml
->xml_value
,
2763 (indentLevel
& TO_SOURCE_FLAG
) != 0);
2765 case JSXML_CLASS_COMMENT
:
2767 return MakeXMLCommentString(cx
, &sb
, xml
->xml_value
);
2769 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
2771 return MakeXMLPIString(cx
, &sb
, xml
->name
->localName
, xml
->xml_value
);
2773 case JSXML_CLASS_LIST
:
2774 /* ECMA-357 10.2.2. */
2775 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
2777 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2778 if (pretty
&& i
!= 0)
2779 js_AppendChar(&sb
, '\n');
2781 kidstr
= XMLToXMLString(cx
, kid
, ancestorNSes
, indentLevel
);
2785 js_AppendJSString(&sb
, kidstr
);
2788 XMLArrayCursorFinish(&cursor
);
2793 return cx
->runtime
->emptyString
;
2795 if (!STRING_BUFFER_OK(&sb
)) {
2796 JS_ReportOutOfMemory(cx
);
2800 str
= js_NewString(cx
, sb
.base
, STRING_BUFFER_OFFSET(&sb
));
2802 if (!str
&& STRING_BUFFER_OK(&sb
))
2803 js_FinishStringBuffer(&sb
);
2809 /* After this point, control must flow through label out: to exit. */
2810 if (!js_EnterLocalRootScope(cx
))
2813 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2814 if (!ancestorNSes
) {
2815 XMLArrayInit(cx
, &empty
, 0);
2816 ancestorNSes
= &empty
;
2818 XMLArrayInit(cx
, &decls
, 0);
2819 ancdecls
.capacity
= 0;
2821 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2822 XMLArrayCursorInit(&cursor
, &xml
->xml_namespaces
);
2823 while ((ns
= (JSXMLNamespace
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2826 if (!XMLARRAY_HAS_MEMBER(ancestorNSes
, ns
, namespace_identity
)) {
2827 /* NOTE: may want to exclude unused namespaces here. */
2828 ns2
= js_NewXMLNamespace(cx
, ns
->prefix
, ns
->uri
, JS_TRUE
);
2829 if (!ns2
|| !XMLARRAY_APPEND(cx
, &decls
, ns2
))
2833 XMLArrayCursorFinish(&cursor
);
2838 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2839 * not own its member references. In the spec, ancdecls has no name, but
2840 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2842 if (!XMLArrayInit(cx
, &ancdecls
, ancestorNSes
->length
+ decls
.length
))
2844 for (i
= 0, n
= ancestorNSes
->length
; i
< n
; i
++) {
2845 ns2
= XMLARRAY_MEMBER(ancestorNSes
, i
, JSXMLNamespace
);
2848 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls
, ns2
, namespace_identity
));
2849 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns2
))
2852 for (i
= 0, n
= decls
.length
; i
< n
; i
++) {
2853 ns2
= XMLARRAY_MEMBER(&decls
, i
, JSXMLNamespace
);
2856 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls
, ns2
, namespace_identity
));
2857 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns2
))
2861 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2862 ns
= GetNamespace(cx
, xml
->name
, &ancdecls
);
2866 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2869 * Create a namespace prefix that isn't used by any member of decls.
2870 * Assign the new prefix to a copy of ns. Flag this namespace as if
2871 * it were declared, for assertion-testing's sake later below.
2873 * Erratum: if ns->prefix and xml->name are both null (*undefined* in
2874 * ECMA-357), we know that xml was named using the default namespace
2875 * (proof: see GetNamespace and the Namespace constructor called with
2876 * two arguments). So we ought not generate a new prefix here, when
2877 * we can declare ns as the default namespace for xml.
2879 * This helps descendants inherit the namespace instead of redundantly
2880 * redeclaring it with generated prefixes in each descendant.
2882 if (!xml
->name
->prefix
) {
2883 prefix
= cx
->runtime
->emptyString
;
2885 prefix
= GeneratePrefix(cx
, ns
->uri
, &ancdecls
);
2889 ns
= js_NewXMLNamespace(cx
, prefix
, ns
->uri
, JS_TRUE
);
2894 * If the xml->name was unprefixed, we must remove any declared default
2895 * namespace from decls before appending ns. How can you get a default
2896 * namespace in decls that doesn't match the one from name? Apparently
2897 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2898 * to fix this is to update x's in-scope namespaces when setNamespace
2899 * is called, but that's not specified by ECMA-357.
2901 * Likely Erratum here, depending on whether the lack of update to x's
2902 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2903 * erratum or not. Note that changing setNamespace to update the list
2904 * of in-scope namespaces will change x.namespaceDeclarations().
2906 if (IS_EMPTY(prefix
)) {
2907 i
= XMLArrayFindMember(&decls
, ns
, namespace_match
);
2908 if (i
!= XML_NOT_FOUND
)
2909 XMLArrayDelete(cx
, &decls
, i
, JS_TRUE
);
2913 * In the spec, ancdecls has no name, but is always written out as
2914 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2915 * that union in ancdecls, any time we append a namespace strong
2916 * ref to decls, we must also append a weak ref to ancdecls. Order
2917 * matters here: code at label out: releases strong refs in decls.
2919 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns
) ||
2920 !XMLARRAY_APPEND(cx
, &decls
, ns
)) {
2925 /* Format the element or point-tag into sb. */
2926 js_AppendChar(&sb
, '<');
2928 if (ns
->prefix
&& !IS_EMPTY(ns
->prefix
)) {
2929 js_AppendJSString(&sb
, ns
->prefix
);
2930 js_AppendChar(&sb
, ':');
2932 js_AppendJSString(&sb
, xml
->name
->localName
);
2935 * Step 16 makes a union to avoid writing two loops in step 17, to share
2936 * common attribute value appending spec-code. We prefer two loops for
2937 * faster code and less data overhead.
2940 /* Step 17(b): append attributes. */
2941 XMLArrayCursorInit(&cursor
, &xml
->xml_attrs
);
2942 while ((attr
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2943 js_AppendChar(&sb
, ' ');
2944 ns2
= GetNamespace(cx
, attr
->name
, &ancdecls
);
2948 /* 17(b)(ii): NULL means *undefined* here. */
2950 prefix
= GeneratePrefix(cx
, ns2
->uri
, &ancdecls
);
2954 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2955 ns2
= js_NewXMLNamespace(cx
, prefix
, ns2
->uri
, JS_TRUE
);
2960 * In the spec, ancdecls has no name, but is always written out as
2961 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2962 * that union in ancdecls, any time we append a namespace strong
2963 * ref to decls, we must also append a weak ref to ancdecls. Order
2964 * matters here: code at label out: releases strong refs in decls.
2966 if (!XMLARRAY_APPEND(cx
, &ancdecls
, ns2
) ||
2967 !XMLARRAY_APPEND(cx
, &decls
, ns2
)) {
2973 if (!IS_EMPTY(ns2
->prefix
)) {
2974 js_AppendJSString(&sb
, ns2
->prefix
);
2975 js_AppendChar(&sb
, ':');
2979 js_AppendJSString(&sb
, attr
->name
->localName
);
2982 AppendAttributeValue(cx
, &sb
, attr
->xml_value
);
2984 XMLArrayCursorFinish(&cursor
);
2988 /* Step 17(c): append XML namespace declarations. */
2989 XMLArrayCursorInit(&cursor
, &decls
);
2990 while ((ns2
= (JSXMLNamespace
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
2991 JS_ASSERT(ns2
->declared
);
2993 js_AppendCString(&sb
, " xmlns");
2995 /* 17(c)(ii): NULL means *undefined* here. */
2997 prefix
= GeneratePrefix(cx
, ns2
->uri
, &ancdecls
);
3000 ns2
->prefix
= prefix
;
3004 if (!IS_EMPTY(ns2
->prefix
)) {
3005 js_AppendChar(&sb
, ':');
3006 js_AppendJSString(&sb
, ns2
->prefix
);
3010 AppendAttributeValue(cx
, &sb
, ns2
->uri
);
3012 XMLArrayCursorFinish(&cursor
);
3016 /* Step 18: handle point tags. */
3017 n
= xml
->xml_kids
.length
;
3019 js_AppendCString(&sb
, "/>");
3021 /* Steps 19 through 25: handle element content, and open the end-tag. */
3022 js_AppendChar(&sb
, '>');
3023 indentKids
= n
> 1 ||
3025 (kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
)) &&
3026 kid
->xml_class
!= JSXML_CLASS_TEXT
);
3028 if (pretty
&& indentKids
) {
3029 if (!GetUint32XMLSetting(cx
, js_prettyIndent_str
, &i
))
3031 nextIndentLevel
= indentLevel
+ i
;
3033 nextIndentLevel
= indentLevel
& TO_SOURCE_FLAG
;
3036 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
3037 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
3038 if (pretty
&& indentKids
)
3039 js_AppendChar(&sb
, '\n');
3041 kidstr
= XMLToXMLString(cx
, kid
, &ancdecls
, nextIndentLevel
);
3045 js_AppendJSString(&sb
, kidstr
);
3047 XMLArrayCursorFinish(&cursor
);
3051 if (pretty
&& indentKids
) {
3052 js_AppendChar(&sb
, '\n');
3053 js_RepeatChar(&sb
, ' ', indentLevel
& ~TO_SOURCE_FLAG
);
3055 js_AppendCString(&sb
, "</");
3058 if (ns
->prefix
&& !IS_EMPTY(ns
->prefix
)) {
3059 js_AppendJSString(&sb
, ns
->prefix
);
3060 js_AppendChar(&sb
, ':');
3064 js_AppendJSString(&sb
, xml
->name
->localName
);
3065 js_AppendChar(&sb
, '>');
3068 if (!STRING_BUFFER_OK(&sb
)) {
3069 JS_ReportOutOfMemory(cx
);
3073 str
= js_NewString(cx
, sb
.base
, STRING_BUFFER_OFFSET(&sb
));
3075 js_LeaveLocalRootScopeWithResult(cx
, STRING_TO_JSVAL(str
));
3076 if (!str
&& STRING_BUFFER_OK(&sb
))
3077 js_FinishStringBuffer(&sb
);
3078 XMLArrayFinish(cx
, &decls
);
3079 if (ancdecls
.capacity
!= 0)
3080 XMLArrayFinish(cx
, &ancdecls
);
3086 ToXMLString(JSContext
*cx
, jsval v
, uint32 toSourceFlag
)
3092 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
3093 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3094 JSMSG_BAD_XML_CONVERSION
,
3095 JSVAL_IS_NULL(v
) ? js_null_str
: js_undefined_str
);
3099 if (JSVAL_IS_BOOLEAN(v
) || JSVAL_IS_NUMBER(v
))
3100 return js_ValueToString(cx
, v
);
3102 if (JSVAL_IS_STRING(v
))
3103 return EscapeElementValue(cx
, NULL
, JSVAL_TO_STRING(v
));
3105 obj
= JSVAL_TO_OBJECT(v
);
3106 if (!OBJECT_IS_XML(cx
, obj
)) {
3107 if (!OBJ_DEFAULT_VALUE(cx
, obj
, JSTYPE_STRING
, &v
))
3109 str
= js_ValueToString(cx
, v
);
3112 return EscapeElementValue(cx
, NULL
, str
);
3115 /* Handle non-element cases in this switch, returning from each case. */
3116 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
3117 return XMLToXMLString(cx
, xml
, NULL
, toSourceFlag
| 0);
3121 ToAttributeName(JSContext
*cx
, jsval v
)
3123 JSString
*name
, *uri
, *prefix
;
3127 JSTempValueRooter tvr
;
3129 if (JSVAL_IS_STRING(v
)) {
3130 name
= JSVAL_TO_STRING(v
);
3131 uri
= prefix
= cx
->runtime
->emptyString
;
3133 if (JSVAL_IS_PRIMITIVE(v
)) {
3134 js_ReportValueError(cx
, JSMSG_BAD_XML_ATTR_NAME
,
3135 JSDVG_IGNORE_STACK
, v
, NULL
);
3139 obj
= JSVAL_TO_OBJECT(v
);
3140 clasp
= OBJ_GET_CLASS(cx
, obj
);
3141 if (clasp
== &js_AttributeNameClass
)
3142 return (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
3144 if (clasp
== &js_QNameClass
.base
) {
3145 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
3147 prefix
= qn
->prefix
;
3148 name
= qn
->localName
;
3150 if (clasp
== &js_AnyNameClass
) {
3151 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
3153 name
= js_ValueToString(cx
, v
);
3157 uri
= prefix
= cx
->runtime
->emptyString
;
3161 qn
= js_NewXMLQName(cx
, uri
, prefix
, name
);
3165 JS_PUSH_TEMP_ROOT_QNAME(cx
, qn
, &tvr
);
3166 obj
= js_GetAttributeNameObject(cx
, qn
);
3167 JS_POP_TEMP_ROOT(cx
, &tvr
);
3174 ReportBadXMLName(JSContext
*cx
, jsval id
)
3176 js_ReportValueError(cx
, JSMSG_BAD_XML_NAME
, JSDVG_IGNORE_STACK
, id
, NULL
);
3180 IsFunctionQName(JSContext
*cx
, JSXMLQName
*qn
, jsid
*funidp
)
3184 atom
= cx
->runtime
->atomState
.lazy
.functionNamespaceURIAtom
;
3185 if (qn
->uri
&& atom
&&
3186 (qn
->uri
== ATOM_TO_STRING(atom
) ||
3187 js_EqualStrings(qn
->uri
, ATOM_TO_STRING(atom
)))) {
3188 return JS_ValueToId(cx
, STRING_TO_JSVAL(qn
->localName
), funidp
);
3195 js_IsFunctionQName(JSContext
*cx
, JSObject
*obj
, jsid
*funidp
)
3199 if (OBJ_GET_CLASS(cx
, obj
) == &js_QNameClass
.base
) {
3200 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
3201 return IsFunctionQName(cx
, qn
, funidp
);
3208 ToXMLName(JSContext
*cx
, jsval v
, jsid
*funidp
)
3216 if (JSVAL_IS_STRING(v
)) {
3217 name
= JSVAL_TO_STRING(v
);
3219 if (JSVAL_IS_PRIMITIVE(v
)) {
3220 ReportBadXMLName(cx
, v
);
3224 obj
= JSVAL_TO_OBJECT(v
);
3225 clasp
= OBJ_GET_CLASS(cx
, obj
);
3226 if (clasp
== &js_AttributeNameClass
|| clasp
== &js_QNameClass
.base
)
3228 if (clasp
== &js_AnyNameClass
) {
3229 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
3232 name
= js_ValueToString(cx
, v
);
3238 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
3240 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
3242 * First, _P_ should be _s_, to refer to the given string.
3244 * Second, why does ToXMLName applied to the string type throw TypeError
3245 * only for numeric literals without any leading or trailing whitespace?
3247 * If the idea is to reject uint32 property names, then the check needs to
3248 * be stricter, to exclude hexadecimal and floating point literals.
3250 if (js_IdIsIndex(STRING_TO_JSVAL(name
), &index
))
3253 if (*JSSTRING_CHARS(name
) == '@') {
3254 name
= js_NewDependentString(cx
, name
, 1, JSSTRING_LENGTH(name
) - 1);
3258 return ToAttributeName(cx
, STRING_TO_JSVAL(name
));
3262 v
= STRING_TO_JSVAL(name
);
3263 obj
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 1, &v
);
3268 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, obj
);
3269 if (!IsFunctionQName(cx
, qn
, funidp
))
3274 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3276 js_ValueToPrintableString(cx
, STRING_TO_JSVAL(name
)));
3280 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
3282 AddInScopeNamespace(JSContext
*cx
, JSXML
*xml
, JSXMLNamespace
*ns
)
3284 JSXMLNamespace
*match
, *ns2
;
3287 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
3290 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
3293 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3294 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSXMLNamespace
);
3295 if (ns2
&& js_EqualStrings(ns2
->uri
, ns
->uri
)) {
3300 if (!match
&& !XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_namespaces
, n
, ns
))
3303 if (IS_EMPTY(ns
->prefix
) && IS_EMPTY(xml
->name
->uri
))
3306 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3309 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3310 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSXMLNamespace
);
3311 if (ns2
&& ns2
->prefix
&&
3312 js_EqualStrings(ns2
->prefix
, ns
->prefix
)) {
3318 if (match
&& !js_EqualStrings(match
->uri
, ns
->uri
)) {
3319 ns2
= XMLARRAY_DELETE(cx
, &xml
->xml_namespaces
, m
, JS_TRUE
,
3321 JS_ASSERT(ns2
== match
);
3322 match
->prefix
= NULL
;
3323 if (!AddInScopeNamespace(cx
, xml
, match
))
3326 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
3330 /* OPTION: enforce that descendants have superset namespaces. */
3334 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3336 Append(JSContext
*cx
, JSXML
*list
, JSXML
*xml
)
3341 JS_ASSERT(list
->xml_class
== JSXML_CLASS_LIST
);
3342 i
= list
->xml_kids
.length
;
3344 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3345 list
->xml_target
= xml
->xml_target
;
3346 list
->xml_targetprop
= xml
->xml_targetprop
;
3347 n
= JSXML_LENGTH(xml
);
3349 if (!XMLArraySetCapacity(cx
, &list
->xml_kids
, k
))
3351 for (j
= 0; j
< n
; j
++) {
3352 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, j
, JSXML
);
3354 XMLARRAY_SET_MEMBER(&list
->xml_kids
, i
+ j
, kid
);
3359 list
->xml_target
= xml
->parent
;
3360 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
3361 list
->xml_targetprop
= NULL
;
3363 list
->xml_targetprop
= xml
->name
;
3364 if (!XMLARRAY_ADD_MEMBER(cx
, &list
->xml_kids
, i
, xml
))
3369 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3371 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
);
3374 DeepCopy(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
, uintN flags
)
3379 /* Our caller may not be protecting newborns with a local root scope. */
3380 if (!js_EnterLocalRootScope(cx
))
3382 copy
= DeepCopyInLRS(cx
, xml
, flags
);
3385 /* Caller provided the object for this copy, hook 'em up. */
3386 ok
= JS_SetPrivate(cx
, obj
, copy
);
3390 ok
= js_GetXMLObject(cx
, copy
) != NULL
;
3395 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) copy
);
3400 * (i) We must be in a local root scope (InLRS).
3401 * (ii) parent must have a rooted object.
3402 * (iii) from's owning object must be locked if not thread-local.
3405 DeepCopySetInLRS(JSContext
*cx
, JSXMLArray
*from
, JSXMLArray
*to
, JSXML
*parent
,
3409 JSXMLArrayCursor cursor
;
3414 JS_ASSERT(cx
->localRootStack
);
3417 if (!XMLArraySetCapacity(cx
, to
, n
))
3420 XMLArrayCursorInit(&cursor
, from
);
3423 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
3424 if ((flags
& XSF_IGNORE_COMMENTS
) &&
3425 kid
->xml_class
== JSXML_CLASS_COMMENT
) {
3428 if ((flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
) &&
3429 kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
3432 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3433 (kid
->xml_flags
& XMLF_WHITESPACE_TEXT
)) {
3436 kid2
= DeepCopyInLRS(cx
, kid
, flags
);
3443 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3444 n
> 1 && kid2
->xml_class
== JSXML_CLASS_TEXT
) {
3445 str
= ChompXMLWhitespace(cx
, kid2
->xml_value
);
3451 kid2
->xml_value
= str
;
3454 XMLARRAY_SET_MEMBER(to
, j
, kid2
);
3456 if (parent
->xml_class
!= JSXML_CLASS_LIST
)
3457 kid2
->parent
= parent
;
3459 XMLArrayCursorFinish(&cursor
);
3469 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
)
3475 JSXMLNamespace
*ns
, *ns2
;
3477 /* Our caller must be protecting newborn objects. */
3478 JS_ASSERT(cx
->localRootStack
);
3480 JS_CHECK_RECURSION(cx
, return NULL
);
3482 copy
= js_NewXML(cx
, (JSXMLClass
) xml
->xml_class
);
3487 qn
= js_NewXMLQName(cx
, qn
->uri
, qn
->prefix
, qn
->localName
);
3494 copy
->xml_flags
= xml
->xml_flags
;
3496 if (JSXML_HAS_VALUE(xml
)) {
3497 copy
->xml_value
= xml
->xml_value
;
3500 ok
= DeepCopySetInLRS(cx
, &xml
->xml_kids
, ©
->xml_kids
, copy
, flags
);
3504 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3505 copy
->xml_target
= xml
->xml_target
;
3506 copy
->xml_targetprop
= xml
->xml_targetprop
;
3508 n
= xml
->xml_namespaces
.length
;
3509 ok
= XMLArraySetCapacity(cx
, ©
->xml_namespaces
, n
);
3512 for (i
= 0; i
< n
; i
++) {
3513 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSXMLNamespace
);
3516 ns2
= js_NewXMLNamespace(cx
, ns
->prefix
, ns
->uri
, ns
->declared
);
3518 copy
->xml_namespaces
.length
= i
;
3522 XMLARRAY_SET_MEMBER(©
->xml_namespaces
, i
, ns2
);
3525 ok
= DeepCopySetInLRS(cx
, &xml
->xml_attrs
, ©
->xml_attrs
, copy
,
3538 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3540 DeleteByIndex(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3544 if (JSXML_HAS_KIDS(xml
) && index
< xml
->xml_kids
.length
) {
3545 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3548 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3552 typedef JSBool (*JSXMLNameMatcher
)(JSXMLQName
*nameqn
, JSXML
*xml
);
3555 MatchAttrName(JSXMLQName
*nameqn
, JSXML
*attr
)
3557 JSXMLQName
*attrqn
= attr
->name
;
3559 return (IS_STAR(nameqn
->localName
) ||
3560 js_EqualStrings(attrqn
->localName
, nameqn
->localName
)) &&
3562 js_EqualStrings(attrqn
->uri
, nameqn
->uri
));
3566 MatchElemName(JSXMLQName
*nameqn
, JSXML
*elem
)
3568 return (IS_STAR(nameqn
->localName
) ||
3569 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3570 js_EqualStrings(elem
->name
->localName
, nameqn
->localName
))) &&
3572 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3573 js_EqualStrings(elem
->name
->uri
, nameqn
->uri
)));
3576 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3578 DescendantsHelper(JSContext
*cx
, JSXML
*xml
, JSXMLQName
*nameqn
, JSXML
*list
)
3583 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
3585 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
&&
3586 OBJ_GET_CLASS(cx
, nameqn
->object
) == &js_AttributeNameClass
) {
3587 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
3588 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3589 if (attr
&& MatchAttrName(nameqn
, attr
)) {
3590 if (!Append(cx
, list
, attr
))
3596 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
3597 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3600 if (OBJ_GET_CLASS(cx
, nameqn
->object
) != &js_AttributeNameClass
&&
3601 MatchElemName(nameqn
, kid
)) {
3602 if (!Append(cx
, list
, kid
))
3605 if (!DescendantsHelper(cx
, kid
, nameqn
, list
))
3612 Descendants(JSContext
*cx
, JSXML
*xml
, jsval id
)
3621 nameqn
= ToXMLName(cx
, id
, &funid
);
3625 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
3628 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
3633 * Protect nameqn's object and strings from GC by linking list to it
3634 * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj,
3635 * which protects list. Any other object allocations occuring beneath
3636 * DescendantsHelper use local roots.
3638 list
->name
= nameqn
;
3639 if (!js_EnterLocalRootScope(cx
))
3641 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3643 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
3644 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3645 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
3646 ok
= DescendantsHelper(cx
, kid
, nameqn
, list
);
3652 ok
= DescendantsHelper(cx
, xml
, nameqn
, list
);
3654 js_LeaveLocalRootScopeWithResult(cx
, (jsval
) list
);
3662 xml_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
);
3664 /* Recursive (JSXML *) parameterized version of Equals. */
3666 XMLEquals(JSContext
*cx
, JSXML
*xml
, JSXML
*vxml
, JSBool
*bp
)
3668 JSXMLQName
*qn
, *vqn
;
3670 JSXMLArrayCursor cursor
, vcursor
;
3671 JSXML
*kid
, *vkid
, *attr
, *vattr
;
3673 JSObject
*xobj
, *vobj
;
3676 if (xml
->xml_class
!= vxml
->xml_class
) {
3677 if (xml
->xml_class
== JSXML_CLASS_LIST
&& xml
->xml_kids
.length
== 1) {
3678 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3682 if (vxml
->xml_class
== JSXML_CLASS_LIST
&& vxml
->xml_kids
.length
== 1) {
3683 vxml
= XMLARRAY_MEMBER(&vxml
->xml_kids
, 0, JSXML
);
3695 js_EqualStrings(qn
->localName
, vqn
->localName
) &&
3696 js_EqualStrings(qn
->uri
, vqn
->uri
);
3703 if (JSXML_HAS_VALUE(xml
)) {
3704 *bp
= js_EqualStrings(xml
->xml_value
, vxml
->xml_value
);
3705 } else if (xml
->xml_kids
.length
!= vxml
->xml_kids
.length
) {
3708 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
3709 XMLArrayCursorInit(&vcursor
, &vxml
->xml_kids
);
3711 kid
= (JSXML
*) XMLArrayCursorNext(&cursor
);
3712 vkid
= (JSXML
*) XMLArrayCursorNext(&vcursor
);
3713 if (!kid
|| !vkid
) {
3714 *bp
= !kid
&& !vkid
;
3718 xobj
= js_GetXMLObject(cx
, kid
);
3719 vobj
= js_GetXMLObject(cx
, vkid
);
3720 ok
= xobj
&& vobj
&&
3721 xml_equality(cx
, xobj
, OBJECT_TO_JSVAL(vobj
), bp
);
3725 XMLArrayCursorFinish(&vcursor
);
3726 XMLArrayCursorFinish(&cursor
);
3730 if (*bp
&& xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3731 n
= xml
->xml_attrs
.length
;
3732 if (n
!= vxml
->xml_attrs
.length
)
3734 for (i
= 0; *bp
&& i
< n
; i
++) {
3735 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3738 j
= XMLARRAY_FIND_MEMBER(&vxml
->xml_attrs
, attr
, attr_identity
);
3739 if (j
== XML_NOT_FOUND
) {
3743 vattr
= XMLARRAY_MEMBER(&vxml
->xml_attrs
, j
, JSXML
);
3746 *bp
= js_EqualStrings(attr
->xml_value
, vattr
->xml_value
);
3754 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3756 Equals(JSContext
*cx
, JSXML
*xml
, jsval v
, JSBool
*bp
)
3761 if (JSVAL_IS_PRIMITIVE(v
)) {
3763 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3764 if (xml
->xml_kids
.length
== 1) {
3765 vxml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3768 vobj
= js_GetXMLObject(cx
, vxml
);
3771 return js_XMLObjectOps
.equality(cx
, vobj
, v
, bp
);
3773 if (JSVAL_IS_VOID(v
) && xml
->xml_kids
.length
== 0)
3777 vobj
= JSVAL_TO_OBJECT(v
);
3778 if (!OBJECT_IS_XML(cx
, vobj
)) {
3781 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
3782 if (!XMLEquals(cx
, xml
, vxml
, bp
))
3790 CheckCycle(JSContext
*cx
, JSXML
*xml
, JSXML
*kid
)
3792 JS_ASSERT(kid
->xml_class
!= JSXML_CLASS_LIST
);
3796 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3797 JSMSG_CYCLIC_VALUE
, js_XML_str
);
3800 } while ((xml
= xml
->parent
) != NULL
);
3805 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3807 Insert(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3814 if (!JSXML_HAS_KIDS(xml
))
3819 if (!JSVAL_IS_PRIMITIVE(v
)) {
3820 vobj
= JSVAL_TO_OBJECT(v
);
3821 if (OBJECT_IS_XML(cx
, vobj
)) {
3822 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
3823 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3824 n
= vxml
->xml_kids
.length
;
3827 for (j
= 0; j
< n
; j
++) {
3828 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3831 if (!CheckCycle(cx
, xml
, kid
))
3834 } else if (vxml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3835 /* OPTION: enforce that descendants have superset namespaces. */
3836 if (!CheckCycle(cx
, xml
, vxml
))
3842 str
= js_ValueToString(cx
, v
);
3846 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3849 vxml
->xml_value
= str
;
3852 if (i
> xml
->xml_kids
.length
)
3853 i
= xml
->xml_kids
.length
;
3855 if (!XMLArrayInsert(cx
, &xml
->xml_kids
, i
, n
))
3858 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3859 for (j
= 0; j
< n
; j
++) {
3860 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3864 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
+ j
, kid
);
3866 /* OPTION: enforce that descendants have superset namespaces. */
3870 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
3876 IndexToIdVal(JSContext
*cx
, uint32 index
, jsval
*idvp
)
3880 if (index
<= JSVAL_INT_MAX
) {
3881 *idvp
= INT_TO_JSVAL(index
);
3883 str
= js_NumberToString(cx
, (jsdouble
) index
);
3886 *idvp
= STRING_TO_JSVAL(str
);
3891 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3893 Replace(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3900 if (!JSXML_HAS_KIDS(xml
))
3905 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3906 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3908 n
= xml
->xml_kids
.length
;
3913 if (!JSVAL_IS_PRIMITIVE(v
)) {
3914 vobj
= JSVAL_TO_OBJECT(v
);
3915 if (OBJECT_IS_XML(cx
, vobj
))
3916 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
3919 switch (vxml
? vxml
->xml_class
: JSXML_CLASS_LIMIT
) {
3920 case JSXML_CLASS_ELEMENT
:
3921 /* OPTION: enforce that descendants have superset namespaces. */
3922 if (!CheckCycle(cx
, xml
, vxml
))
3924 case JSXML_CLASS_COMMENT
:
3925 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
3926 case JSXML_CLASS_TEXT
:
3929 case JSXML_CLASS_LIST
:
3931 DeleteByIndex(cx
, xml
, i
);
3932 if (!Insert(cx
, xml
, i
, v
))
3937 str
= js_ValueToString(cx
, v
);
3941 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3944 vxml
->xml_value
= str
;
3949 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3953 if (!XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_kids
, i
, vxml
))
3961 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3963 DeleteNamedProperty(JSContext
*cx
, JSXML
*xml
, JSXMLQName
*nameqn
,
3967 uint32 index
, deleteCount
;
3969 JSXMLNameMatcher matcher
;
3971 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3972 array
= &xml
->xml_kids
;
3973 for (index
= 0; index
< array
->length
; index
++) {
3974 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3975 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
)
3976 DeleteNamedProperty(cx
, kid
, nameqn
, attributes
);
3978 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3980 array
= &xml
->xml_attrs
;
3981 matcher
= MatchAttrName
;
3983 array
= &xml
->xml_kids
;
3984 matcher
= MatchElemName
;
3987 for (index
= 0; index
< array
->length
; index
++) {
3988 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3989 if (kid
&& matcher(nameqn
, kid
)) {
3991 XMLArrayDelete(cx
, array
, index
, JS_FALSE
);
3993 } else if (deleteCount
!= 0) {
3994 XMLARRAY_SET_MEMBER(array
,
3995 index
- deleteCount
,
3996 array
->vector
[index
]);
3999 array
->length
-= deleteCount
;
4003 /* ECMA-357 9.2.1.3 index case. */
4005 DeleteListElement(JSContext
*cx
, JSXML
*xml
, uint32 index
)
4007 JSXML
*kid
, *parent
;
4010 JS_ASSERT(xml
->xml_class
== JSXML_CLASS_LIST
);
4012 if (index
< xml
->xml_kids
.length
) {
4013 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
4015 parent
= kid
->parent
;
4017 JS_ASSERT(parent
!= xml
);
4018 JS_ASSERT(JSXML_HAS_KIDS(parent
));
4020 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4021 DeleteNamedProperty(cx
, parent
, kid
->name
, JS_TRUE
);
4023 kidIndex
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
,
4025 JS_ASSERT(kidIndex
!= XML_NOT_FOUND
);
4026 DeleteByIndex(cx
, parent
, kidIndex
);
4029 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
4035 SyncInScopeNamespaces(JSContext
*cx
, JSXML
*xml
)
4037 JSXMLArray
*nsarray
;
4041 nsarray
= &xml
->xml_namespaces
;
4042 while ((xml
= xml
->parent
) != NULL
) {
4043 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
4044 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSXMLNamespace
);
4045 if (ns
&& !XMLARRAY_HAS_MEMBER(nsarray
, ns
, namespace_identity
)) {
4046 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
4055 GetNamedProperty(JSContext
*cx
, JSXML
*xml
, JSXMLQName
* nameqn
, JSXML
*list
)
4058 JSXMLNameMatcher matcher
;
4059 JSXMLArrayCursor cursor
;
4063 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4064 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
4065 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
4066 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
4067 !GetNamedProperty(cx
, kid
, nameqn
, list
)) {
4071 XMLArrayCursorFinish(&cursor
);
4074 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
4075 attrs
= (OBJ_GET_CLASS(cx
, nameqn
->object
) == &js_AttributeNameClass
);
4077 array
= &xml
->xml_attrs
;
4078 matcher
= MatchAttrName
;
4080 array
= &xml
->xml_kids
;
4081 matcher
= MatchElemName
;
4084 XMLArrayCursorInit(&cursor
, array
);
4085 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
4086 if (matcher(nameqn
, kid
)) {
4088 kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
4089 !SyncInScopeNamespaces(cx
, kid
)) {
4092 if (!Append(cx
, list
, kid
))
4096 XMLArrayCursorFinish(&cursor
);
4104 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
4106 GetProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4108 JSXML
*xml
, *list
, *kid
;
4110 JSObject
*kidobj
, *listobj
;
4114 JSTempValueRooter tvr
;
4116 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
4120 if (js_IdIsIndex(id
, &index
)) {
4121 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
4122 *vp
= (index
== 0) ? OBJECT_TO_JSVAL(obj
) : JSVAL_VOID
;
4125 * ECMA-357 9.2.1.1 starts here.
4127 * Erratum: 9.2 is not completely clear that indexed properties
4128 * correspond to kids, but that's what it seems to say, and it's
4129 * what any sane user would want.
4131 if (index
< xml
->xml_kids
.length
) {
4132 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
4137 kidobj
= js_GetXMLObject(cx
, kid
);
4141 *vp
= OBJECT_TO_JSVAL(kidobj
);
4150 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
4152 nameqn
= ToXMLName(cx
, id
, &funid
);
4156 return js_GetXMLFunction(cx
, obj
, funid
, vp
);
4158 roots
[0] = OBJECT_TO_JSVAL(nameqn
->object
);
4159 JS_PUSH_TEMP_ROOT(cx
, 1, roots
, &tvr
);
4161 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
4163 roots
[1] = OBJECT_TO_JSVAL(listobj
);
4166 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
4167 if (!GetNamedProperty(cx
, xml
, nameqn
, list
)) {
4171 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
4172 * given list's [[TargetProperty]] to the property that is being
4173 * appended. This means that any use of the internal [[Get]]
4174 * property returns a list which, when used by e.g. [[Insert]]
4175 * duplicates the last element matched by id. See bug 336921.
4177 list
->xml_target
= xml
;
4178 list
->xml_targetprop
= nameqn
;
4179 *vp
= OBJECT_TO_JSVAL(listobj
);
4183 JS_POP_TEMP_ROOT(cx
, &tvr
);
4184 return listobj
!= NULL
;
4188 CopyOnWrite(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
)
4190 JS_ASSERT(xml
->object
!= obj
);
4192 xml
= DeepCopy(cx
, xml
, obj
, 0);
4196 JS_ASSERT(xml
->object
== obj
);
4200 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
4201 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
4204 KidToString(JSContext
*cx
, JSXML
*xml
, uint32 index
)
4209 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
4211 return cx
->runtime
->emptyString
;
4212 kidobj
= js_GetXMLObject(cx
, kid
);
4215 return js_ValueToString(cx
, OBJECT_TO_JSVAL(kidobj
));
4218 /* Forward declared -- its implementation uses other statics that call it. */
4220 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
);
4222 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
4224 PutProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4226 JSBool ok
, primitiveAssign
;
4227 enum { OBJ_ROOT
, ID_ROOT
, VAL_ROOT
};
4229 JSTempValueRooter tvr
;
4230 JSXML
*xml
, *vxml
, *rxml
, *kid
, *attr
, *parent
, *copy
, *kid2
, *match
;
4231 JSObject
*vobj
, *nameobj
, *attrobj
, *parentobj
, *kidobj
, *copyobj
;
4232 JSXMLQName
*targetprop
, *nameqn
, *attrqn
;
4233 uint32 index
, i
, j
, k
, n
, q
, matchIndex
;
4234 jsval attrval
, nsval
;
4236 JSString
*left
, *right
, *space
;
4239 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
4243 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
4247 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
4249 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
4250 vobj
= JSVAL_TO_OBJECT(*vp
);
4251 if (OBJECT_IS_XML(cx
, vobj
))
4252 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
4255 /* Control flow after here must exit via label out. */
4256 ok
= js_EnterLocalRootScope(cx
);
4259 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
4260 roots
[ID_ROOT
] = id
;
4261 roots
[VAL_ROOT
] = *vp
;
4262 JS_PUSH_TEMP_ROOT(cx
, 3, roots
, &tvr
);
4264 if (js_IdIsIndex(id
, &index
)) {
4265 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
4266 /* See NOTE in spec: this variation is reserved for future use. */
4267 ReportBadXMLName(cx
, id
);
4272 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
4277 if (xml
->xml_target
) {
4278 ok
= ResolveValue(cx
, xml
->xml_target
, &rxml
);
4283 JS_ASSERT(rxml
->object
);
4289 if (index
>= xml
->xml_kids
.length
) {
4292 if (rxml
->xml_class
== JSXML_CLASS_LIST
) {
4293 if (rxml
->xml_kids
.length
!= 1)
4295 rxml
= XMLARRAY_MEMBER(&rxml
->xml_kids
, 0, JSXML
);
4298 ok
= js_GetXMLObject(cx
, rxml
) != NULL
;
4304 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
4305 * _y.[[Parent]] = r_ where _r_ is the result of
4306 * [[ResolveValue]] called on _x.[[TargetObject]] in
4307 * 2(a)(i). This can result in text parenting text:
4309 * var MYXML = new XML();
4310 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
4312 * (testcase from Werner Sharp <wsharp@macromedia.com>).
4314 * To match insertChildAfter, insertChildBefore,
4315 * prependChild, and setChildren, we should silently
4316 * do nothing in this case.
4318 if (!JSXML_HAS_KIDS(rxml
))
4322 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
4323 targetprop
= xml
->xml_targetprop
;
4324 if (!targetprop
|| IS_STAR(targetprop
->localName
)) {
4325 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
4326 kid
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
4330 nameobj
= js_GetXMLQNameObject(cx
, targetprop
);
4333 if (OBJ_GET_CLASS(cx
, nameobj
) == &js_AttributeNameClass
) {
4336 * Note that rxml can't be null here, because target
4337 * and targetprop are non-null.
4339 ok
= GetProperty(cx
, rxml
->object
, id
, &attrval
);
4342 if (JSVAL_IS_PRIMITIVE(attrval
)) /* no such attribute */
4344 attrobj
= JSVAL_TO_OBJECT(attrval
);
4345 attr
= (JSXML
*) JS_GetPrivate(cx
, attrobj
);
4346 if (JSXML_LENGTH(attr
) != 0)
4349 kid
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
4352 kid
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
4357 /* An important bit of 2(c)(ii). */
4358 kid
->name
= targetprop
;
4361 /* Final important bit of 2(c)(ii). */
4365 i
= xml
->xml_kids
.length
;
4366 if (kid
->xml_class
!= JSXML_CLASS_ATTRIBUTE
) {
4368 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
4369 * y.[[Parent]] is here called kid->parent, which we know
4370 * from 2(c)(ii) is _r_, here called rxml. So let's just
4371 * test that! Erratum, the spec should be simpler here.
4374 JS_ASSERT(JSXML_HAS_KIDS(rxml
));
4375 n
= rxml
->xml_kids
.length
;
4377 if (n
!= 0 && i
!= 0) {
4378 for (n
= j
, j
= 0; j
< n
; j
++) {
4379 if (rxml
->xml_kids
.vector
[j
] ==
4380 xml
->xml_kids
.vector
[i
-1]) {
4386 kidobj
= js_GetXMLObject(cx
, kid
);
4389 ok
= Insert(cx
, rxml
, j
+ 1, OBJECT_TO_JSVAL(kidobj
));
4396 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4397 * typo for [[TargetProperty]].
4400 kid
->name
= (vxml
->xml_class
== JSXML_CLASS_LIST
)
4401 ? vxml
->xml_targetprop
4407 ok
= Append(cx
, xml
, kid
);
4414 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4415 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4416 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4419 roots
[VAL_ROOT
] = *vp
;
4423 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
4426 parent
= kid
->parent
;
4427 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4428 nameobj
= js_GetAttributeNameObject(cx
, kid
->name
);
4431 id
= OBJECT_TO_JSVAL(nameobj
);
4435 parentobj
= js_GetXMLObject(cx
, parent
);
4438 ok
= PutProperty(cx
, parentobj
, id
, vp
);
4443 ok
= GetProperty(cx
, parentobj
, id
, vp
);
4446 attr
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(*vp
));
4449 xml
->xml_kids
.vector
[i
] = attr
->xml_kids
.vector
[0];
4454 else if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4458 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4459 * if we do that we never change the parent of each child in the
4460 * list. Since [[Put]] when called on an XML object deeply copies
4461 * the provided list _V_, we also do so here. Perhaps the shallow
4462 * copy was a misguided optimization?
4464 copy
= DeepCopyInLRS(cx
, vxml
, 0);
4467 copyobj
= js_GetXMLObject(cx
, copy
);
4471 JS_ASSERT(parent
!= xml
);
4473 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4474 JS_ASSERT(q
!= XML_NOT_FOUND
);
4475 ok
= Replace(cx
, parent
, q
, OBJECT_TO_JSVAL(copyobj
));
4480 /* Erratum: this loop in the spec is useless. */
4481 for (j
= 0, n
= copy
->xml_kids
.length
; j
< n
; j
++) {
4482 kid2
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
+ j
, JSXML
);
4483 JS_ASSERT(XMLARRAY_MEMBER(©
->xml_kids
, j
, JSXML
)
4491 * Erratum: notice the unhandled zero-length V basis case and
4492 * the off-by-one errors for the n != 0 cases in the spec.
4494 n
= copy
->xml_kids
.length
;
4496 XMLArrayDelete(cx
, &xml
->xml_kids
, i
, JS_TRUE
);
4498 ok
= XMLArrayInsert(cx
, &xml
->xml_kids
, i
+ 1, n
- 1);
4502 for (j
= 0; j
< n
; j
++)
4503 xml
->xml_kids
.vector
[i
+ j
] = copy
->xml_kids
.vector
[j
];
4508 else if (vxml
|| JSXML_HAS_VALUE(kid
)) {
4510 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4511 JS_ASSERT(q
!= XML_NOT_FOUND
);
4512 ok
= Replace(cx
, parent
, q
, *vp
);
4516 vxml
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
, JSXML
);
4519 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4524 * Erratum: _V_ may not be of type XML, but all index-named
4525 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4526 * according to 9.2.1.1 Overview and other places in the spec.
4528 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4529 * or an XML/XMLList object. If *vp is a string, call ToXML
4530 * on it to satisfy the constraint.
4533 JS_ASSERT(JSVAL_IS_STRING(*vp
));
4534 vobj
= ToXML(cx
, *vp
);
4537 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vobj
);
4538 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
4540 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
4545 kidobj
= js_GetXMLObject(cx
, kid
);
4548 id
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
4549 ok
= PutProperty(cx
, kidobj
, id
, vp
);
4555 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4557 nameqn
= ToXMLName(cx
, id
, &funid
);
4561 ok
= js_SetProperty(cx
, obj
, funid
, vp
);
4564 nameobj
= nameqn
->object
;
4565 roots
[ID_ROOT
] = OBJECT_TO_JSVAL(nameobj
);
4567 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4569 * Step 3 of 9.2.1.2.
4570 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4571 * or an r with r.[[Length]] != 1, throw TypeError.
4573 n
= JSXML_LENGTH(xml
);
4577 ok
= ResolveValue(cx
, xml
, &rxml
);
4580 if (!rxml
|| JSXML_LENGTH(rxml
) != 1)
4582 ok
= Append(cx
, xml
, rxml
);
4586 JS_ASSERT(JSXML_LENGTH(xml
) == 1);
4587 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
4590 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
4591 obj
= js_GetXMLObject(cx
, xml
);
4594 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
4596 /* FALL THROUGH to non-list case */
4601 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4602 * effort in ToString or [[DeepCopy]].
4605 if (JSXML_HAS_VALUE(xml
))
4609 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4610 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4611 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4615 rxml
= DeepCopyInLRS(cx
, vxml
, 0);
4616 if (!rxml
|| !js_GetXMLObject(cx
, rxml
))
4619 *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4621 roots
[VAL_ROOT
] = *vp
;
4625 * Erratum: why is this done here, so early? use is way later....
4627 ok
= js_GetDefaultXMLNamespace(cx
, &nsval
);
4631 if (OBJ_GET_CLASS(cx
, nameobj
) == &js_AttributeNameClass
) {
4633 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)))
4637 if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4638 n
= vxml
->xml_kids
.length
;
4640 *vp
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4642 left
= KidToString(cx
, vxml
, 0);
4646 space
= ATOM_TO_STRING(cx
->runtime
->atomState
.spaceAtom
);
4647 for (i
= 1; i
< n
; i
++) {
4648 left
= js_ConcatStrings(cx
, left
, space
);
4651 right
= KidToString(cx
, vxml
, i
);
4654 left
= js_ConcatStrings(cx
, left
, right
);
4659 roots
[VAL_ROOT
] = *vp
= STRING_TO_JSVAL(left
);
4662 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4665 roots
[VAL_ROOT
] = *vp
;
4670 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
4671 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
4674 attrqn
= attr
->name
;
4675 if (js_EqualStrings(attrqn
->localName
, nameqn
->localName
) &&
4677 js_EqualStrings(attrqn
->uri
, nameqn
->uri
))) {
4681 DeleteNamedProperty(cx
, xml
, attrqn
, JS_TRUE
);
4692 left
= right
= cx
->runtime
->emptyString
;
4695 right
= nameqn
->prefix
;
4697 nameqn
= js_NewXMLQName(cx
, left
, right
, nameqn
->localName
);
4702 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
4706 attr
->name
= nameqn
;
4709 ok
= XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_attrs
, n
, attr
);
4714 ns
= GetNamespace(cx
, nameqn
, NULL
);
4717 ok
= AddInScopeNamespace(cx
, xml
, ns
);
4723 attr
->xml_value
= JSVAL_TO_STRING(*vp
);
4728 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)) &&
4729 !IS_STAR(nameqn
->localName
)) {
4735 primitiveAssign
= !vxml
&& !IS_STAR(nameqn
->localName
);
4738 k
= n
= xml
->xml_kids
.length
;
4739 matchIndex
= XML_NOT_FOUND
;
4743 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, k
, JSXML
);
4744 if (kid
&& MatchElemName(nameqn
, kid
)) {
4745 if (matchIndex
!= XML_NOT_FOUND
)
4746 DeleteByIndex(cx
, xml
, matchIndex
);
4753 * Erratum: ECMA-357 specified child insertion inconsistently:
4754 * insertChildBefore and insertChildAfter insert an arbitrary XML
4755 * instance, and therefore can create cycles, but appendChild as
4756 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4757 * its argument. But the "Semantics" in 13.4.4.3 do not include
4758 * any [[DeepCopy]] call.
4760 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4761 * required adding cycle detection, and allowing duplicate kids to
4762 * be created (see comment 6 in the bug). Allowing duplicate kid
4763 * references means the loop above will delete all but the lowest
4764 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4765 * parent. Thus the need to restore parent here. This is covered
4766 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4769 JS_ASSERT(kid2
->parent
== xml
|| !kid2
->parent
);
4775 if (matchIndex
== XML_NOT_FOUND
) {
4780 if (primitiveAssign
) {
4782 ns
= (JSXMLNamespace
*)
4783 JS_GetPrivate(cx
, JSVAL_TO_OBJECT(nsval
));
4788 right
= nameqn
->prefix
;
4790 nameqn
= js_NewXMLQName(cx
, left
, right
, nameqn
->localName
);
4795 vobj
= js_NewXMLObject(cx
, JSXML_CLASS_ELEMENT
);
4798 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
4800 vxml
->name
= nameqn
;
4803 ns
= GetNamespace(cx
, nameqn
, NULL
);
4806 ok
= Replace(cx
, xml
, matchIndex
, OBJECT_TO_JSVAL(vobj
));
4809 ok
= AddInScopeNamespace(cx
, vxml
, ns
);
4816 if (primitiveAssign
) {
4817 JSXMLArrayCursor cursor
;
4819 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
4820 cursor
.index
= matchIndex
;
4821 kid
= (JSXML
*) XMLArrayCursorItem(&cursor
);
4822 if (JSXML_HAS_KIDS(kid
)) {
4823 XMLArrayFinish(cx
, &kid
->xml_kids
);
4824 ok
= XMLArrayInit(cx
, &kid
->xml_kids
, 1);
4828 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4830 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4831 if (ok
&& !IS_EMPTY(JSVAL_TO_STRING(*vp
))) {
4832 roots
[VAL_ROOT
] = *vp
;
4833 if ((JSXML
*) XMLArrayCursorItem(&cursor
) == kid
)
4834 ok
= Replace(cx
, kid
, 0, *vp
);
4837 XMLArrayCursorFinish(&cursor
);
4840 ok
= Replace(cx
, xml
, matchIndex
, *vp
);
4845 JS_POP_TEMP_ROOT(cx
, &tvr
);
4846 js_LeaveLocalRootScope(cx
);
4850 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4851 JSMSG_BAD_XMLLIST_PUT
,
4852 js_ValueToPrintableString(cx
, id
));
4858 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4860 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
)
4862 JSXML
*target
, *base
;
4863 JSXMLQName
*targetprop
;
4864 JSObject
*targetpropobj
;
4867 /* Our caller must be protecting newborn objects. */
4868 JS_ASSERT(cx
->localRootStack
);
4870 if (list
->xml_class
!= JSXML_CLASS_LIST
|| list
->xml_kids
.length
!= 0) {
4871 if (!js_GetXMLObject(cx
, list
))
4877 target
= list
->xml_target
;
4878 targetprop
= list
->xml_targetprop
;
4879 if (!target
|| !targetprop
|| IS_STAR(targetprop
->localName
)) {
4884 targetpropobj
= js_GetXMLQNameObject(cx
, targetprop
);
4887 if (OBJ_GET_CLASS(cx
, targetpropobj
) == &js_AttributeNameClass
) {
4892 if (!ResolveValue(cx
, target
, &base
))
4898 if (!js_GetXMLObject(cx
, base
))
4901 id
= OBJECT_TO_JSVAL(targetpropobj
);
4902 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4904 target
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(tv
));
4906 if (JSXML_LENGTH(target
) == 0) {
4907 if (base
->xml_class
== JSXML_CLASS_LIST
&& JSXML_LENGTH(base
) > 1) {
4911 tv
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4912 if (!PutProperty(cx
, base
->object
, id
, &tv
))
4914 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4916 target
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(tv
));
4924 HasNamedProperty(JSXML
*xml
, JSXMLQName
*nameqn
)
4927 JSXMLArrayCursor cursor
;
4930 JSXMLNameMatcher matcher
;
4933 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4935 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
4936 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
4937 found
= HasNamedProperty(kid
, nameqn
);
4941 XMLArrayCursorFinish(&cursor
);
4945 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
4946 if (OBJ_GET_CLASS(cx
, nameqn
->object
) == &js_AttributeNameClass
) {
4947 array
= &xml
->xml_attrs
;
4948 matcher
= MatchAttrName
;
4950 array
= &xml
->xml_kids
;
4951 matcher
= MatchElemName
;
4953 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
4954 kid
= XMLARRAY_MEMBER(array
, i
, JSXML
);
4955 if (kid
&& matcher(nameqn
, kid
))
4964 HasIndexedProperty(JSXML
*xml
, uint32 i
)
4966 if (xml
->xml_class
== JSXML_CLASS_LIST
)
4967 return i
< JSXML_LENGTH(xml
);
4969 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
)
4976 HasSimpleContent(JSXML
*xml
);
4979 HasFunctionProperty(JSContext
*cx
, JSObject
*obj
, jsid funid
, JSBool
*found
)
4984 JSTempValueRooter tvr
;
4987 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_XMLClass
);
4989 if (!js_LookupProperty(cx
, obj
, funid
, &pobj
, &prop
))
4992 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
4994 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
4995 if (HasSimpleContent(xml
)) {
4997 * Search in String.prototype to set found whenever
4998 * js_GetXMLFunction returns existing function.
5000 JS_PUSH_TEMP_ROOT_OBJECT(cx
, NULL
, &tvr
);
5001 ok
= js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(JSProto_String
),
5003 JS_ASSERT(tvr
.u
.object
);
5005 ok
= js_LookupProperty(cx
, tvr
.u
.object
, funid
, &pobj
, &prop
);
5007 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
5009 JS_POP_TEMP_ROOT(cx
, &tvr
);
5014 *found
= (prop
!= NULL
);
5018 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
5020 HasProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, JSBool
*found
)
5027 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5028 if (js_IdIsIndex(id
, &i
)) {
5029 *found
= HasIndexedProperty(xml
, i
);
5031 qn
= ToXMLName(cx
, id
, &funid
);
5035 if (!HasFunctionProperty(cx
, obj
, funid
, found
))
5038 *found
= HasNamedProperty(xml
, qn
);
5045 xml_finalize(JSContext
*cx
, JSObject
*obj
)
5049 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5052 if (xml
->object
== obj
)
5054 UNMETER(xml_stats
.livexmlobj
);
5058 xml_trace_vector(JSTracer
*trc
, JSXML
**vec
, uint32 len
)
5063 for (i
= 0; i
< len
; i
++) {
5066 JS_SET_TRACING_INDEX(trc
, "xml_vector", i
);
5067 JS_CallTracer(trc
, xml
, JSTRACE_XML
);
5073 * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
5074 * be native. Therefore, xml_lookupProperty must return a valid JSProperty
5075 * pointer parameter via *propp to signify "property found". Since the only
5076 * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
5077 * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case
5078 * in the interpreter, the only time we add a JSScopeProperty here is when an
5079 * unqualified name is being accessed or when "name in xml" is called.
5081 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
5082 * giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
5084 * NB: xml_deleteProperty must take care to remove any property added here.
5086 * FIXME This clashes with the function namespace implementation which also
5087 * uses native properties. Effectively after xml_lookupProperty any property
5088 * stored previously using assignments to xml.function::name will be removed.
5089 * We partially workaround the problem in js_GetXMLFunction. There we take
5090 * advantage of the fact that typically function:: is used to access the
5091 * functions from XML.prototype. So when js_GetProperty returns a non-function
5092 * property, we assume that it represents the result of GetProperty setter
5093 * hiding the function and use an extra prototype chain lookup to recover it.
5094 * For a proper solution see bug 355257.
5097 xml_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
5106 JSScopeProperty
*sprop
;
5108 v
= ID_TO_VALUE(id
);
5109 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5110 if (js_IdIsIndex(v
, &i
)) {
5111 found
= HasIndexedProperty(xml
, i
);
5113 qn
= ToXMLName(cx
, v
, &funid
);
5117 return js_LookupProperty(cx
, obj
, funid
, objp
, propp
);
5118 found
= HasNamedProperty(xml
, qn
);
5124 sprop
= js_AddNativeProperty(cx
, obj
, id
, GetProperty
, PutProperty
,
5125 SPROP_INVALID_SLOT
, JSPROP_ENUMERATE
,
5130 JS_LOCK_OBJ(cx
, obj
);
5132 *propp
= (JSProperty
*) sprop
;
5138 xml_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval value
,
5139 JSPropertyOp getter
, JSPropertyOp setter
, uintN attrs
,
5142 if (VALUE_IS_FUNCTION(cx
, value
) || getter
|| setter
||
5143 (attrs
& JSPROP_ENUMERATE
) == 0 ||
5144 (attrs
& (JSPROP_READONLY
| JSPROP_PERMANENT
| JSPROP_SHARED
))) {
5145 return js_DefineProperty(cx
, obj
, id
, value
, getter
, setter
, attrs
,
5149 if (!PutProperty(cx
, obj
, ID_TO_VALUE(id
), &value
))
5157 xml_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5159 if (id
== JS_DEFAULT_XML_NAMESPACE_ID
) {
5164 return GetProperty(cx
, obj
, ID_TO_VALUE(id
), vp
);
5168 xml_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5170 return PutProperty(cx
, obj
, ID_TO_VALUE(id
), vp
);
5174 FoundProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
5178 return HasProperty(cx
, obj
, ID_TO_VALUE(id
), foundp
);
5185 xml_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
5190 if (!FoundProperty(cx
, obj
, id
, prop
, &found
))
5192 *attrsp
= found
? JSPROP_ENUMERATE
: 0;
5197 xml_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
5202 if (!FoundProperty(cx
, obj
, id
, prop
, &found
))
5205 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5206 JSMSG_CANT_SET_XML_ATTRS
);
5212 xml_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*rval
)
5220 idval
= ID_TO_VALUE(id
);
5221 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5222 if (js_IdIsIndex(idval
, &index
)) {
5223 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
5224 /* See NOTE in spec: this variation is reserved for future use. */
5225 ReportBadXMLName(cx
, id
);
5229 /* ECMA-357 9.2.1.3. */
5230 DeleteListElement(cx
, xml
, index
);
5232 nameqn
= ToXMLName(cx
, idval
, &funid
);
5236 return js_DeleteProperty(cx
, obj
, funid
, rval
);
5238 DeleteNamedProperty(cx
, xml
, nameqn
,
5239 OBJ_GET_CLASS(cx
, nameqn
->object
) ==
5240 &js_AttributeNameClass
);
5244 * If this object has its own (mutable) scope, then we may have added a
5245 * property to the scope in xml_lookupProperty for it to return to mean
5246 * "found" and to provide a handle for access operations to call the
5247 * property's getter or setter. But now it's time to remove any such
5248 * property, to purge the property cache and remove the scope entry.
5250 if (OBJ_SCOPE(obj
)->object
== obj
&& !js_DeleteProperty(cx
, obj
, id
, rval
))
5258 xml_defaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, jsval
*vp
)
5262 if (hint
== JSTYPE_OBJECT
) {
5263 /* Called from for..in code in js_Interpret: return an XMLList. */
5264 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5265 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
5266 obj
= ToXMLList(cx
, OBJECT_TO_JSVAL(obj
));
5270 *vp
= OBJECT_TO_JSVAL(obj
);
5274 return JS_CallFunctionName(cx
, obj
, js_toString_str
, 0, NULL
, vp
);
5278 xml_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
5279 jsval
*statep
, jsid
*idp
)
5282 uint32 length
, index
;
5283 JSXMLArrayCursor
*cursor
;
5285 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5286 length
= JSXML_LENGTH(xml
);
5289 case JSENUMERATE_INIT
:
5293 cursor
= (JSXMLArrayCursor
*) JS_malloc(cx
, sizeof *cursor
);
5296 XMLArrayCursorInit(cursor
, &xml
->xml_kids
);
5298 *statep
= PRIVATE_TO_JSVAL(cursor
);
5300 *idp
= INT_TO_JSID(length
);
5303 case JSENUMERATE_NEXT
:
5304 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5305 if (cursor
&& cursor
->array
&& (index
= cursor
->index
) < length
) {
5306 *idp
= INT_TO_JSID(index
);
5307 cursor
->index
= index
+ 1;
5312 case JSENUMERATE_DESTROY
:
5313 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5315 XMLArrayCursorFinish(cursor
);
5316 JS_free(cx
, cursor
);
5318 *statep
= JSVAL_NULL
;
5325 xml_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
5331 xml_trace(JSTracer
*trc
, JSObject
*obj
)
5335 xml
= (JSXML
*) JS_GetPrivate(trc
->context
, obj
);
5337 JS_CALL_TRACER(trc
, xml
, JSTRACE_XML
, "private");
5341 xml_clear(JSContext
*cx
, JSObject
*obj
)
5346 HasSimpleContent(JSXML
*xml
)
5353 switch (xml
->xml_class
) {
5354 case JSXML_CLASS_COMMENT
:
5355 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
5357 case JSXML_CLASS_LIST
:
5358 if (xml
->xml_kids
.length
== 0)
5360 if (xml
->xml_kids
.length
== 1) {
5361 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5370 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5371 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5372 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5382 * 11.2.2.1 Step 3(d) onward.
5385 xml_getMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5387 JSTempValueRooter tvr
;
5389 JS_ASSERT(JS_InstanceOf(cx
, obj
, &js_XMLClass
, NULL
));
5392 * As our callers have a bad habit of passing a pointer to an unrooted
5393 * local value as vp, we use a proper root here.
5395 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
5396 if (!js_GetXMLFunction(cx
, obj
, id
, &tvr
.u
.value
))
5399 JS_POP_TEMP_ROOT(cx
, &tvr
);
5404 xml_setMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
5406 return js_SetProperty(cx
, obj
, id
, vp
);
5410 xml_enumerateValues(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
5411 jsval
*statep
, jsid
*idp
, jsval
*vp
)
5414 uint32 length
, index
;
5415 JSXMLArrayCursor
*cursor
;
5418 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5419 length
= JSXML_LENGTH(xml
);
5420 JS_ASSERT(INT_FITS_IN_JSVAL(length
));
5423 case JSENUMERATE_INIT
:
5427 cursor
= (JSXMLArrayCursor
*) JS_malloc(cx
, sizeof *cursor
);
5430 XMLArrayCursorInit(cursor
, &xml
->xml_kids
);
5432 *statep
= PRIVATE_TO_JSVAL(cursor
);
5434 *idp
= INT_TO_JSID(length
);
5439 case JSENUMERATE_NEXT
:
5440 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5441 if (cursor
&& cursor
->array
&& (index
= cursor
->index
) < length
) {
5442 while (!(kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
))) {
5443 if (++index
== length
)
5446 kidobj
= js_GetXMLObject(cx
, kid
);
5449 JS_ASSERT(INT_FITS_IN_JSVAL(index
));
5450 *idp
= INT_TO_JSID(index
);
5451 *vp
= OBJECT_TO_JSVAL(kidobj
);
5452 cursor
->index
= index
+ 1;
5457 case JSENUMERATE_DESTROY
:
5458 cursor
= (JSXMLArrayCursor
*) JSVAL_TO_PRIVATE(*statep
);
5461 XMLArrayCursorFinish(cursor
);
5462 JS_free(cx
, cursor
);
5464 *statep
= JSVAL_NULL
;
5471 xml_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
5476 JSString
*str
, *vstr
;
5479 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5481 if (!JSVAL_IS_PRIMITIVE(v
)) {
5482 vobj
= JSVAL_TO_OBJECT(v
);
5483 if (OBJECT_IS_XML(cx
, vobj
))
5484 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
5487 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5488 ok
= Equals(cx
, xml
, v
, bp
);
5490 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
5491 ok
= Equals(cx
, vxml
, OBJECT_TO_JSVAL(obj
), bp
);
5493 if (((xml
->xml_class
== JSXML_CLASS_TEXT
||
5494 xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5495 HasSimpleContent(vxml
)) ||
5496 ((vxml
->xml_class
== JSXML_CLASS_TEXT
||
5497 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5498 HasSimpleContent(xml
))) {
5499 ok
= js_EnterLocalRootScope(cx
);
5501 str
= js_ValueToString(cx
, OBJECT_TO_JSVAL(obj
));
5502 vstr
= js_ValueToString(cx
, v
);
5505 *bp
= js_EqualStrings(str
, vstr
);
5506 js_LeaveLocalRootScope(cx
);
5509 ok
= XMLEquals(cx
, xml
, vxml
, bp
);
5513 ok
= js_EnterLocalRootScope(cx
);
5515 if (HasSimpleContent(xml
)) {
5516 str
= js_ValueToString(cx
, OBJECT_TO_JSVAL(obj
));
5517 vstr
= js_ValueToString(cx
, v
);
5520 *bp
= js_EqualStrings(str
, vstr
);
5521 } else if (JSVAL_IS_STRING(v
) || JSVAL_IS_NUMBER(v
)) {
5522 str
= js_ValueToString(cx
, OBJECT_TO_JSVAL(obj
));
5525 } else if (JSVAL_IS_STRING(v
)) {
5526 *bp
= js_EqualStrings(str
, JSVAL_TO_STRING(v
));
5528 ok
= JS_ValueToNumber(cx
, STRING_TO_JSVAL(str
), &d
);
5530 d2
= JSVAL_IS_INT(v
) ? JSVAL_TO_INT(v
)
5531 : *JSVAL_TO_DOUBLE(v
);
5532 *bp
= JSDOUBLE_COMPARE(d
, ==, d2
, JS_FALSE
);
5538 js_LeaveLocalRootScope(cx
);
5545 xml_concatenate(JSContext
*cx
, JSObject
*obj
, jsval v
, jsval
*vp
)
5548 JSObject
*listobj
, *robj
;
5549 JSXML
*list
, *lxml
, *rxml
;
5551 ok
= js_EnterLocalRootScope(cx
);
5555 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5561 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
5562 lxml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
5563 ok
= Append(cx
, list
, lxml
);
5567 if (VALUE_IS_XML(cx
, v
)) {
5568 rxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5570 robj
= ToXML(cx
, v
);
5575 rxml
= (JSXML
*) JS_GetPrivate(cx
, robj
);
5577 ok
= Append(cx
, list
, rxml
);
5581 *vp
= OBJECT_TO_JSVAL(listobj
);
5583 js_LeaveLocalRootScopeWithResult(cx
, *vp
);
5587 /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */
5588 JS_FRIEND_DATA(JSXMLObjectOps
) js_XMLObjectOps
= {
5589 { js_NewObjectMap
, js_DestroyObjectMap
,
5590 xml_lookupProperty
, xml_defineProperty
,
5591 xml_getProperty
, xml_setProperty
,
5592 xml_getAttributes
, xml_setAttributes
,
5593 xml_deleteProperty
, xml_defaultValue
,
5594 xml_enumerate
, js_CheckAccess
,
5597 NULL
, xml_hasInstance
,
5598 js_SetProtoOrParent
, js_SetProtoOrParent
,
5599 js_TraceObject
, xml_clear
,
5601 xml_getMethod
, xml_setMethod
,
5602 xml_enumerateValues
, xml_equality
,
5606 static JSObjectOps
*
5607 xml_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
5609 return &js_XMLObjectOps
.base
;
5612 JS_FRIEND_DATA(JSClass
) js_XMLClass
= {
5614 JSCLASS_HAS_PRIVATE
| JSCLASS_MARK_IS_TRACE
|
5615 JSCLASS_HAS_CACHED_PROTO(JSProto_XML
),
5616 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
5617 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, xml_finalize
,
5618 xml_getObjectOps
, NULL
, NULL
, NULL
,
5619 NULL
, NULL
, JS_CLASS_TRACE(xml_trace
), NULL
5623 StartNonListXMLMethod(JSContext
*cx
, jsval
*vp
, JSObject
**objp
)
5629 JS_ASSERT(VALUE_IS_FUNCTION(cx
, *vp
));
5631 *objp
= JS_THIS_OBJECT(cx
, vp
);
5632 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, *objp
, &js_XMLClass
, vp
+ 2);
5633 if (!xml
|| xml
->xml_class
!= JSXML_CLASS_LIST
)
5636 if (xml
->xml_kids
.length
== 1) {
5637 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5639 *objp
= js_GetXMLObject(cx
, xml
);
5642 vp
[1] = OBJECT_TO_JSVAL(*objp
);
5647 fun
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
));
5648 JS_snprintf(numBuf
, sizeof numBuf
, "%u", xml
->xml_kids
.length
);
5649 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5650 JSMSG_NON_LIST_XML_METHOD
,
5651 JS_GetFunctionName(fun
), numBuf
);
5655 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5656 #define XML_METHOD_PROLOG \
5657 JSObject *obj = JS_THIS_OBJECT(cx, vp); \
5658 JSXML *xml = (JSXML *)JS_GetInstancePrivate(cx, obj, &js_XMLClass, vp+2); \
5662 #define NON_LIST_XML_METHOD_PROLOG \
5664 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5667 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5670 xml_addNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
5674 NON_LIST_XML_METHOD_PROLOG
;
5675 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
5677 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5681 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
5683 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
5685 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(*vp
));
5686 if (!AddInScopeNamespace(cx
, xml
, ns
))
5688 ns
->declared
= JS_TRUE
;
5691 *vp
= OBJECT_TO_JSVAL(obj
);
5696 xml_appendChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
5702 NON_LIST_XML_METHOD_PROLOG
;
5703 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5707 if (!js_GetAnyName(cx
, &name
))
5710 if (!GetProperty(cx
, obj
, name
, &v
))
5713 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5714 vobj
= JSVAL_TO_OBJECT(v
);
5715 JS_ASSERT(OBJECT_IS_XML(cx
, vobj
));
5716 vxml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
5717 JS_ASSERT(vxml
->xml_class
== JSXML_CLASS_LIST
);
5719 if (!IndexToIdVal(cx
, vxml
->xml_kids
.length
, &name
))
5721 *vp
= (argc
!= 0) ? vp
[2] : JSVAL_VOID
;
5722 if (!PutProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
))
5725 *vp
= OBJECT_TO_JSVAL(obj
);
5729 /* XML and XMLList */
5731 xml_attribute(JSContext
*cx
, uintN argc
, jsval
*vp
)
5736 js_ReportMissingArg(cx
, vp
, 0);
5740 qn
= ToAttributeName(cx
, vp
[2]);
5743 vp
[2] = OBJECT_TO_JSVAL(qn
->object
); /* local root */
5745 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), vp
[2], vp
);
5748 /* XML and XMLList */
5750 xml_attributes(JSContext
*cx
, uintN argc
, jsval
*vp
)
5754 JSTempValueRooter tvr
;
5757 name
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
5758 qn
= ToAttributeName(cx
, name
);
5761 name
= OBJECT_TO_JSVAL(qn
->object
);
5762 JS_PUSH_SINGLE_TEMP_ROOT(cx
, name
, &tvr
);
5763 ok
= GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), name
, vp
);
5764 JS_POP_TEMP_ROOT(cx
, &tvr
);
5769 xml_list_helper(JSContext
*cx
, JSXML
*xml
, jsval
*rval
)
5774 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5778 *rval
= OBJECT_TO_JSVAL(listobj
);
5779 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
5780 list
->xml_target
= xml
;
5785 xml_child_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval name
,
5792 /* ECMA-357 13.4.4.6 */
5793 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
5795 if (js_IdIsIndex(name
, &index
)) {
5796 if (index
>= JSXML_LENGTH(xml
)) {
5799 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
5803 kidobj
= js_GetXMLObject(cx
, kid
);
5806 *rval
= OBJECT_TO_JSVAL(kidobj
);
5812 return GetProperty(cx
, obj
, name
, rval
);
5815 /* XML and XMLList */
5817 xml_child(JSContext
*cx
, uintN argc
, jsval
*vp
)
5820 JSXML
*list
, *kid
, *vxml
;
5821 JSXMLArrayCursor cursor
;
5825 name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5826 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5827 /* ECMA-357 13.5.4.4 */
5828 list
= xml_list_helper(cx
, xml
, vp
);
5832 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
5833 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
5834 kidobj
= js_GetXMLObject(cx
, kid
);
5837 if (!xml_child_helper(cx
, kidobj
, kid
, name
, &v
))
5839 if (JSVAL_IS_VOID(v
)) {
5840 /* The property didn't exist in this kid. */
5844 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5845 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5846 if ((!JSXML_HAS_KIDS(vxml
) || vxml
->xml_kids
.length
!= 0) &&
5847 !Append(cx
, list
, vxml
)) {
5851 XMLArrayCursorFinish(&cursor
);
5855 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5856 if (!xml_child_helper(cx
, obj
, xml
, name
, vp
))
5858 if (JSVAL_IS_VOID(*vp
) && !xml_list_helper(cx
, xml
, vp
))
5864 xml_childIndex(JSContext
*cx
, uintN argc
, jsval
*vp
)
5869 NON_LIST_XML_METHOD_PROLOG
;
5870 parent
= xml
->parent
;
5871 if (!parent
|| xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
5872 *vp
= DOUBLE_TO_JSVAL(cx
->runtime
->jsNaN
);
5875 for (i
= 0, n
= JSXML_LENGTH(parent
); i
< n
; i
++) {
5876 if (XMLARRAY_MEMBER(&parent
->xml_kids
, i
, JSXML
) == xml
)
5880 return js_NewNumberInRootedValue(cx
, i
, vp
);
5883 /* XML and XMLList */
5885 xml_children(JSContext
*cx
, uintN argc
, jsval
*vp
)
5889 name
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
5890 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), name
, vp
);
5893 /* XML and XMLList */
5895 xml_comments_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
5897 JSXML
*list
, *kid
, *vxml
;
5903 list
= xml_list_helper(cx
, xml
, vp
);
5909 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5910 /* 13.5.4.6 Step 2. */
5911 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5912 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5913 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5914 ok
= js_EnterLocalRootScope(cx
);
5917 kidobj
= js_GetXMLObject(cx
, kid
);
5919 ok
= xml_comments_helper(cx
, kidobj
, kid
, &v
);
5924 js_LeaveLocalRootScopeWithResult(cx
, v
);
5927 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
5928 if (JSXML_LENGTH(vxml
) != 0) {
5929 ok
= Append(cx
, list
, vxml
);
5936 /* 13.4.4.9 Step 2. */
5937 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5938 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5939 if (kid
&& kid
->xml_class
== JSXML_CLASS_COMMENT
) {
5940 ok
= Append(cx
, list
, kid
);
5951 xml_comments(JSContext
*cx
, uintN argc
, jsval
*vp
)
5954 return xml_comments_helper(cx
, obj
, xml
, vp
);
5957 /* XML and XMLList */
5959 xml_contains(JSContext
*cx
, uintN argc
, jsval
*vp
)
5963 JSXMLArrayCursor cursor
;
5968 value
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5969 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5971 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
5972 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
5973 kidobj
= js_GetXMLObject(cx
, kid
);
5974 if (!kidobj
|| !xml_equality(cx
, kidobj
, value
, &eq
))
5979 XMLArrayCursorFinish(&cursor
);
5983 if (!xml_equality(cx
, obj
, value
, &eq
))
5986 *vp
= BOOLEAN_TO_JSVAL(eq
);
5990 /* XML and XMLList */
5992 xml_copy(JSContext
*cx
, uintN argc
, jsval
*vp
)
5997 copy
= DeepCopy(cx
, xml
, NULL
, 0);
6000 *vp
= OBJECT_TO_JSVAL(copy
->object
);
6004 /* XML and XMLList */
6006 xml_descendants(JSContext
*cx
, uintN argc
, jsval
*vp
)
6012 name
= (argc
== 0) ? ATOM_KEY(cx
->runtime
->atomState
.starAtom
) : vp
[2];
6013 list
= Descendants(cx
, xml
, name
);
6016 *vp
= OBJECT_TO_JSVAL(list
->object
);
6020 /* XML and XMLList */
6022 xml_elements_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
6023 JSXMLQName
*nameqn
, jsval
*vp
)
6025 JSXML
*list
, *kid
, *vxml
;
6028 JSXMLArrayCursor cursor
;
6032 list
= xml_list_helper(cx
, xml
, vp
);
6036 list
->xml_targetprop
= nameqn
;
6039 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6041 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
6042 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
6043 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6044 ok
= js_EnterLocalRootScope(cx
);
6047 kidobj
= js_GetXMLObject(cx
, kid
);
6049 ok
= xml_elements_helper(cx
, kidobj
, kid
, nameqn
, &v
);
6054 js_LeaveLocalRootScopeWithResult(cx
, v
);
6057 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
6058 if (JSXML_LENGTH(vxml
) != 0) {
6059 ok
= Append(cx
, list
, vxml
);
6065 XMLArrayCursorFinish(&cursor
);
6067 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6068 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6069 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
6070 MatchElemName(nameqn
, kid
)) {
6071 ok
= Append(cx
, list
, kid
);
6082 xml_elements(JSContext
*cx
, uintN argc
, jsval
*vp
)
6090 name
= (argc
== 0) ? ATOM_KEY(cx
->runtime
->atomState
.starAtom
) : vp
[2];
6091 nameqn
= ToXMLName(cx
, name
, &funid
);
6094 vp
[2] = OBJECT_TO_JSVAL(nameqn
->object
);
6097 return xml_list_helper(cx
, xml
, vp
) != NULL
;
6099 return xml_elements_helper(cx
, obj
, xml
, nameqn
, vp
);
6102 /* XML and XMLList */
6104 xml_hasOwnProperty(JSContext
*cx
, uintN argc
, jsval
*vp
)
6110 obj
= JS_THIS_OBJECT(cx
, vp
);
6111 if (!JS_InstanceOf(cx
, obj
, &js_XMLClass
, vp
+ 2))
6114 name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
6115 if (!HasProperty(cx
, obj
, name
, &found
))
6121 return js_HasOwnPropertyHelper(cx
, js_LookupProperty
, argc
, vp
);
6124 /* XML and XMLList */
6126 xml_hasComplexContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6134 switch (xml
->xml_class
) {
6135 case JSXML_CLASS_ATTRIBUTE
:
6136 case JSXML_CLASS_COMMENT
:
6137 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
6138 case JSXML_CLASS_TEXT
:
6141 case JSXML_CLASS_LIST
:
6142 if (xml
->xml_kids
.length
== 0) {
6144 } else if (xml
->xml_kids
.length
== 1) {
6145 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
6147 kidobj
= js_GetXMLObject(cx
, kid
);
6151 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
6158 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6159 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6160 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6170 /* XML and XMLList */
6172 xml_hasSimpleContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6175 *vp
= BOOLEAN_TO_JSVAL(HasSimpleContent(xml
));
6179 typedef struct JSTempRootedNSArray
{
6180 JSTempValueRooter tvr
;
6182 jsval value
; /* extra root for temporaries */
6183 } JSTempRootedNSArray
;
6185 JS_STATIC_DLL_CALLBACK(void)
6186 trace_temp_ns_array(JSTracer
*trc
, JSTempValueRooter
*tvr
)
6188 JSTempRootedNSArray
*tmp
= (JSTempRootedNSArray
*)tvr
;
6190 namespace_trace_vector(trc
,
6191 (JSXMLNamespace
**)tmp
->array
.vector
,
6193 XMLArrayCursorTrace(trc
, tmp
->array
.cursors
);
6194 JS_CALL_VALUE_TRACER(trc
, tmp
->value
, "temp_ns_array_value");
6198 InitTempNSArray(JSContext
*cx
, JSTempRootedNSArray
*tmp
)
6200 XMLArrayInit(cx
, &tmp
->array
, 0);
6201 tmp
->value
= JSVAL_NULL
;
6202 JS_PUSH_TEMP_ROOT_TRACE(cx
, trace_temp_ns_array
, &tmp
->tvr
);
6206 FinishTempNSArray(JSContext
*cx
, JSTempRootedNSArray
*tmp
)
6208 JS_ASSERT(tmp
->tvr
.u
.trace
== trace_temp_ns_array
);
6209 JS_POP_TEMP_ROOT(cx
, &tmp
->tvr
);
6210 XMLArrayFinish(cx
, &tmp
->array
);
6214 * Populate a new JS array with elements of JSTempRootedNSArray.array and
6215 * place the result into rval. rval must point to a rooted location.
6218 TempNSArrayToJSArray(JSContext
*cx
, JSTempRootedNSArray
*tmp
, jsval
*rval
)
6225 arrayobj
= js_NewArrayObject(cx
, 0, NULL
);
6228 *rval
= OBJECT_TO_JSVAL(arrayobj
);
6229 for (i
= 0, n
= tmp
->array
.length
; i
< n
; i
++) {
6230 ns
= XMLARRAY_MEMBER(&tmp
->array
, i
, JSXMLNamespace
);
6233 nsobj
= js_GetXMLNamespaceObject(cx
, ns
);
6236 tmp
->value
= OBJECT_TO_JSVAL(nsobj
);
6237 if (!OBJ_SET_PROPERTY(cx
, arrayobj
, INT_TO_JSID(i
), &tmp
->value
))
6244 FindInScopeNamespaces(JSContext
*cx
, JSXML
*xml
, JSXMLArray
*nsarray
)
6246 uint32 length
, i
, j
, n
;
6247 JSXMLNamespace
*ns
, *ns2
;
6249 length
= nsarray
->length
;
6251 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6253 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
6254 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSXMLNamespace
);
6258 for (j
= 0; j
< length
; j
++) {
6259 ns2
= XMLARRAY_MEMBER(nsarray
, j
, JSXMLNamespace
);
6261 ((ns2
->prefix
&& ns
->prefix
)
6262 ? js_EqualStrings(ns2
->prefix
, ns
->prefix
)
6263 : js_EqualStrings(ns2
->uri
, ns
->uri
))) {
6269 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
6274 } while ((xml
= xml
->parent
) != NULL
);
6275 JS_ASSERT(length
== nsarray
->length
);
6281 xml_inScopeNamespaces(JSContext
*cx
, uintN argc
, jsval
*vp
)
6283 JSTempRootedNSArray namespaces
;
6286 NON_LIST_XML_METHOD_PROLOG
;
6288 InitTempNSArray(cx
, &namespaces
);
6289 ok
= FindInScopeNamespaces(cx
, xml
, &namespaces
.array
) &&
6290 TempNSArrayToJSArray(cx
, &namespaces
, vp
);
6291 FinishTempNSArray(cx
, &namespaces
);
6296 xml_insertChildAfter(JSContext
*cx
, uintN argc
, jsval
*vp
)
6302 NON_LIST_XML_METHOD_PROLOG
;
6303 *vp
= OBJECT_TO_JSVAL(obj
);
6304 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
6308 if (JSVAL_IS_NULL(arg
)) {
6312 if (!VALUE_IS_XML(cx
, arg
))
6314 kid
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(arg
));
6315 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
6316 if (i
== XML_NOT_FOUND
)
6321 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6324 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
6328 xml_insertChildBefore(JSContext
*cx
, uintN argc
, jsval
*vp
)
6334 NON_LIST_XML_METHOD_PROLOG
;
6335 *vp
= OBJECT_TO_JSVAL(obj
);
6336 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
6340 if (JSVAL_IS_NULL(arg
)) {
6342 i
= xml
->xml_kids
.length
;
6344 if (!VALUE_IS_XML(cx
, arg
))
6346 kid
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(arg
));
6347 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
6348 if (i
== XML_NOT_FOUND
)
6352 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6355 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
6358 /* XML and XMLList */
6360 xml_length(JSContext
*cx
, uintN argc
, jsval
*vp
)
6363 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
6366 if (!js_NewNumberInRootedValue(cx
, xml
->xml_kids
.length
, vp
))
6373 xml_localName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6375 NON_LIST_XML_METHOD_PROLOG
;
6376 *vp
= xml
->name
? STRING_TO_JSVAL(xml
->name
->localName
) : JSVAL_NULL
;
6381 xml_name(JSContext
*cx
, uintN argc
, jsval
*vp
)
6385 NON_LIST_XML_METHOD_PROLOG
;
6389 nameobj
= js_GetXMLQNameObject(cx
, xml
->name
);
6392 *vp
= OBJECT_TO_JSVAL(nameobj
);
6398 xml_namespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6401 JSTempRootedNSArray inScopeNSes
;
6407 NON_LIST_XML_METHOD_PROLOG
;
6408 if (argc
== 0 && !JSXML_HAS_NAME(xml
)) {
6416 prefix
= js_ValueToString(cx
, vp
[2]);
6419 vp
[2] = STRING_TO_JSVAL(prefix
); /* local root */
6422 /* After this point the control must flow through label out. */
6423 InitTempNSArray(cx
, &inScopeNSes
);
6424 ok
= FindInScopeNamespaces(cx
, xml
, &inScopeNSes
.array
);
6429 ns
= GetNamespace(cx
, xml
->name
, &inScopeNSes
.array
);
6436 for (i
= 0, length
= inScopeNSes
.array
.length
; i
< length
; i
++) {
6437 ns
= XMLARRAY_MEMBER(&inScopeNSes
.array
, i
, JSXMLNamespace
);
6438 if (ns
&& ns
->prefix
&& js_EqualStrings(ns
->prefix
, prefix
))
6447 nsobj
= js_GetXMLNamespaceObject(cx
, ns
);
6452 *vp
= OBJECT_TO_JSVAL(nsobj
);
6456 FinishTempNSArray(cx
, &inScopeNSes
);
6461 xml_namespaceDeclarations(JSContext
*cx
, uintN argc
, jsval
*vp
)
6464 JSTempRootedNSArray ancestors
, declared
;
6469 NON_LIST_XML_METHOD_PROLOG
;
6470 if (JSXML_HAS_VALUE(xml
))
6473 /* From here, control flow must goto out to finish these arrays. */
6475 InitTempNSArray(cx
, &ancestors
);
6476 InitTempNSArray(cx
, &declared
);
6479 while ((yml
= yml
->parent
) != NULL
) {
6480 JS_ASSERT(yml
->xml_class
== JSXML_CLASS_ELEMENT
);
6481 for (i
= 0, n
= yml
->xml_namespaces
.length
; i
< n
; i
++) {
6482 ns
= XMLARRAY_MEMBER(&yml
->xml_namespaces
, i
, JSXMLNamespace
);
6484 !XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
6485 ok
= XMLARRAY_APPEND(cx
, &ancestors
.array
, ns
);
6492 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
6493 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSXMLNamespace
);
6498 if (!XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
6499 ok
= XMLARRAY_APPEND(cx
, &declared
.array
, ns
);
6505 ok
= TempNSArrayToJSArray(cx
, &declared
, vp
);
6508 /* Finishing must be in reverse order of initialization to follow LIFO. */
6509 FinishTempNSArray(cx
, &declared
);
6510 FinishTempNSArray(cx
, &ancestors
);
6514 static const char js_attribute_str
[] = "attribute";
6515 static const char js_text_str
[] = "text";
6517 /* Exported to jsgc.c #ifdef DEBUG. */
6518 const char *js_xml_class_str
[] = {
6522 "processing-instruction",
6528 xml_nodeKind(JSContext
*cx
, uintN argc
, jsval
*vp
)
6532 NON_LIST_XML_METHOD_PROLOG
;
6533 str
= JS_InternString(cx
, js_xml_class_str
[xml
->xml_class
]);
6536 *vp
= STRING_TO_JSVAL(str
);
6541 NormalizingDelete(JSContext
*cx
, JSXML
*xml
, uint32 index
)
6543 if (xml
->xml_class
== JSXML_CLASS_LIST
)
6544 DeleteListElement(cx
, xml
, index
);
6546 DeleteByIndex(cx
, xml
, index
);
6549 /* XML and XMLList */
6551 xml_normalize_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
)
6558 if (!JSXML_HAS_KIDS(xml
))
6561 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6565 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6566 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6569 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6570 kidobj
= js_GetXMLObject(cx
, kid
);
6571 if (!kidobj
|| !xml_normalize_helper(cx
, kidobj
, kid
))
6573 } else if (kid
->xml_class
== JSXML_CLASS_TEXT
) {
6575 (kid2
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
+ 1, JSXML
)) &&
6576 kid2
->xml_class
== JSXML_CLASS_TEXT
) {
6577 str
= js_ConcatStrings(cx
, kid
->xml_value
, kid2
->xml_value
);
6580 NormalizingDelete(cx
, xml
, i
+ 1);
6581 n
= xml
->xml_kids
.length
;
6582 kid
->xml_value
= str
;
6584 if (IS_EMPTY(kid
->xml_value
)) {
6585 NormalizingDelete(cx
, xml
, i
);
6586 n
= xml
->xml_kids
.length
;
6596 xml_normalize(JSContext
*cx
, uintN argc
, jsval
*vp
)
6599 *vp
= OBJECT_TO_JSVAL(obj
);
6600 return xml_normalize_helper(cx
, obj
, xml
);
6603 /* XML and XMLList */
6605 xml_parent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6607 JSXML
*parent
, *kid
;
6609 JSObject
*parentobj
;
6612 parent
= xml
->parent
;
6613 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6615 n
= xml
->xml_kids
.length
;
6619 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
6622 parent
= kid
->parent
;
6623 for (i
= 1; i
< n
; i
++) {
6624 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6625 if (kid
&& kid
->parent
!= parent
)
6635 parentobj
= js_GetXMLObject(cx
, parent
);
6638 *vp
= OBJECT_TO_JSVAL(parentobj
);
6642 /* XML and XMLList */
6644 xml_processingInstructions_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
6645 JSXMLQName
*nameqn
, jsval
*vp
)
6647 JSXML
*list
, *kid
, *vxml
;
6649 JSXMLArrayCursor cursor
;
6654 list
= xml_list_helper(cx
, xml
, vp
);
6658 list
->xml_targetprop
= nameqn
;
6661 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6662 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6663 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
6664 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
6665 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6666 ok
= js_EnterLocalRootScope(cx
);
6669 kidobj
= js_GetXMLObject(cx
, kid
);
6671 ok
= xml_processingInstructions_helper(cx
, kidobj
, kid
,
6677 js_LeaveLocalRootScopeWithResult(cx
, v
);
6680 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
6681 if (JSXML_LENGTH(vxml
) != 0) {
6682 ok
= Append(cx
, list
, vxml
);
6688 XMLArrayCursorFinish(&cursor
);
6690 /* 13.4.4.28 Step 4. */
6691 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6692 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6693 if (kid
&& kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
&&
6694 (IS_STAR(nameqn
->localName
) ||
6695 js_EqualStrings(nameqn
->localName
, kid
->name
->localName
))) {
6696 ok
= Append(cx
, list
, kid
);
6707 xml_processingInstructions(JSContext
*cx
, uintN argc
, jsval
*vp
)
6715 name
= (argc
== 0) ? ATOM_KEY(cx
->runtime
->atomState
.starAtom
) : vp
[2];
6716 nameqn
= ToXMLName(cx
, name
, &funid
);
6719 vp
[2] = OBJECT_TO_JSVAL(nameqn
->object
);
6722 return xml_list_helper(cx
, xml
, vp
) != NULL
;
6724 return xml_processingInstructions_helper(cx
, obj
, xml
, nameqn
, vp
);
6728 xml_prependChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
6730 NON_LIST_XML_METHOD_PROLOG
;
6731 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6734 *vp
= OBJECT_TO_JSVAL(obj
);
6735 return Insert(cx
, xml
, 0, argc
!= 0 ? vp
[2] : JSVAL_VOID
);
6738 /* XML and XMLList */
6740 xml_propertyIsEnumerable(JSContext
*cx
, uintN argc
, jsval
*vp
)
6746 if (argc
!= 0 && js_IdIsIndex(vp
[2], &index
)) {
6747 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6749 *vp
= BOOLEAN_TO_JSVAL(index
< xml
->xml_kids
.length
);
6752 *vp
= BOOLEAN_TO_JSVAL(index
== 0);
6759 namespace_full_match(const void *a
, const void *b
)
6761 const JSXMLNamespace
*nsa
= (const JSXMLNamespace
*) a
;
6762 const JSXMLNamespace
*nsb
= (const JSXMLNamespace
*) b
;
6764 if (nsa
->prefix
&& nsb
->prefix
&&
6765 !js_EqualStrings(nsa
->prefix
, nsb
->prefix
)) {
6768 return js_EqualStrings(nsa
->uri
, nsb
->uri
);
6772 xml_removeNamespace_helper(JSContext
*cx
, JSXML
*xml
, JSXMLNamespace
*ns
)
6774 JSXMLNamespace
*thisns
, *attrns
;
6778 thisns
= GetNamespace(cx
, xml
->name
, &xml
->xml_namespaces
);
6783 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
6784 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
6787 attrns
= GetNamespace(cx
, attr
->name
, &xml
->xml_namespaces
);
6793 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_namespaces
, ns
, namespace_full_match
);
6794 if (i
!= XML_NOT_FOUND
)
6795 XMLArrayDelete(cx
, &xml
->xml_namespaces
, i
, JS_TRUE
);
6797 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6798 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6799 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6800 if (!xml_removeNamespace_helper(cx
, kid
, ns
))
6808 xml_removeNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6812 NON_LIST_XML_METHOD_PROLOG
;
6813 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6815 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6819 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
6821 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6822 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(*vp
));
6824 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6825 if (!xml_removeNamespace_helper(cx
, xml
, ns
))
6828 *vp
= OBJECT_TO_JSVAL(obj
);
6833 xml_replace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6840 NON_LIST_XML_METHOD_PROLOG
;
6841 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6845 value
= STRING_TO_JSVAL(ATOM_TO_STRING(cx
->runtime
->atomState
.
6846 typeAtoms
[JSTYPE_VOID
]));
6849 vxml
= VALUE_IS_XML(cx
, value
)
6850 ? (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(value
))
6853 if (!JS_ConvertValue(cx
, value
, JSTYPE_STRING
, &vp
[3]))
6857 vxml
= DeepCopy(cx
, vxml
, NULL
, 0);
6860 value
= vp
[3] = OBJECT_TO_JSVAL(vxml
->object
);
6864 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6868 if (argc
== 0 || !js_IdIsIndex(vp
[2], &index
)) {
6870 * Call function QName per spec, not ToXMLName, to avoid attribute
6873 if (!QNameHelper(cx
, NULL
, &js_QNameClass
.base
, argc
== 0 ? -1 : 1,
6877 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6878 nameqn
= (JSXMLQName
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(*vp
));
6880 i
= xml
->xml_kids
.length
;
6881 index
= XML_NOT_FOUND
;
6884 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6885 if (kid
&& MatchElemName(nameqn
, kid
)) {
6886 if (i
!= XML_NOT_FOUND
)
6887 DeleteByIndex(cx
, xml
, i
);
6892 if (index
== XML_NOT_FOUND
)
6896 if (!Replace(cx
, xml
, index
, value
))
6900 *vp
= OBJECT_TO_JSVAL(obj
);
6905 xml_setChildren(JSContext
*cx
, uintN argc
, jsval
*vp
)
6909 if (!StartNonListXMLMethod(cx
, vp
, &obj
))
6912 *vp
= argc
!= 0 ? vp
[2] : JSVAL_VOID
; /* local root */
6913 if (!PutProperty(cx
, obj
, ATOM_KEY(cx
->runtime
->atomState
.starAtom
), vp
))
6916 *vp
= OBJECT_TO_JSVAL(obj
);
6921 xml_setLocalName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6927 NON_LIST_XML_METHOD_PROLOG
;
6928 if (!JSXML_HAS_NAME(xml
))
6932 namestr
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6935 if (!JSVAL_IS_PRIMITIVE(name
) &&
6936 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(name
)) == &js_QNameClass
.base
) {
6937 nameqn
= (JSXMLQName
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(name
));
6938 namestr
= nameqn
->localName
;
6940 if (!JS_ConvertValue(cx
, name
, JSTYPE_STRING
, &vp
[2]))
6943 namestr
= JSVAL_TO_STRING(name
);
6947 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6950 xml
->name
->localName
= namestr
;
6955 xml_setName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6961 JSXMLArray
*nsarray
;
6965 NON_LIST_XML_METHOD_PROLOG
;
6966 if (!JSXML_HAS_NAME(xml
))
6970 name
= STRING_TO_JSVAL(ATOM_TO_STRING(cx
->runtime
->atomState
.
6971 typeAtoms
[JSTYPE_VOID
]));
6974 if (!JSVAL_IS_PRIMITIVE(name
) &&
6975 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(name
)) == &js_QNameClass
.base
&&
6976 !(nameqn
= (JSXMLQName
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(name
)))
6978 name
= vp
[2] = STRING_TO_JSVAL(nameqn
->localName
);
6982 nameobj
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 1, &name
);
6985 nameqn
= (JSXMLQName
*) JS_GetPrivate(cx
, nameobj
);
6987 /* ECMA-357 13.4.4.35 Step 4. */
6988 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
6989 nameqn
->uri
= cx
->runtime
->emptyString
;
6991 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6997 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6998 * in-scope namespaces, either by finding an in-scope namespace with a
6999 * matching uri and setting the new name's prefix to that namespace's
7000 * prefix, or by extending the in-scope namespaces for xml (which are in
7001 * xml->parent if xml is an attribute or a PI).
7003 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
7006 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
7008 nsowner
= xml
->parent
;
7011 if (nameqn
->prefix
) {
7013 * The name being set has a prefix, which originally came from some
7014 * namespace object (which may be the null namespace, where both the
7015 * prefix and uri are the empty string). We must go through a full
7016 * GetNamespace in case that namespace is in-scope in nsowner.
7018 * If we find such an in-scope namespace, we return true right away,
7019 * in this block. Otherwise, we fall through to the final return of
7020 * AddInScopeNamespace(cx, nsowner, ns).
7022 ns
= GetNamespace(cx
, nameqn
, &nsowner
->xml_namespaces
);
7026 /* XXXbe have to test membership to see whether GetNamespace added */
7027 if (XMLARRAY_HAS_MEMBER(&nsowner
->xml_namespaces
, ns
, NULL
))
7031 * At this point, we know nameqn->prefix is null, so nameqn->uri can't
7032 * be the empty string (the null namespace always uses the empty string
7033 * for both prefix and uri).
7035 * This means we must inline GetNamespace and specialize it to match
7036 * uri only, never prefix. If we find a namespace with nameqn's uri
7037 * already in nsowner->xml_namespaces, then all that we need do is set
7038 * nameqn->prefix to that namespace's prefix.
7040 * If no such namespace exists, we can create one without going through
7041 * the constructor, because we know nameqn->uri is non-empty (so prefix
7042 * does not need to be converted from null to empty by QName).
7044 JS_ASSERT(!IS_EMPTY(nameqn
->uri
));
7046 nsarray
= &nsowner
->xml_namespaces
;
7047 for (i
= 0, n
= nsarray
->length
; i
< n
; i
++) {
7048 ns
= XMLARRAY_MEMBER(nsarray
, i
, JSXMLNamespace
);
7049 if (ns
&& js_EqualStrings(ns
->uri
, nameqn
->uri
)) {
7050 nameqn
->prefix
= ns
->prefix
;
7055 ns
= js_NewXMLNamespace(cx
, NULL
, nameqn
->uri
, JS_TRUE
);
7060 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
7067 xml_setNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
7069 JSObject
*nsobj
, *qnobj
;
7074 NON_LIST_XML_METHOD_PROLOG
;
7075 if (!JSXML_HAS_NAME(xml
))
7078 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
7079 if (!xml
|| !js_GetXMLQNameObject(cx
, xml
->name
))
7082 nsobj
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, obj
,
7083 argc
== 0 ? 0 : 1, vp
+ 2);
7086 vp
[0] = OBJECT_TO_JSVAL(nsobj
);
7087 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, nsobj
);
7088 ns
->declared
= JS_TRUE
;
7090 qnargv
[0] = vp
[2] = OBJECT_TO_JSVAL(nsobj
);
7091 qnargv
[1] = OBJECT_TO_JSVAL(xml
->name
->object
);
7092 qnobj
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 2, qnargv
);
7096 xml
->name
= (JSXMLQName
*) JS_GetPrivate(cx
, qnobj
);
7099 * Erratum: the spec fails to update the governing in-scope namespaces.
7100 * See the erratum noted in xml_setName, above.
7102 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
7105 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
7107 nsowner
= xml
->parent
;
7109 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
7115 /* XML and XMLList */
7117 xml_text_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
7119 JSXML
*list
, *kid
, *vxml
;
7125 list
= xml_list_helper(cx
, xml
, vp
);
7129 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7131 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
7132 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
7133 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
7134 ok
= js_EnterLocalRootScope(cx
);
7137 kidobj
= js_GetXMLObject(cx
, kid
);
7139 ok
= xml_text_helper(cx
, kidobj
, kid
, &v
);
7144 js_LeaveLocalRootScopeWithResult(cx
, v
);
7147 vxml
= (JSXML
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(v
));
7148 if (JSXML_LENGTH(vxml
) != 0 && !Append(cx
, list
, vxml
))
7153 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
7154 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
7155 if (kid
&& kid
->xml_class
== JSXML_CLASS_TEXT
) {
7156 if (!Append(cx
, list
, kid
))
7165 xml_text(JSContext
*cx
, uintN argc
, jsval
*vp
)
7168 return xml_text_helper(cx
, obj
, xml
, vp
);
7171 /* XML and XMLList */
7173 xml_toString_helper(JSContext
*cx
, JSXML
*xml
)
7175 JSString
*str
, *kidstr
;
7177 JSXMLArrayCursor cursor
;
7179 if (xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
||
7180 xml
->xml_class
== JSXML_CLASS_TEXT
) {
7181 return xml
->xml_value
;
7184 if (!HasSimpleContent(xml
))
7185 return ToXMLString(cx
, OBJECT_TO_JSVAL(xml
->object
), 0);
7187 str
= cx
->runtime
->emptyString
;
7188 if (!js_EnterLocalRootScope(cx
))
7190 XMLArrayCursorInit(&cursor
, &xml
->xml_kids
);
7191 while ((kid
= (JSXML
*) XMLArrayCursorNext(&cursor
)) != NULL
) {
7192 if (kid
->xml_class
!= JSXML_CLASS_COMMENT
&&
7193 kid
->xml_class
!= JSXML_CLASS_PROCESSING_INSTRUCTION
) {
7194 kidstr
= xml_toString_helper(cx
, kid
);
7199 str
= js_ConcatStrings(cx
, str
, kidstr
);
7204 XMLArrayCursorFinish(&cursor
);
7205 js_LeaveLocalRootScopeWithResult(cx
, STRING_TO_JSVAL(str
));
7210 xml_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
7215 thisv
= JS_THIS(cx
, vp
);
7216 if (JSVAL_IS_NULL(thisv
))
7218 str
= ToXMLString(cx
, thisv
, TO_SOURCE_FLAG
);
7221 *vp
= STRING_TO_JSVAL(str
);
7226 xml_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
7231 str
= xml_toString_helper(cx
, xml
);
7234 *vp
= STRING_TO_JSVAL(str
);
7238 /* XML and XMLList */
7240 xml_toXMLString(JSContext
*cx
, uintN argc
, jsval
*vp
)
7245 thisv
= JS_THIS(cx
, vp
);
7246 if (JSVAL_IS_NULL(thisv
))
7248 str
= ToXMLString(cx
, thisv
, 0);
7251 *vp
= STRING_TO_JSVAL(str
);
7255 /* XML and XMLList */
7257 xml_valueOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
7259 *vp
= JS_THIS(cx
, vp
);
7260 return !JSVAL_IS_NULL(*vp
);
7263 static JSFunctionSpec xml_methods
[] = {
7264 JS_FN("addNamespace", xml_addNamespace
, 1,0),
7265 JS_FN("appendChild", xml_appendChild
, 1,0),
7266 JS_FN(js_attribute_str
, xml_attribute
, 1,0),
7267 JS_FN("attributes", xml_attributes
, 0,0),
7268 JS_FN("child", xml_child
, 1,0),
7269 JS_FN("childIndex", xml_childIndex
, 0,0),
7270 JS_FN("children", xml_children
, 0,0),
7271 JS_FN("comments", xml_comments
, 0,0),
7272 JS_FN("contains", xml_contains
, 1,0),
7273 JS_FN("copy", xml_copy
, 0,0),
7274 JS_FN("descendants", xml_descendants
, 1,0),
7275 JS_FN("elements", xml_elements
, 1,0),
7276 JS_FN("hasOwnProperty", xml_hasOwnProperty
, 1,0),
7277 JS_FN("hasComplexContent", xml_hasComplexContent
, 1,0),
7278 JS_FN("hasSimpleContent", xml_hasSimpleContent
, 1,0),
7279 JS_FN("inScopeNamespaces", xml_inScopeNamespaces
, 0,0),
7280 JS_FN("insertChildAfter", xml_insertChildAfter
, 2,0),
7281 JS_FN("insertChildBefore", xml_insertChildBefore
, 2,0),
7282 JS_FN(js_length_str
, xml_length
, 0,0),
7283 JS_FN(js_localName_str
, xml_localName
, 0,0),
7284 JS_FN(js_name_str
, xml_name
, 0,0),
7285 JS_FN(js_namespace_str
, xml_namespace
, 1,0),
7286 JS_FN("namespaceDeclarations", xml_namespaceDeclarations
, 0,0),
7287 JS_FN("nodeKind", xml_nodeKind
, 0,0),
7288 JS_FN("normalize", xml_normalize
, 0,0),
7289 JS_FN(js_xml_parent_str
, xml_parent
, 0,0),
7290 JS_FN("processingInstructions",xml_processingInstructions
,1,0),
7291 JS_FN("prependChild", xml_prependChild
, 1,0),
7292 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable
, 1,0),
7293 JS_FN("removeNamespace", xml_removeNamespace
, 1,0),
7294 JS_FN("replace", xml_replace
, 2,0),
7295 JS_FN("setChildren", xml_setChildren
, 1,0),
7296 JS_FN("setLocalName", xml_setLocalName
, 1,0),
7297 JS_FN("setName", xml_setName
, 1,0),
7298 JS_FN("setNamespace", xml_setNamespace
, 1,0),
7299 JS_FN(js_text_str
, xml_text
, 0,0),
7300 JS_FN(js_toSource_str
, xml_toSource
, 0,0),
7301 JS_FN(js_toString_str
, xml_toString
, 0,0),
7302 JS_FN(js_toXMLString_str
, xml_toXMLString
, 0,0),
7303 JS_FN(js_valueOf_str
, xml_valueOf
, 0,0),
7308 CopyXMLSettings(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
7314 for (i
= XML_IGNORE_COMMENTS
; i
< XML_PRETTY_INDENT
; i
++) {
7315 name
= xml_static_props
[i
].name
;
7316 if (!JS_GetProperty(cx
, from
, name
, &v
))
7318 if (JSVAL_IS_BOOLEAN(v
) && !JS_SetProperty(cx
, to
, name
, &v
))
7322 name
= xml_static_props
[i
].name
;
7323 if (!JS_GetProperty(cx
, from
, name
, &v
))
7325 if (JSVAL_IS_NUMBER(v
) && !JS_SetProperty(cx
, to
, name
, &v
))
7331 SetDefaultXMLSettings(JSContext
*cx
, JSObject
*obj
)
7336 for (i
= XML_IGNORE_COMMENTS
; i
< XML_PRETTY_INDENT
; i
++) {
7338 if (!JS_SetProperty(cx
, obj
, xml_static_props
[i
].name
, &v
))
7341 v
= INT_TO_JSVAL(2);
7342 return JS_SetProperty(cx
, obj
, xml_static_props
[i
].name
, &v
);
7346 xml_settings(JSContext
*cx
, uintN argc
, jsval
*vp
)
7351 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
7354 *vp
= OBJECT_TO_JSVAL(settings
);
7355 obj
= JS_THIS_OBJECT(cx
, vp
);
7356 return obj
&& CopyXMLSettings(cx
, obj
, settings
);
7360 xml_setSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
7362 JSObject
*obj
, *settings
;
7366 obj
= JS_THIS_OBJECT(cx
, vp
);
7369 v
= (argc
== 0) ? JSVAL_VOID
: vp
[2];
7370 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
7371 cx
->xmlSettingFlags
= 0;
7372 ok
= SetDefaultXMLSettings(cx
, obj
);
7374 if (JSVAL_IS_PRIMITIVE(v
))
7376 settings
= JSVAL_TO_OBJECT(v
);
7377 cx
->xmlSettingFlags
= 0;
7378 ok
= CopyXMLSettings(cx
, settings
, obj
);
7381 cx
->xmlSettingFlags
|= XSF_CACHE_VALID
;
7386 xml_defaultSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
7390 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
7393 *vp
= OBJECT_TO_JSVAL(settings
);
7394 return SetDefaultXMLSettings(cx
, settings
);
7397 static JSFunctionSpec xml_static_methods
[] = {
7398 JS_FN("settings", xml_settings
, 0,0),
7399 JS_FN("setSettings", xml_setSettings
, 1,0),
7400 JS_FN("defaultSettings", xml_defaultSettings
, 0,0),
7405 XML(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
7409 JSObject
*xobj
, *vobj
;
7413 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
7414 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
7416 xobj
= ToXML(cx
, v
);
7419 *rval
= OBJECT_TO_JSVAL(xobj
);
7420 xml
= (JSXML
*) JS_GetPrivate(cx
, xobj
);
7422 if ((cx
->fp
->flags
& JSFRAME_CONSTRUCTING
) && !JSVAL_IS_PRIMITIVE(v
)) {
7423 vobj
= JSVAL_TO_OBJECT(v
);
7424 clasp
= OBJ_GET_CLASS(cx
, vobj
);
7425 if (clasp
== &js_XMLClass
||
7426 (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
)) {
7427 /* No need to lock obj, it's newly constructed and thread local. */
7428 copy
= DeepCopy(cx
, xml
, obj
, 0);
7431 JS_ASSERT(copy
->object
== obj
);
7432 *rval
= OBJECT_TO_JSVAL(obj
);
7440 XMLList(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
7443 JSObject
*vobj
, *listobj
;
7447 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
7448 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
7450 if ((cx
->fp
->flags
& JSFRAME_CONSTRUCTING
) && !JSVAL_IS_PRIMITIVE(v
)) {
7451 vobj
= JSVAL_TO_OBJECT(v
);
7452 if (OBJECT_IS_XML(cx
, vobj
)) {
7453 xml
= (JSXML
*) JS_GetPrivate(cx
, vobj
);
7454 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7455 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
7458 *rval
= OBJECT_TO_JSVAL(listobj
);
7460 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
7461 if (!Append(cx
, list
, xml
))
7468 /* Toggle on XML support since the script has explicitly requested it. */
7469 listobj
= ToXMLList(cx
, v
);
7473 *rval
= OBJECT_TO_JSVAL(listobj
);
7477 #define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar))
7478 #define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLElemVar))
7479 #define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *))
7481 static size_t sizeof_JSXML
[JSXML_CLASS_LIMIT
] = {
7482 JSXML_LIST_SIZE
, /* JSXML_CLASS_LIST */
7483 JSXML_ELEMENT_SIZE
, /* JSXML_CLASS_ELEMENT */
7484 JSXML_LEAF_SIZE
, /* JSXML_CLASS_ATTRIBUTE */
7485 JSXML_LEAF_SIZE
, /* JSXML_CLASS_PROCESSING_INSTRUCTION */
7486 JSXML_LEAF_SIZE
, /* JSXML_CLASS_TEXT */
7487 JSXML_LEAF_SIZE
/* JSXML_CLASS_COMMENT */
7491 JSCList xml_leaks
= JS_INIT_STATIC_CLIST(&xml_leaks
);
7496 js_NewXML(JSContext
*cx
, JSXMLClass xml_class
)
7500 xml
= (JSXML
*) js_NewGCThing(cx
, GCX_XML
, sizeof_JSXML
[xml_class
]);
7505 xml
->domnode
= NULL
;
7508 xml
->xml_class
= xml_class
;
7510 if (JSXML_CLASS_HAS_VALUE(xml_class
)) {
7511 xml
->xml_value
= cx
->runtime
->emptyString
;
7513 XMLArrayInit(cx
, &xml
->xml_kids
, 0);
7514 if (xml_class
== JSXML_CLASS_LIST
) {
7515 xml
->xml_target
= NULL
;
7516 xml
->xml_targetprop
= NULL
;
7518 XMLArrayInit(cx
, &xml
->xml_namespaces
, 0);
7519 XMLArrayInit(cx
, &xml
->xml_attrs
, 0);
7524 JS_APPEND_LINK(&xml
->links
, &xml_leaks
);
7525 xml
->serial
= xml_serial
++;
7527 METER(xml_stats
.xml
);
7528 METER(xml_stats
.livexml
);
7533 js_TraceXML(JSTracer
*trc
, JSXML
*xml
)
7536 JS_CALL_OBJECT_TRACER(trc
, xml
->object
, "object");
7538 JS_CALL_TRACER(trc
, xml
->name
, JSTRACE_QNAME
, "name");
7540 JS_CALL_TRACER(trc
, xml
->parent
, JSTRACE_XML
, "xml_parent");
7542 if (JSXML_HAS_VALUE(xml
)) {
7544 JS_CALL_STRING_TRACER(trc
, xml
->xml_value
, "value");
7548 xml_trace_vector(trc
,
7549 (JSXML
**) xml
->xml_kids
.vector
,
7550 xml
->xml_kids
.length
);
7551 XMLArrayCursorTrace(trc
, xml
->xml_kids
.cursors
);
7552 if (IS_GC_MARKING_TRACER(trc
))
7553 XMLArrayTrim(&xml
->xml_kids
);
7555 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7556 if (xml
->xml_target
)
7557 JS_CALL_TRACER(trc
, xml
->xml_target
, JSTRACE_XML
, "target");
7558 if (xml
->xml_targetprop
) {
7559 JS_CALL_TRACER(trc
, xml
->xml_targetprop
, JSTRACE_QNAME
,
7563 namespace_trace_vector(trc
,
7564 (JSXMLNamespace
**)xml
->xml_namespaces
.vector
,
7565 xml
->xml_namespaces
.length
);
7566 XMLArrayCursorTrace(trc
, xml
->xml_namespaces
.cursors
);
7567 if (IS_GC_MARKING_TRACER(trc
))
7568 XMLArrayTrim(&xml
->xml_namespaces
);
7570 xml_trace_vector(trc
,
7571 (JSXML
**) xml
->xml_attrs
.vector
,
7572 xml
->xml_attrs
.length
);
7573 XMLArrayCursorTrace(trc
, xml
->xml_attrs
.cursors
);
7574 if (IS_GC_MARKING_TRACER(trc
))
7575 XMLArrayTrim(&xml
->xml_attrs
);
7580 js_FinalizeXML(JSContext
*cx
, JSXML
*xml
)
7582 if (JSXML_HAS_KIDS(xml
)) {
7583 XMLArrayFinish(cx
, &xml
->xml_kids
);
7584 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
7585 XMLArrayFinish(cx
, &xml
->xml_namespaces
);
7586 XMLArrayFinish(cx
, &xml
->xml_attrs
);
7591 JS_REMOVE_LINK(&xml
->links
);
7594 UNMETER(xml_stats
.livexml
);
7598 js_ParseNodeToXMLObject(JSContext
*cx
, JSParseContext
*pc
, JSParseNode
*pn
)
7605 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
7607 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval
));
7608 ns
= (JSXMLNamespace
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(nsval
));
7610 if (!XMLArrayInit(cx
, &nsarray
, 1))
7613 XMLARRAY_APPEND(cx
, &nsarray
, ns
);
7614 xml
= ParseNodeToXML(cx
, pc
, pn
, &nsarray
, XSF_PRECOMPILED_ROOT
);
7615 XMLArrayFinish(cx
, &nsarray
);
7623 js_NewXMLObject(JSContext
*cx
, JSXMLClass xml_class
)
7627 JSTempValueRooter tvr
;
7629 xml
= js_NewXML(cx
, xml_class
);
7632 JS_PUSH_TEMP_ROOT_XML(cx
, xml
, &tvr
);
7633 obj
= js_GetXMLObject(cx
, xml
);
7634 JS_POP_TEMP_ROOT(cx
, &tvr
);
7639 NewXMLObject(JSContext
*cx
, JSXML
*xml
)
7643 obj
= js_NewObject(cx
, &js_XMLClass
, NULL
, NULL
, 0);
7644 if (!obj
|| !JS_SetPrivate(cx
, obj
, xml
)) {
7645 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
7648 METER(xml_stats
.xmlobj
);
7649 METER(xml_stats
.livexmlobj
);
7654 js_GetXMLObject(JSContext
*cx
, JSXML
*xml
)
7660 JS_ASSERT(JS_GetPrivate(cx
, obj
) == xml
);
7665 * A JSXML cannot be shared among threads unless it has an object.
7666 * A JSXML cannot be given an object unless:
7667 * (a) it has no parent; or
7668 * (b) its parent has no object (therefore is thread-private); or
7669 * (c) its parent's object is locked.
7671 * Once given an object, a JSXML is immutable.
7673 JS_ASSERT(!xml
->parent
||
7674 !xml
->parent
->object
||
7675 JS_IS_OBJ_LOCKED(cx
, xml
->parent
->object
));
7677 obj
= NewXMLObject(cx
, xml
);
7685 js_InitNamespaceClass(JSContext
*cx
, JSObject
*obj
)
7687 return JS_InitClass(cx
, obj
, NULL
, &js_NamespaceClass
.base
, Namespace
, 2,
7688 namespace_props
, namespace_methods
, NULL
, NULL
);
7692 js_InitQNameClass(JSContext
*cx
, JSObject
*obj
)
7694 return JS_InitClass(cx
, obj
, NULL
, &js_QNameClass
.base
, QName
, 2,
7695 qname_props
, qname_methods
, NULL
, NULL
);
7699 js_InitAttributeNameClass(JSContext
*cx
, JSObject
*obj
)
7701 return JS_InitClass(cx
, obj
, NULL
, &js_AttributeNameClass
, AttributeName
, 2,
7702 qname_props
, qname_methods
, NULL
, NULL
);
7706 js_InitAnyNameClass(JSContext
*cx
, JSObject
*obj
)
7710 if (!js_GetAnyName(cx
, &v
))
7712 return JSVAL_TO_OBJECT(v
);
7716 js_InitXMLClass(JSContext
*cx
, JSObject
*obj
)
7718 JSObject
*proto
, *pobj
;
7722 JSScopeProperty
*sprop
;
7725 /* Define the isXMLName function. */
7726 if (!JS_DefineFunction(cx
, obj
, js_isXMLName_str
, xml_isXMLName
, 1, 0))
7729 /* Define the XML class constructor and prototype. */
7730 proto
= JS_InitClass(cx
, obj
, NULL
, &js_XMLClass
, XML
, 1,
7732 xml_static_props
, xml_static_methods
);
7736 xml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
7737 if (!xml
|| !JS_SetPrivate(cx
, proto
, xml
))
7739 xml
->object
= proto
;
7740 METER(xml_stats
.xmlobj
);
7741 METER(xml_stats
.livexmlobj
);
7744 * Prepare to set default settings on the XML constructor we just made.
7745 * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY,
7746 * which is xml_getProperty, which creates a new XMLList every time! We
7747 * must instead call js_LookupProperty directly.
7749 if (!js_LookupProperty(cx
, proto
,
7750 ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
7755 sprop
= (JSScopeProperty
*) prop
;
7756 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop
, OBJ_SCOPE(pobj
)));
7757 cval
= OBJ_GET_SLOT(cx
, pobj
, sprop
->slot
);
7758 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
7759 JS_ASSERT(VALUE_IS_FUNCTION(cx
, cval
));
7761 /* Set default settings. */
7765 if (!xml_setSettings(cx
, 1, vp
))
7768 /* Define the XMLList function and give it the same prototype as XML. */
7769 fun
= JS_DefineFunction(cx
, obj
, js_XMLList_str
, XMLList
, 1, 0);
7772 if (!js_SetClassPrototype(cx
, FUN_OBJECT(fun
), proto
,
7773 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
7780 js_InitXMLClasses(JSContext
*cx
, JSObject
*obj
)
7782 if (!js_InitNamespaceClass(cx
, obj
))
7784 if (!js_InitQNameClass(cx
, obj
))
7786 if (!js_InitAttributeNameClass(cx
, obj
))
7788 if (!js_InitAnyNameClass(cx
, obj
))
7790 if (!js_InitXMLFilterClass(cx
, obj
))
7792 return js_InitXMLClass(cx
, obj
);
7796 js_GetFunctionNamespace(JSContext
*cx
, jsval
*vp
)
7801 JSString
*prefix
, *uri
;
7803 /* An invalid URI, for internal use only, guaranteed not to collide. */
7804 static const char anti_uri
[] = "@mozilla.org/js/function";
7806 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
7808 obj
= rt
->functionNamespaceObject
;
7811 obj
= rt
->functionNamespaceObject
;
7816 * Note that any race to atomize anti_uri here is resolved by
7817 * the atom table code, such that at most one atom for anti_uri
7818 * is created. We store in rt->atomState.lazy unconditionally,
7819 * since we are guaranteed to overwrite either null or the same
7822 atom
= js_Atomize(cx
, anti_uri
, sizeof anti_uri
- 1, ATOM_PINNED
);
7825 rt
->atomState
.lazy
.functionNamespaceURIAtom
= atom
;
7827 prefix
= ATOM_TO_STRING(rt
->atomState
.typeAtoms
[JSTYPE_FUNCTION
]);
7828 uri
= ATOM_TO_STRING(atom
);
7829 obj
= js_NewXMLNamespaceObject(cx
, prefix
, uri
, JS_FALSE
);
7834 * Avoid entraining any in-scope Object.prototype. The loss of
7835 * Namespace.prototype is not detectable, as there is no way to
7836 * refer to this instance in scripts. When used to qualify method
7837 * names, its prefix and uri references are copied to the QName.
7839 OBJ_CLEAR_PROTO(cx
, obj
);
7840 OBJ_CLEAR_PARENT(cx
, obj
);
7843 if (!rt
->functionNamespaceObject
)
7844 rt
->functionNamespaceObject
= obj
;
7846 obj
= rt
->functionNamespaceObject
;
7850 *vp
= OBJECT_TO_JSVAL(obj
);
7855 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7856 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7857 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a
7858 * lightweight function activation). There's no requirement that fp->varobj
7859 * lie directly on fp->scopeChain, although it should be reachable using the
7860 * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h).
7862 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7863 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7864 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7865 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7866 * the default XML namespace will be set to ("", n.uri). So the uri string
7867 * is really the only usefully stored value of the default namespace.
7870 js_GetDefaultXMLNamespace(JSContext
*cx
, jsval
*vp
)
7873 JSObject
*nsobj
, *obj
, *tmp
;
7877 nsobj
= fp
->xmlNamespace
;
7879 *vp
= OBJECT_TO_JSVAL(nsobj
);
7884 for (tmp
= fp
->scopeChain
; tmp
; tmp
= OBJ_GET_PARENT(cx
, obj
)) {
7886 if (!OBJ_GET_PROPERTY(cx
, obj
, JS_DEFAULT_XML_NAMESPACE_ID
, &v
))
7888 if (!JSVAL_IS_PRIMITIVE(v
)) {
7889 fp
->xmlNamespace
= JSVAL_TO_OBJECT(v
);
7895 nsobj
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, obj
, 0, NULL
);
7898 v
= OBJECT_TO_JSVAL(nsobj
);
7900 !OBJ_DEFINE_PROPERTY(cx
, obj
, JS_DEFAULT_XML_NAMESPACE_ID
, v
,
7901 JS_PropertyStub
, JS_PropertyStub
,
7902 JSPROP_PERMANENT
, NULL
)) {
7905 fp
->xmlNamespace
= nsobj
;
7911 js_SetDefaultXMLNamespace(JSContext
*cx
, jsval v
)
7914 JSObject
*nsobj
, *varobj
;
7917 argv
[0] = STRING_TO_JSVAL(cx
->runtime
->emptyString
);
7919 nsobj
= js_ConstructObject(cx
, &js_NamespaceClass
.base
, NULL
, NULL
,
7923 v
= OBJECT_TO_JSVAL(nsobj
);
7926 varobj
= fp
->varobj
;
7928 if (!OBJ_DEFINE_PROPERTY(cx
, varobj
, JS_DEFAULT_XML_NAMESPACE_ID
, v
,
7929 JS_PropertyStub
, JS_PropertyStub
,
7930 JSPROP_PERMANENT
, NULL
)) {
7934 JS_ASSERT(fp
->fun
&& !JSFUN_HEAVYWEIGHT_TEST(fp
->fun
->flags
));
7936 fp
->xmlNamespace
= JSVAL_TO_OBJECT(v
);
7941 js_ToAttributeName(JSContext
*cx
, jsval
*vp
)
7945 qn
= ToAttributeName(cx
, *vp
);
7948 *vp
= OBJECT_TO_JSVAL(qn
->object
);
7953 js_EscapeAttributeValue(JSContext
*cx
, JSString
*str
, JSBool quote
)
7955 return EscapeAttributeValue(cx
, NULL
, str
, quote
);
7959 js_AddAttributePart(JSContext
*cx
, JSBool isName
, JSString
*str
, JSString
*str2
)
7961 size_t len
, len2
, newlen
;
7962 jschar
*chars
, *chars2
;
7964 JSSTRING_CHARS_AND_LENGTH(str
, chars
, len
);
7965 if (!JSSTRING_IS_MUTABLE(str
)) {
7966 str
= js_NewStringCopyN(cx
, chars
, len
);
7969 chars
= JSFLATSTR_CHARS(str
);
7972 * Reallocating str (because we know it has no other references)
7973 * requires purging any deflated string cached for it.
7975 js_PurgeDeflatedStringCache(cx
->runtime
, str
);
7978 JSSTRING_CHARS_AND_LENGTH(str2
, chars2
, len2
);
7979 newlen
= (isName
) ? len
+ 1 + len2
: len
+ 2 + len2
+ 1;
7980 chars
= (jschar
*) JS_realloc(cx
, chars
, (newlen
+1) * sizeof(jschar
));
7984 JSFLATSTR_INIT(str
, chars
, newlen
);
7988 js_strncpy(chars
, chars2
, len2
);
7993 js_strncpy(chars
, chars2
, len2
);
8002 js_EscapeElementValue(JSContext
*cx
, JSString
*str
)
8004 return EscapeElementValue(cx
, NULL
, str
);
8008 js_ValueToXMLString(JSContext
*cx
, jsval v
)
8010 return ToXMLString(cx
, v
, 0);
8014 anyname_toString(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
8017 *rval
= ATOM_KEY(cx
->runtime
->atomState
.starAtom
);
8022 js_GetAnyName(JSContext
*cx
, jsval
*vp
)
8029 /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */
8031 obj
= rt
->anynameObject
;
8034 obj
= rt
->anynameObject
;
8039 * Protect multiple newborns created below, in the do-while(0)
8040 * loop used to ensure that we leave this local root scope.
8042 ok
= js_EnterLocalRootScope(cx
);
8047 qn
= js_NewXMLQName(cx
, rt
->emptyString
, rt
->emptyString
,
8048 ATOM_TO_STRING(rt
->atomState
.starAtom
));
8054 obj
= js_NewObjectWithGivenProto(cx
, &js_AnyNameClass
, NULL
,
8056 if (!obj
|| !JS_SetPrivate(cx
, obj
, qn
)) {
8057 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
8062 METER(xml_stats
.qnameobj
);
8063 METER(xml_stats
.liveqnameobj
);
8066 * Avoid entraining any Object.prototype found via cx's scope
8067 * chain or global object. This loses the default toString,
8068 * but no big deal: we want to customize toString anyway for
8069 * clearer diagnostics.
8071 if (!JS_DefineFunction(cx
, obj
, js_toString_str
,
8072 anyname_toString
, 0, 0)) {
8076 JS_ASSERT(!OBJ_GET_PROTO(cx
, obj
));
8077 JS_ASSERT(!OBJ_GET_PARENT(cx
, obj
));
8080 js_LeaveLocalRootScopeWithResult(cx
, OBJECT_TO_JSVAL(obj
));
8085 if (!rt
->anynameObject
)
8086 rt
->anynameObject
= obj
;
8088 obj
= rt
->anynameObject
;
8092 *vp
= OBJECT_TO_JSVAL(obj
);
8097 js_FindXMLProperty(JSContext
*cx
, jsval nameval
, JSObject
**objp
, jsid
*idp
)
8103 JSObject
*obj
, *target
, *proto
, *pobj
;
8107 const char *printable
;
8109 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nameval
));
8110 nameobj
= JSVAL_TO_OBJECT(nameval
);
8111 if (OBJ_GET_CLASS(cx
, nameobj
) == &js_AnyNameClass
) {
8112 v
= STRING_TO_JSVAL(ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
));
8113 nameobj
= js_ConstructObject(cx
, &js_QNameClass
.base
, NULL
, NULL
, 1,
8118 JS_ASSERT(OBJ_GET_CLASS(cx
, nameobj
) == &js_AttributeNameClass
||
8119 OBJ_GET_CLASS(cx
, nameobj
) == &js_QNameClass
.base
);
8122 qn
= (JSXMLQName
*) JS_GetPrivate(cx
, nameobj
);
8123 if (!IsFunctionQName(cx
, qn
, &funid
))
8126 obj
= cx
->fp
->scopeChain
;
8128 /* Skip any With object that can wrap XML. */
8130 while (OBJ_GET_CLASS(cx
, target
) == &js_WithClass
) {
8131 proto
= OBJ_GET_PROTO(cx
, target
);
8137 if (OBJECT_IS_XML(cx
, target
)) {
8139 xml
= (JSXML
*) JS_GetPrivate(cx
, target
);
8140 found
= HasNamedProperty(xml
, qn
);
8142 if (!HasFunctionProperty(cx
, target
, funid
, &found
))
8146 *idp
= OBJECT_TO_JSID(nameobj
);
8150 } else if (funid
!= 0) {
8151 if (!OBJ_LOOKUP_PROPERTY(cx
, target
, funid
, &pobj
, &prop
))
8154 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
8160 } while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
);
8162 printable
= js_ValueToPrintableString(cx
, OBJECT_TO_JSVAL(nameobj
));
8164 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
8165 js_GetErrorMessage
, NULL
,
8166 JSMSG_UNDEFINED_XML_NAME
, printable
);
8172 js_GetXMLFunction(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
8176 JSTempValueRooter tvr
;
8179 JS_ASSERT(OBJECT_IS_XML(cx
, obj
));
8181 /* After this point, control must flow through label out: to exit. */
8182 JS_PUSH_TEMP_ROOT_OBJECT(cx
, NULL
, &tvr
);
8185 * See comments before xml_lookupProperty about the need for the proto
8190 ok
= js_GetProperty(cx
, target
, id
, vp
);
8193 if (VALUE_IS_FUNCTION(cx
, *vp
)) {
8197 target
= OBJ_GET_PROTO(cx
, target
);
8200 tvr
.u
.object
= target
;
8203 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8204 if (HasSimpleContent(xml
)) {
8205 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
8206 ok
= js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(JSProto_String
),
8210 JS_ASSERT(tvr
.u
.object
);
8211 ok
= OBJ_GET_PROPERTY(cx
, tvr
.u
.object
, id
, vp
);
8215 JS_POP_TEMP_ROOT(cx
, &tvr
);
8220 GetPrivate(JSContext
*cx
, JSObject
*obj
, const char *method
)
8224 xml
= (JSXML
*) JS_GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
8226 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
8227 JSMSG_INCOMPATIBLE_METHOD
,
8228 js_XML_str
, method
, OBJ_GET_CLASS(cx
, obj
)->name
);
8234 js_GetXMLDescendants(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
8238 xml
= GetPrivate(cx
, obj
, "descendants internal method");
8242 list
= Descendants(cx
, xml
, id
);
8245 *vp
= OBJECT_TO_JSVAL(list
->object
);
8250 js_DeleteXMLListElements(JSContext
*cx
, JSObject
*listobj
)
8255 list
= (JSXML
*) JS_GetPrivate(cx
, listobj
);
8256 for (n
= list
->xml_kids
.length
; n
!= 0; --n
)
8257 DeleteListElement(cx
, list
, 0);
8262 typedef struct JSXMLFilter
{
8266 JSXMLArrayCursor cursor
;
8271 xmlfilter_trace(JSTracer
*trc
, JSObject
*obj
)
8273 JSXMLFilter
*filter
;
8275 filter
= (JSXMLFilter
*) JS_GetPrivate(trc
->context
, obj
);
8279 JS_ASSERT(filter
->list
);
8280 JS_CALL_TRACER(trc
, filter
->list
, JSTRACE_XML
, "list");
8282 JS_CALL_TRACER(trc
, filter
->result
, JSTRACE_XML
, "result");
8284 JS_CALL_TRACER(trc
, filter
->kid
, JSTRACE_XML
, "kid");
8287 * We do not need to trace the cursor as that would be done when
8288 * tracing the filter->list.
8293 xmlfilter_finalize(JSContext
*cx
, JSObject
*obj
)
8295 JSXMLFilter
*filter
;
8297 filter
= (JSXMLFilter
*) JS_GetPrivate(cx
, obj
);
8301 XMLArrayCursorFinish(&filter
->cursor
);
8302 JS_free(cx
, filter
);
8305 JSClass js_XMLFilterClass
= {
8307 JSCLASS_HAS_PRIVATE
|
8308 JSCLASS_IS_ANONYMOUS
|
8309 JSCLASS_MARK_IS_TRACE
|
8310 JSCLASS_HAS_CACHED_PROTO(JSProto_XMLFilter
),
8311 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
8312 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, xmlfilter_finalize
,
8313 NULL
, NULL
, NULL
, NULL
,
8314 NULL
, NULL
, JS_CLASS_TRACE(xmlfilter_trace
), NULL
8318 js_InitXMLFilterClass(JSContext
*cx
, JSObject
*obj
)
8322 proto
= JS_InitClass(cx
, obj
, NULL
, &js_XMLFilterClass
, NULL
, 0, NULL
,
8327 OBJ_CLEAR_PROTO(cx
, proto
);
8332 js_StepXMLListFilter(JSContext
*cx
, JSBool initialized
)
8335 JSObject
*obj
, *filterobj
, *resobj
, *kidobj
;
8337 JSXMLFilter
*filter
;
8339 sp
= cx
->fp
->regs
->sp
;
8342 * We haven't iterated yet, so initialize the filter based on the
8343 * value stored in sp[-2].
8345 if (!VALUE_IS_XML(cx
, sp
[-2])) {
8346 js_ReportValueError(cx
, JSMSG_NON_XML_FILTER
, -2, sp
[-2], NULL
);
8349 obj
= JSVAL_TO_OBJECT(sp
[-2]);
8350 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8352 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
8355 obj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
8360 * Root just-created obj. sp[-2] cannot be used yet for rooting
8361 * as it may be the only root holding xml.
8363 sp
[-1] = OBJECT_TO_JSVAL(obj
);
8364 list
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8365 if (!Append(cx
, list
, xml
))
8369 filterobj
= js_NewObject(cx
, &js_XMLFilterClass
, NULL
, NULL
, 0);
8373 filter
= (JSXMLFilter
*) JS_malloc(cx
, sizeof *filter
);
8378 * Init all filter fields before JS_SetPrivate exposes it to
8379 * xmlfilter_trace or xmlfilter_finalize.
8381 filter
->list
= list
;
8382 filter
->result
= NULL
;
8384 XMLArrayCursorInit(&filter
->cursor
, &list
->xml_kids
);
8385 JS_SetPrivate(cx
, filterobj
, filter
);
8387 /* Store filterobj to use in the later iterations. */
8388 sp
[-2] = OBJECT_TO_JSVAL(filterobj
);
8390 resobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
8394 /* This also roots resobj. */
8395 filter
->result
= (JSXML
*) JS_GetPrivate(cx
, resobj
);
8397 /* We have iterated at least once. */
8398 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp
[-2]));
8399 JS_ASSERT(OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(sp
[-2])) ==
8400 &js_XMLFilterClass
);
8401 filter
= (JSXMLFilter
*) JS_GetPrivate(cx
, JSVAL_TO_OBJECT(sp
[-2]));
8402 JS_ASSERT(filter
->kid
);
8404 /* Check if the filter expression wants to append the element. */
8405 if (js_ValueToBoolean(sp
[-1]) &&
8406 !Append(cx
, filter
->result
, filter
->kid
)) {
8411 /* Do the iteration. */
8412 filter
->kid
= (JSXML
*) XMLArrayCursorNext(&filter
->cursor
);
8415 * Do not defer finishing the cursor until the next GC cycle to avoid
8416 * accumulation of dead cursors associated with filter->list.
8418 XMLArrayCursorFinish(&filter
->cursor
);
8419 JS_ASSERT(filter
->result
->object
);
8420 sp
[-2] = OBJECT_TO_JSVAL(filter
->result
->object
);
8423 kidobj
= js_GetXMLObject(cx
, filter
->kid
);
8428 /* Null as kidobj at sp[-1] signals filter termination. */
8429 sp
[-1] = OBJECT_TO_JSVAL(kidobj
);
8434 js_ValueToXMLObject(JSContext
*cx
, jsval v
)
8436 return ToXML(cx
, v
);
8440 js_ValueToXMLListObject(JSContext
*cx
, jsval v
)
8442 return ToXMLList(cx
, v
);
8446 js_CloneXMLObject(JSContext
*cx
, JSObject
*obj
)
8451 if (!GetXMLSettingFlags(cx
, &flags
))
8453 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8454 if (flags
& (XSF_IGNORE_COMMENTS
|
8455 XSF_IGNORE_PROCESSING_INSTRUCTIONS
|
8456 XSF_IGNORE_WHITESPACE
)) {
8457 xml
= DeepCopy(cx
, xml
, NULL
, flags
);
8462 return NewXMLObject(cx
, xml
);
8466 js_NewXMLSpecialObject(JSContext
*cx
, JSXMLClass xml_class
, JSString
*name
,
8474 if (!GetXMLSettingFlags(cx
, &flags
))
8477 if ((xml_class
== JSXML_CLASS_COMMENT
&&
8478 (flags
& XSF_IGNORE_COMMENTS
)) ||
8479 (xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
&&
8480 (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
))) {
8481 return js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
8484 obj
= js_NewXMLObject(cx
, xml_class
);
8487 xml
= (JSXML
*) JS_GetPrivate(cx
, obj
);
8489 qn
= js_NewXMLQName(cx
, cx
->runtime
->emptyString
, NULL
, name
);
8494 xml
->xml_value
= value
;
8499 js_MakeXMLCDATAString(JSContext
*cx
, JSString
*str
)
8501 return MakeXMLCDATAString(cx
, NULL
, str
);
8505 js_MakeXMLCommentString(JSContext
*cx
, JSString
*str
)
8507 return MakeXMLCommentString(cx
, NULL
, str
);
8511 js_MakeXMLPIString(JSContext
*cx
, JSString
*name
, JSString
*str
)
8513 return MakeXMLPIString(cx
, NULL
, name
, str
);
8516 #endif /* JS_HAS_XML_SUPPORT */