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 ***** */
40 #include "jsversion.h"
42 #if JS_HAS_XML_SUPPORT
70 #include "jsstaticcheck.h"
73 #include "jsatominlines.h"
74 #include "jscntxtinlines.h"
75 #include "jsinterpinlines.h"
76 #include "jsobjinlines.h"
77 #include "jsstrinlines.h"
80 #include <string.h> /* for #ifdef DEBUG memset calls */
84 using namespace js::gc
;
88 * - in the js shell, you must use the -x command line option, or call
89 * options('xml') before compiling anything that uses XML literals
93 * - Fuse objects and their JSXML* private data into single GC-things
94 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
95 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
99 js_EnterLocalRootScope(JSContext
*cx
)
105 js_LeaveLocalRootScope(JSContext
*cx
)
110 js_LeaveLocalRootScopeWithResult(JSContext
*cx
, jsval rval
)
115 js_LeaveLocalRootScopeWithResult(JSContext
*cx
, Value rval
)
120 js_LeaveLocalRootScopeWithResult(JSContext
*cx
, void *rval
)
127 jsrefcount xmlnamespace
;
132 #define METER(x) JS_ATOMIC_INCREMENT(&(x))
133 #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
135 #define METER(x) /* nothing */
136 #define UNMETER(x) /* nothing */
140 * Random utilities and global functions.
142 const char js_AttributeName_str
[] = "AttributeName";
143 const char js_isXMLName_str
[] = "isXMLName";
144 const char js_XMLList_str
[] = "XMLList";
145 const char js_localName_str
[] = "localName";
146 const char js_xml_parent_str
[] = "parent";
147 const char js_prefix_str
[] = "prefix";
148 const char js_toXMLString_str
[] = "toXMLString";
149 const char js_uri_str
[] = "uri";
151 const char js_amp_entity_str
[] = "&";
152 const char js_gt_entity_str
[] = ">";
153 const char js_lt_entity_str
[] = "<";
154 const char js_quot_entity_str
[] = """;
155 const char js_leftcurly_entity_str
[] = "{";
157 #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*')
160 GetXMLFunction(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
);
163 IsDeclared(const JSObject
*obj
)
167 JS_ASSERT(obj
->getClass() == &js_NamespaceClass
);
168 v
= obj
->getNamespaceDeclared();
169 JS_ASSERT(JSVAL_IS_VOID(v
) || v
== JSVAL_TRUE
);
170 return v
== JSVAL_TRUE
;
174 xml_isXMLName(JSContext
*cx
, uintN argc
, jsval
*vp
)
176 *vp
= BOOLEAN_TO_JSVAL(js_IsXMLName(cx
, argc
? vp
[2] : JSVAL_VOID
));
181 * This wrapper is needed because NewBuiltinClassInstance doesn't
182 * call the constructor, and we need a place to set the
185 static inline JSObject
*
186 NewBuiltinClassInstanceXML(JSContext
*cx
, Class
*clasp
)
188 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
190 obj
->syncSpecialEquality();
194 #define DEFINE_GETTER(name,code) \
196 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
203 * Namespace class and library functions.
205 DEFINE_GETTER(NamePrefix_getter
,
206 if (obj
->getClass() == &js_NamespaceClass
) *vp
= obj
->getNamePrefixVal())
207 DEFINE_GETTER(NameURI_getter
,
208 if (obj
->getClass() == &js_NamespaceClass
) *vp
= obj
->getNameURIVal())
211 namespace_equality(JSContext
*cx
, JSObject
*obj
, const Value
*v
, JSBool
*bp
)
215 JS_ASSERT(v
->isObjectOrNull());
216 obj2
= v
->toObjectOrNull();
217 *bp
= (!obj2
|| obj2
->getClass() != &js_NamespaceClass
)
219 : EqualStrings(obj
->getNameURI(), obj2
->getNameURI());
223 JS_FRIEND_DATA(Class
) js_NamespaceClass
= {
225 JSCLASS_CONSTRUCT_PROTOTYPE
|
226 JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS
) |
227 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace
),
228 PropertyStub
, /* addProperty */
229 PropertyStub
, /* delProperty */
230 PropertyStub
, /* getProperty */
231 StrictPropertyStub
, /* setProperty */
236 NULL
, /* reserved0 */
237 NULL
, /* checkAccess */
239 NULL
, /* construct */
240 NULL
, /* xdrObject */
241 NULL
, /* hasInstance */
245 NULL
, /* outerObject */
246 NULL
, /* innerObject */
247 NULL
, /* iteratorObject */
248 NULL
, /* wrappedObject */
252 #define NAMESPACE_ATTRS \
253 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
255 static JSPropertySpec namespace_props
[] = {
256 {js_prefix_str
, 0, NAMESPACE_ATTRS
, NamePrefix_getter
, 0},
257 {js_uri_str
, 0, NAMESPACE_ATTRS
, NameURI_getter
, 0},
262 namespace_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
264 JSObject
*obj
= ToObject(cx
, &vp
[1]);
267 if (!JS_InstanceOf(cx
, obj
, Jsvalify(&js_NamespaceClass
), Jsvalify(vp
+ 2)))
269 *vp
= Valueify(obj
->getNameURIVal());
273 static JSFunctionSpec namespace_methods
[] = {
274 JS_FN(js_toString_str
, namespace_toString
, 0,0),
279 NewXMLNamespace(JSContext
*cx
, JSLinearString
*prefix
, JSLinearString
*uri
, JSBool declared
)
283 obj
= NewBuiltinClassInstanceXML(cx
, &js_NamespaceClass
);
286 JS_ASSERT(JSVAL_IS_VOID(obj
->getNamePrefixVal()));
287 JS_ASSERT(JSVAL_IS_VOID(obj
->getNameURIVal()));
288 JS_ASSERT(JSVAL_IS_VOID(obj
->getNamespaceDeclared()));
290 obj
->setNamePrefix(prefix
);
292 obj
->setNameURI(uri
);
294 obj
->setNamespaceDeclared(JSVAL_TRUE
);
295 METER(xml_stats
.xmlnamespace
);
300 * QName class and library functions.
302 DEFINE_GETTER(QNameNameURI_getter
,
303 if (obj
->getClass() == &js_QNameClass
)
304 *vp
= JSVAL_IS_VOID(obj
->getNameURIVal()) ? JSVAL_NULL
: obj
->getNameURIVal())
305 DEFINE_GETTER(QNameLocalName_getter
,
306 if (obj
->getClass() == &js_QNameClass
)
307 *vp
= obj
->getQNameLocalNameVal())
310 qname_identity(JSObject
*qna
, JSObject
*qnb
)
312 JSLinearString
*uri1
= qna
->getNameURI();
313 JSLinearString
*uri2
= qnb
->getNameURI();
317 if (uri1
&& !EqualStrings(uri1
, uri2
))
319 return EqualStrings(qna
->getQNameLocalName(), qnb
->getQNameLocalName());
323 qname_equality(JSContext
*cx
, JSObject
*qn
, const Value
*v
, JSBool
*bp
)
327 obj2
= v
->toObjectOrNull();
328 *bp
= (!obj2
|| obj2
->getClass() != &js_QNameClass
)
330 : qname_identity(qn
, obj2
);
334 JS_FRIEND_DATA(Class
) js_QNameClass
= {
336 JSCLASS_CONSTRUCT_PROTOTYPE
|
337 JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS
) |
338 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_QName
),
339 PropertyStub
, /* addProperty */
340 PropertyStub
, /* delProperty */
341 PropertyStub
, /* getProperty */
342 StrictPropertyStub
, /* setProperty */
347 NULL
, /* reserved0 */
348 NULL
, /* checkAccess */
350 NULL
, /* construct */
351 NULL
, /* xdrObject */
352 NULL
, /* hasInstance */
356 NULL
, /* outerObject */
357 NULL
, /* innerObject */
358 NULL
, /* iteratorObject */
359 NULL
, /* wrappedObject */
364 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
365 * are like QName, except that they have no property getters. They share the
366 * qname_toString method, and therefore are exposed as constructable objects
367 * in this implementation.
369 JS_FRIEND_DATA(Class
) js_AttributeNameClass
= {
370 js_AttributeName_str
,
371 JSCLASS_CONSTRUCT_PROTOTYPE
|
372 JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS
) |
373 JSCLASS_MARK_IS_TRACE
| JSCLASS_IS_ANONYMOUS
,
374 PropertyStub
, /* addProperty */
375 PropertyStub
, /* delProperty */
376 PropertyStub
, /* getProperty */
377 StrictPropertyStub
, /* setProperty */
384 JS_FRIEND_DATA(Class
) js_AnyNameClass
= {
386 JSCLASS_CONSTRUCT_PROTOTYPE
|
387 JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS
) |
388 JSCLASS_MARK_IS_TRACE
| JSCLASS_IS_ANONYMOUS
,
389 PropertyStub
, /* addProperty */
390 PropertyStub
, /* delProperty */
391 PropertyStub
, /* getProperty */
392 StrictPropertyStub
, /* setProperty */
399 #define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
401 static JSPropertySpec qname_props
[] = {
402 {js_uri_str
, 0, QNAME_ATTRS
, QNameNameURI_getter
, 0},
403 {js_localName_str
, 0, QNAME_ATTRS
, QNameLocalName_getter
, 0},
408 ConvertQNameToString(JSContext
*cx
, JSObject
*obj
)
410 JS_ASSERT(obj
->isQName());
411 JSString
*uri
= obj
->getNameURI();
414 /* No uri means wildcard qualifier. */
415 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.starQualifierAtom
);
416 } else if (uri
->empty()) {
417 /* Empty string for uri means localName is in no namespace. */
418 str
= cx
->runtime
->emptyString
;
420 JSString
*qualstr
= ATOM_TO_STRING(cx
->runtime
->atomState
.qualifierAtom
);
421 str
= js_ConcatStrings(cx
, uri
, qualstr
);
425 str
= js_ConcatStrings(cx
, str
, obj
->getQNameLocalName());
429 if (obj
->getClass() == &js_AttributeNameClass
) {
430 size_t length
= str
->length();
431 jschar
*chars
= (jschar
*) cx
->malloc((length
+ 2) * sizeof(jschar
));
435 const jschar
*strChars
= str
->getChars(cx
);
440 js_strncpy(chars
+ 1, strChars
, length
);
442 str
= js_NewString(cx
, chars
, length
);
452 qname_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
454 JSObject
*obj
= ToObject(cx
, &vp
[1]);
458 if (!InstanceOf(cx
, obj
, &js_QNameClass
, vp
+ 2))
461 JSString
*str
= ConvertQNameToString(cx
, obj
);
469 static JSFunctionSpec qname_methods
[] = {
470 JS_FN(js_toString_str
, qname_toString
, 0,0),
476 InitXMLQName(JSObject
*obj
, JSLinearString
*uri
, JSLinearString
*prefix
,
477 JSLinearString
*localName
)
479 JS_ASSERT(obj
->isQName());
480 JS_ASSERT(JSVAL_IS_VOID(obj
->getNamePrefixVal()));
481 JS_ASSERT(JSVAL_IS_VOID(obj
->getNameURIVal()));
482 JS_ASSERT(JSVAL_IS_VOID(obj
->getQNameLocalNameVal()));
484 obj
->setNameURI(uri
);
486 obj
->setNamePrefix(prefix
);
488 obj
->setQNameLocalName(localName
);
492 NewXMLQName(JSContext
*cx
, JSLinearString
*uri
, JSLinearString
*prefix
,
493 JSLinearString
*localName
)
495 JSObject
*obj
= NewBuiltinClassInstanceXML(cx
, &js_QNameClass
);
498 InitXMLQName(obj
, uri
, prefix
, localName
);
499 METER(xml_stats
.qname
);
504 NewXMLAttributeName(JSContext
*cx
, JSLinearString
*uri
, JSLinearString
*prefix
,
505 JSLinearString
*localName
)
508 * AttributeName is an internal anonymous class which instances are not
509 * exposed to scripts.
511 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_AttributeNameClass
, NULL
, NULL
);
514 JS_ASSERT(obj
->isQName());
515 InitXMLQName(obj
, uri
, prefix
, localName
);
516 METER(xml_stats
.qname
);
521 js_ConstructXMLQNameObject(JSContext
*cx
, const Value
&nsval
, const Value
&lnval
)
527 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
528 * production, step 2.
530 if (nsval
.isObject() &&
531 nsval
.toObject().getClass() == &js_AnyNameClass
) {
537 return js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 2, argv
);
541 IsXMLName(const jschar
*cp
, size_t n
)
547 if (n
!= 0 && JS_ISXMLNSSTART(*cp
)) {
559 js_IsXMLName(JSContext
*cx
, jsval v
)
561 JSLinearString
*name
= NULL
;
562 JSErrorReporter older
;
565 * Inline specialization of the QName constructor called with v passed as
566 * the only argument, to compute the localName for the constructed qname,
567 * without actually allocating the object or computing its uri and prefix.
568 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
570 if (!JSVAL_IS_PRIMITIVE(v
) &&
571 JSVAL_TO_OBJECT(v
)->isQName()) {
572 name
= JSVAL_TO_OBJECT(v
)->getQNameLocalName();
574 older
= JS_SetErrorReporter(cx
, NULL
);
575 JSString
*str
= js_ValueToString(cx
, Valueify(v
));
577 name
= str
->ensureLinear(cx
);
578 JS_SetErrorReporter(cx
, older
);
580 JS_ClearPendingException(cx
);
585 return IsXMLName(name
->chars(), name
->length());
589 * When argc is -1, it indicates argv is empty but the code should behave as
590 * if argc is 1 and argv[0] is JSVAL_VOID.
593 NamespaceHelper(JSContext
*cx
, JSObject
*obj
, intN argc
, jsval
*argv
,
596 jsval urival
, prefixval
;
598 JSBool isNamespace
, isQName
;
600 JSLinearString
*empty
, *prefix
, *uri
;
602 isNamespace
= isQName
= JS_FALSE
;
603 #ifdef __GNUC__ /* suppress bogus gcc warnings */
609 urival
= argv
[argc
> 1];
610 if (!JSVAL_IS_PRIMITIVE(urival
)) {
611 uriobj
= JSVAL_TO_OBJECT(urival
);
612 clasp
= uriobj
->getClass();
613 isNamespace
= (clasp
== &js_NamespaceClass
);
614 isQName
= (clasp
== &js_QNameClass
);
619 /* Namespace called as function. */
620 if (argc
== 1 && isNamespace
) {
621 /* Namespace called with one Namespace argument is identity. */
626 obj
= NewBuiltinClassInstanceXML(cx
, &js_NamespaceClass
);
630 *rval
= OBJECT_TO_JSVAL(obj
);
631 METER(xml_stats
.xmlnamespace
);
633 empty
= cx
->runtime
->emptyString
;
634 obj
->setNamePrefix(empty
);
635 obj
->setNameURI(empty
);
637 if (argc
== 1 || argc
== -1) {
639 obj
->setNameURI(uriobj
->getNameURI());
640 obj
->setNamePrefix(uriobj
->getNamePrefix());
641 } else if (isQName
&& (uri
= uriobj
->getNameURI())) {
642 obj
->setNameURI(uri
);
643 obj
->setNamePrefix(uriobj
->getNamePrefix());
645 JSString
*str
= js_ValueToString(cx
, Valueify(urival
));
648 uri
= str
->ensureLinear(cx
);
651 obj
->setNameURI(uri
);
653 obj
->clearNamePrefix();
655 } else if (argc
== 2) {
656 if (!isQName
|| !(uri
= uriobj
->getNameURI())) {
657 JSString
*str
= js_ValueToString(cx
, Valueify(urival
));
660 uri
= str
->ensureLinear(cx
);
664 obj
->setNameURI(uri
);
668 if (!JSVAL_IS_VOID(prefixval
)) {
669 JSString
*str
= js_ValueToString(cx
, Valueify(prefixval
));
673 JSAutoByteString bytes
;
674 if (js_ValueToPrintable(cx
, StringValue(str
), &bytes
)) {
675 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
676 JSMSG_BAD_XML_NAMESPACE
, bytes
.ptr());
681 } else if (JSVAL_IS_VOID(prefixval
) || !js_IsXMLName(cx
, prefixval
)) {
682 obj
->clearNamePrefix();
684 JSString
*str
= js_ValueToString(cx
, Valueify(prefixval
));
687 prefix
= str
->ensureLinear(cx
);
690 obj
->setNamePrefix(prefix
);
697 Namespace(JSContext
*cx
, uintN argc
, Value
*vp
)
699 JSObject
*thisobj
= NULL
;
700 (void)IsConstructing_PossiblyWithGivenThisObject(vp
, &thisobj
);
701 return NamespaceHelper(cx
, thisobj
, argc
, Jsvalify(vp
+ 2), Jsvalify(vp
));
705 * When argc is -1, it indicates argv is empty but the code should behave as
706 * if argc is 1 and argv[0] is JSVAL_VOID.
709 QNameHelper(JSContext
*cx
, JSObject
*obj
, intN argc
, jsval
*argv
, jsval
*rval
)
711 jsval nameval
, nsval
;
712 JSBool isQName
, isNamespace
;
714 JSLinearString
*uri
, *prefix
, *name
;
718 nameval
= JSVAL_VOID
;
721 nameval
= argv
[argc
> 1];
723 !JSVAL_IS_PRIMITIVE(nameval
) &&
724 JSVAL_TO_OBJECT(nameval
)->getClass() == &js_QNameClass
;
728 /* QName called as function. */
729 if (argc
== 1 && isQName
) {
730 /* QName called with one QName argument is identity. */
735 /* Create and return a new QName object exactly as if constructed. */
736 obj
= NewBuiltinClassInstanceXML(cx
, &js_QNameClass
);
740 *rval
= OBJECT_TO_JSVAL(obj
);
741 METER(xml_stats
.qname
);
744 /* If namespace is not specified and name is a QName, clone it. */
745 qn
= JSVAL_TO_OBJECT(nameval
);
747 uri
= qn
->getNameURI();
748 prefix
= qn
->getNamePrefix();
749 name
= qn
->getQNameLocalName();
753 /* Namespace and qname were passed -- use the qname's localName. */
754 nameval
= qn
->getQNameLocalNameVal();
758 name
= cx
->runtime
->emptyString
;
759 } else if (argc
< 0) {
760 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
762 JSString
*str
= js_ValueToString(cx
, Valueify(nameval
));
765 name
= str
->ensureLinear(cx
);
768 argv
[argc
> 1] = STRING_TO_JSVAL(name
);
771 if (argc
> 1 && !JSVAL_IS_VOID(argv
[0])) {
773 } else if (IS_STAR(name
)) {
776 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
778 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval
));
779 JS_ASSERT(JSVAL_TO_OBJECT(nsval
)->getClass() ==
783 if (JSVAL_IS_NULL(nsval
)) {
784 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
788 * Inline specialization of the Namespace constructor called with
789 * nsval passed as the only argument, to compute the uri and prefix
790 * for the constructed namespace, without actually allocating the
791 * object or computing other members. See ECMA-357 13.3.2 6(a) and
794 isNamespace
= isQName
= JS_FALSE
;
795 if (!JSVAL_IS_PRIMITIVE(nsval
)) {
796 obj2
= JSVAL_TO_OBJECT(nsval
);
797 isNamespace
= (obj2
->getClass() == &js_NamespaceClass
);
798 isQName
= (obj2
->getClass() == &js_QNameClass
);
800 #ifdef __GNUC__ /* suppress bogus gcc warnings */
805 uri
= obj2
->getNameURI();
806 prefix
= obj2
->getNamePrefix();
807 } else if (isQName
&& (uri
= obj2
->getNameURI())) {
809 prefix
= obj2
->getNamePrefix();
812 JSString
*str
= js_ValueToString(cx
, Valueify(nsval
));
815 uri
= str
->ensureLinear(cx
);
818 argv
[0] = STRING_TO_JSVAL(uri
); /* local root */
820 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
821 prefix
= uri
->empty() ? cx
->runtime
->emptyString
: NULL
;
826 InitXMLQName(obj
, uri
, prefix
, name
);
831 QName(JSContext
*cx
, uintN argc
, Value
*vp
)
833 JSObject
*thisobj
= NULL
;
834 (void)IsConstructing_PossiblyWithGivenThisObject(vp
, &thisobj
);
835 return QNameHelper(cx
, thisobj
, argc
, Jsvalify(vp
+ 2), Jsvalify(vp
));
839 * XMLArray library functions.
842 namespace_identity(const void *a
, const void *b
)
844 const JSObject
*nsa
= (const JSObject
*) a
;
845 const JSObject
*nsb
= (const JSObject
*) b
;
846 JSLinearString
*prefixa
= nsa
->getNamePrefix();
847 JSLinearString
*prefixb
= nsb
->getNamePrefix();
849 if (prefixa
&& prefixb
) {
850 if (!EqualStrings(prefixa
, prefixb
))
853 if (prefixa
|| prefixb
)
856 return EqualStrings(nsa
->getNameURI(), nsb
->getNameURI());
860 attr_identity(const void *a
, const void *b
)
862 const JSXML
*xmla
= (const JSXML
*) a
;
863 const JSXML
*xmlb
= (const JSXML
*) b
;
865 return qname_identity(xmla
->name
, xmlb
->name
);
869 JSXMLArrayCursor::trace(JSTracer
*trc
) {
873 for (JSXMLArrayCursor
*cursor
= this; cursor
; cursor
= cursor
->next
)
874 js::gc::MarkGCThing(trc
, cursor
->root
, "cursor_root", index
++);
878 XMLArrayCursorTrace(JSTracer
*trc
, JSXMLArrayCursor
*cursor
)
883 /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
885 JSXMLArray::setCapacity(JSContext
*cx
, uint32 newCapacity
)
887 if (newCapacity
== 0) {
888 /* We could let realloc(p, 0) free this, but purify gets confused. */
900 #if JS_BITS_PER_WORD == 32
901 (size_t)newCapacity
> ~(size_t)0 / sizeof(void *) ||
903 !(tmp
= (void **) js_realloc(vector
, newCapacity
* sizeof(void *)))) {
905 JS_ReportOutOfMemory(cx
);
910 capacity
= JSXML_PRESET_CAPACITY
| newCapacity
;
917 if (capacity
& JSXML_PRESET_CAPACITY
)
919 if (length
< capacity
)
920 setCapacity(NULL
, length
);
924 JSXMLArray::finish(JSContext
*cx
)
928 while (JSXMLArrayCursor
*cursor
= cursors
)
929 cursor
->disconnect();
932 memset(this, 0xd5, sizeof *this);
936 #define XML_NOT_FOUND ((uint32) -1)
939 XMLArrayFindMember(const JSXMLArray
*array
, void *elt
, JSIdentityOp identity
)
944 /* The identity op must not reallocate array->vector. */
945 vector
= array
->vector
;
947 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
948 if (identity(vector
[i
], elt
))
952 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
953 if (vector
[i
] == elt
)
957 return XML_NOT_FOUND
;
961 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
962 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
963 * should be greater than increment.
965 #define LINEAR_THRESHOLD 256
966 #define LINEAR_INCREMENT 32
969 XMLArrayAddMember(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, void *elt
)
975 if (index
>= array
->length
) {
976 if (index
>= JSXML_CAPACITY(array
)) {
977 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
978 capacity
= index
+ 1;
979 if (index
>= LINEAR_THRESHOLD
) {
980 capacity
= JS_ROUNDUP(capacity
, LINEAR_INCREMENT
);
982 JS_CEILING_LOG2(log2
, capacity
);
983 capacity
= JS_BIT(log2
);
986 #if JS_BITS_PER_WORD == 32
987 (size_t)capacity
> ~(size_t)0 / sizeof(void *) ||
990 js_realloc(array
->vector
, capacity
* sizeof(void *)))) {
991 JS_ReportOutOfMemory(cx
);
994 array
->capacity
= capacity
;
995 array
->vector
= vector
;
996 for (i
= array
->length
; i
< index
; i
++)
999 array
->length
= index
+ 1;
1002 array
->vector
[index
] = elt
;
1007 XMLArrayInsert(JSContext
*cx
, JSXMLArray
*array
, uint32 i
, uint32 n
)
1010 JSXMLArrayCursor
*cursor
;
1014 if (!array
->setCapacity(cx
, j
+ n
))
1017 array
->length
= j
+ n
;
1018 JS_ASSERT(n
!= (uint32
)-1);
1021 array
->vector
[j
+ n
] = array
->vector
[j
];
1024 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1025 if (cursor
->index
> i
)
1032 XMLArrayDelete(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, JSBool compress
)
1035 void **vector
, *elt
;
1036 JSXMLArrayCursor
*cursor
;
1038 length
= array
->length
;
1039 if (index
>= length
)
1042 vector
= array
->vector
;
1043 elt
= vector
[index
];
1045 while (++index
< length
)
1046 vector
[index
-1] = vector
[index
];
1047 array
->length
= length
- 1;
1048 array
->capacity
= JSXML_CAPACITY(array
);
1050 vector
[index
] = NULL
;
1053 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1054 if (cursor
->index
> index
)
1061 XMLArrayTruncate(JSContext
*cx
, JSXMLArray
*array
, uint32 length
)
1065 JS_ASSERT(!array
->cursors
);
1066 if (length
>= array
->length
)
1071 cx
->free(array
->vector
);
1074 vector
= (void **) js_realloc(array
->vector
, length
* sizeof(void *));
1079 if (array
->length
> length
)
1080 array
->length
= length
;
1081 array
->capacity
= length
;
1082 array
->vector
= vector
;
1085 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1086 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1088 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1089 ? (t *) (a)->vector[i] \
1091 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1092 if ((a)->length <= (i)) \
1093 (a)->length = (i) + 1; \
1094 ((a)->vector[i] = (void *)(e)); \
1096 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1097 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1098 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1099 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1100 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1103 * Define XML setting property strings and constants early, so everyone can
1104 * use the same names.
1106 static const char js_ignoreComments_str
[] = "ignoreComments";
1107 static const char js_ignoreProcessingInstructions_str
[]
1108 = "ignoreProcessingInstructions";
1109 static const char js_ignoreWhitespace_str
[] = "ignoreWhitespace";
1110 static const char js_prettyPrinting_str
[] = "prettyPrinting";
1111 static const char js_prettyIndent_str
[] = "prettyIndent";
1113 #define XSF_IGNORE_COMMENTS JS_BIT(0)
1114 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1115 #define XSF_IGNORE_WHITESPACE JS_BIT(2)
1116 #define XSF_PRETTY_PRINTING JS_BIT(3)
1118 static JSPropertySpec xml_static_props
[] = {
1119 {js_ignoreComments_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1120 {js_ignoreProcessingInstructions_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1121 {js_ignoreWhitespace_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1122 {js_prettyPrinting_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1123 {js_prettyIndent_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1127 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1128 #define IS_XML(str) \
1129 (str->length() == 3 && IS_XML_CHARS(str->chars()))
1131 #define IS_XMLNS(str) \
1132 (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1134 #define IS_XML_CHARS(chars) \
1135 (JS_TOLOWER((chars)[0]) == 'x' && \
1136 JS_TOLOWER((chars)[1]) == 'm' && \
1137 JS_TOLOWER((chars)[2]) == 'l')
1139 #define HAS_NS_AFTER_XML(chars) \
1140 (JS_TOLOWER((chars)[3]) == 'n' && \
1141 JS_TOLOWER((chars)[4]) == 's')
1143 #define IS_XMLNS_CHARS(chars) \
1144 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1146 #define STARTS_WITH_XML(chars,length) \
1147 (length >= 3 && IS_XML_CHARS(chars))
1149 static const char xml_namespace_str
[] = "http://www.w3.org/XML/1998/namespace";
1150 static const char xmlns_namespace_str
[] = "http://www.w3.org/2000/xmlns/";
1153 ParseNodeToQName(Parser
*parser
, JSParseNode
*pn
,
1154 JSXMLArray
*inScopeNSes
, JSBool isAttributeName
)
1156 JSContext
*cx
= parser
->context
;
1157 JSLinearString
*str
, *uri
, *prefix
, *localName
;
1158 size_t length
, offset
;
1159 const jschar
*start
, *limit
, *colon
;
1162 JSLinearString
*nsprefix
;
1164 JS_ASSERT(pn
->pn_arity
== PN_NULLARY
);
1166 start
= str
->chars();
1167 length
= str
->length();
1168 JS_ASSERT(length
!= 0 && *start
!= '@');
1169 JS_ASSERT(length
!= 1 || *start
!= '*');
1171 uri
= cx
->runtime
->emptyString
;
1172 limit
= start
+ length
;
1173 colon
= js_strchr_limit(start
, ':', limit
);
1175 offset
= colon
- start
;
1176 prefix
= js_NewDependentString(cx
, str
, 0, offset
);
1180 if (STARTS_WITH_XML(start
, offset
)) {
1182 uri
= JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx
, xml_namespace_str
));
1185 } else if (offset
== 5 && HAS_NS_AFTER_XML(start
)) {
1186 uri
= JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx
, xmlns_namespace_str
));
1194 n
= inScopeNSes
->length
;
1197 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSObject
);
1198 nsprefix
= ns
->getNamePrefix();
1199 if (nsprefix
&& EqualStrings(nsprefix
, prefix
)) {
1200 uri
= ns
->getNameURI();
1207 Value v
= StringValue(prefix
);
1208 JSAutoByteString bytes
;
1209 if (js_ValueToPrintable(parser
->context
, v
, &bytes
)) {
1210 ReportCompileErrorNumber(parser
->context
, &parser
->tokenStream
, pn
,
1211 JSREPORT_ERROR
, JSMSG_BAD_XML_NAMESPACE
, bytes
.ptr());
1216 localName
= js_NewStringCopyN(parser
->context
, colon
+ 1, length
- (offset
+ 1));
1220 if (isAttributeName
) {
1222 * An unprefixed attribute is not in any namespace, so set prefix
1223 * as well as uri to the empty string.
1228 * Loop from back to front looking for the closest declared default
1231 n
= inScopeNSes
->length
;
1234 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSObject
);
1235 nsprefix
= ns
->getNamePrefix();
1236 if (!nsprefix
|| nsprefix
->empty()) {
1237 uri
= ns
->getNameURI();
1241 prefix
= uri
->empty() ? parser
->context
->runtime
->emptyString
: NULL
;
1246 return NewXMLQName(parser
->context
, uri
, prefix
, localName
);
1250 ChompXMLWhitespace(JSContext
*cx
, JSString
*str
)
1252 size_t length
, newlength
, offset
;
1253 const jschar
*cp
, *start
, *end
;
1256 length
= str
->length();
1257 start
= str
->getChars(cx
);
1261 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
1263 if (!JS_ISXMLSPACE(c
))
1268 if (!JS_ISXMLSPACE(c
))
1272 newlength
= end
- cp
;
1273 if (newlength
== length
)
1275 offset
= cp
- start
;
1276 return js_NewDependentString(cx
, str
, offset
, newlength
);
1280 ParseNodeToXML(Parser
*parser
, JSParseNode
*pn
,
1281 JSXMLArray
*inScopeNSes
, uintN flags
)
1283 JSContext
*cx
= parser
->context
;
1284 JSXML
*xml
, *kid
, *attr
, *attrj
;
1285 JSLinearString
*str
;
1286 uint32 length
, n
, i
, j
;
1287 JSParseNode
*pn2
, *pn3
, *head
, **pnp
;
1289 JSObject
*qn
, *attrjqn
;
1290 JSXMLClass xml_class
;
1293 if (!JS_CHECK_STACK_SIZE(cx
->stackLimit
, &stackDummy
)) {
1294 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn
, JSREPORT_ERROR
,
1295 JSMSG_OVER_RECURSED
);
1299 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1302 * Cases return early to avoid common code that gets an outermost xml's
1303 * object, which protects GC-things owned by xml and its descendants from
1304 * garbage collection.
1307 if (!js_EnterLocalRootScope(cx
))
1309 switch (pn
->pn_type
) {
1311 length
= inScopeNSes
->length
;
1313 xml
= ParseNodeToXML(parser
, pn2
, inScopeNSes
, flags
);
1320 if (!xml
->xml_kids
.setCapacity(cx
, n
))
1324 while ((pn2
= pn2
->pn_next
) != NULL
) {
1325 if (!pn2
->pn_next
) {
1326 /* Don't append the end tag! */
1327 JS_ASSERT(pn2
->pn_type
== TOK_XMLETAGO
);
1331 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1332 n
> 1 && pn2
->pn_type
== TOK_XMLSPACE
) {
1337 kid
= ParseNodeToXML(parser
, pn2
, inScopeNSes
, flags
);
1338 if (kid
== PN2X_SKIP_CHILD
) {
1346 /* Store kid in xml right away, to protect it from GC. */
1347 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1351 /* XXX where is this documented in an XML spec, or in E4X? */
1352 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1353 n
> 1 && kid
->xml_class
== JSXML_CLASS_TEXT
) {
1354 JSString
*str
= ChompXMLWhitespace(cx
, kid
->xml_value
);
1357 kid
->xml_value
= str
;
1362 if (n
< pn
->pn_count
- 2)
1363 xml
->xml_kids
.trim();
1364 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1368 xml
= js_NewXML(cx
, JSXML_CLASS_LIST
);
1373 if (!xml
->xml_kids
.setCapacity(cx
, n
))
1377 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
1379 * Always ignore insignificant whitespace in lists -- we shouldn't
1380 * condition this on an XML.ignoreWhitespace setting when the list
1381 * constructor is XMLList (note XML/XMLList unification hazard).
1383 if (pn2
->pn_type
== TOK_XMLSPACE
) {
1388 kid
= ParseNodeToXML(parser
, pn2
, inScopeNSes
, flags
);
1389 if (kid
== PN2X_SKIP_CHILD
) {
1397 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1401 if (n
< pn
->pn_count
)
1402 xml
->xml_kids
.trim();
1407 length
= inScopeNSes
->length
;
1409 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
);
1410 if (pn2
->pn_arity
== PN_LIST
)
1413 xml
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
1417 /* First pass: check syntax and process namespace declarations. */
1418 JS_ASSERT(pn
->pn_count
>= 1);
1419 n
= pn
->pn_count
- 1;
1420 pnp
= &pn2
->pn_next
;
1422 while ((pn2
= *pnp
) != NULL
) {
1424 const jschar
*chars
;
1426 if (pn2
->pn_type
!= TOK_XMLNAME
|| pn2
->pn_arity
!= PN_NULLARY
)
1429 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1430 for (pn3
= head
; pn3
!= pn2
; pn3
= pn3
->pn_next
->pn_next
) {
1431 if (pn3
->pn_atom
== pn2
->pn_atom
) {
1432 Value v
= StringValue(ATOM_TO_STRING(pn2
->pn_atom
));
1433 JSAutoByteString bytes
;
1434 if (js_ValueToPrintable(cx
, v
, &bytes
)) {
1435 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn2
,
1436 JSREPORT_ERROR
, JSMSG_DUPLICATE_XML_ATTR
,
1443 JSAtom
*atom
= pn2
->pn_atom
;
1446 if (pn2
->pn_type
!= TOK_XMLATTR
)
1449 chars
= atom
->chars();
1450 length
= atom
->length();
1452 IS_XMLNS_CHARS(chars
) &&
1453 (length
== 5 || chars
[5] == ':')) {
1454 JSLinearString
*uri
, *prefix
;
1456 uri
= ATOM_TO_STRING(pn2
->pn_atom
);
1458 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1459 prefix
= cx
->runtime
->emptyString
;
1461 prefix
= js_NewStringCopyN(cx
, chars
+ 6, length
- 6);
1467 * Once the new ns is appended to xml->xml_namespaces, it is
1468 * protected from GC by the object that owns xml -- which is
1469 * either xml->object if outermost, or the object owning xml's
1470 * oldest ancestor if !outermost.
1472 ns
= NewXMLNamespace(cx
, prefix
, uri
, JS_TRUE
);
1477 * Don't add a namespace that's already in scope. If someone
1478 * extracts a child property from its parent via [[Get]], then
1479 * we enforce the invariant, noted many times in ECMA-357, that
1480 * the child's namespaces form a possibly-improper superset of
1481 * its ancestors' namespaces.
1483 if (!XMLARRAY_HAS_MEMBER(inScopeNSes
, ns
, namespace_identity
)) {
1484 if (!XMLARRAY_APPEND(cx
, inScopeNSes
, ns
) ||
1485 !XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
)) {
1492 *pnp
= pn2
->pn_next
;
1493 /* XXXbe recycle pn2 */
1497 pnp
= &pn2
->pn_next
;
1500 xml
->xml_namespaces
.trim();
1502 /* Second pass: process tag name and attributes, using namespaces. */
1504 qn
= ParseNodeToQName(parser
, pn2
, inScopeNSes
, JS_FALSE
);
1509 JS_ASSERT((n
& 1) == 0);
1511 if (!xml
->xml_attrs
.setCapacity(cx
, n
))
1514 for (i
= 0; (pn2
= pn2
->pn_next
) != NULL
; i
++) {
1515 qn
= ParseNodeToQName(parser
, pn2
, inScopeNSes
, JS_TRUE
);
1517 xml
->xml_attrs
.length
= i
;
1522 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1523 * this time checking local name and namespace URI.
1525 for (j
= 0; j
< i
; j
++) {
1526 attrj
= XMLARRAY_MEMBER(&xml
->xml_attrs
, j
, JSXML
);
1527 attrjqn
= attrj
->name
;
1528 if (EqualStrings(attrjqn
->getNameURI(), qn
->getNameURI()) &&
1529 EqualStrings(attrjqn
->getQNameLocalName(), qn
->getQNameLocalName())) {
1530 Value v
= StringValue(ATOM_TO_STRING(pn2
->pn_atom
));
1531 JSAutoByteString bytes
;
1532 if (js_ValueToPrintable(cx
, v
, &bytes
)) {
1533 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn2
,
1534 JSREPORT_ERROR
, JSMSG_DUPLICATE_XML_ATTR
,
1543 JS_ASSERT(pn2
->pn_type
== TOK_XMLATTR
);
1545 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
1549 XMLARRAY_SET_MEMBER(&xml
->xml_attrs
, i
, attr
);
1552 attr
->xml_value
= ATOM_TO_STRING(pn2
->pn_atom
);
1555 /* Point tag closes its own namespace scope. */
1556 if (pn
->pn_type
== TOK_XMLPTAGC
)
1557 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1563 case TOK_XMLCOMMENT
:
1565 str
= ATOM_TO_STRING(pn
->pn_atom
);
1567 if (pn
->pn_type
== TOK_XMLCOMMENT
) {
1568 if (flags
& XSF_IGNORE_COMMENTS
)
1570 xml_class
= JSXML_CLASS_COMMENT
;
1571 } else if (pn
->pn_type
== TOK_XMLPI
) {
1573 Value v
= StringValue(str
);
1574 JSAutoByteString bytes
;
1575 if (js_ValueToPrintable(cx
, v
, &bytes
)) {
1576 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn
,
1577 JSREPORT_ERROR
, JSMSG_RESERVED_ID
, bytes
.ptr());
1582 if (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
)
1585 qn
= ParseNodeToQName(parser
, pn
, inScopeNSes
, JS_FALSE
);
1590 ? ATOM_TO_STRING(pn
->pn_atom2
)
1591 : cx
->runtime
->emptyString
;
1592 xml_class
= JSXML_CLASS_PROCESSING_INSTRUCTION
;
1594 /* CDATA section content, or element text. */
1595 xml_class
= JSXML_CLASS_TEXT
;
1598 xml
= js_NewXML(cx
, xml_class
);
1602 if (pn
->pn_type
== TOK_XMLSPACE
)
1603 xml
->xml_flags
|= XMLF_WHITESPACE_TEXT
;
1604 xml
->xml_value
= str
;
1611 js_LeaveLocalRootScopeWithResult(cx
, xml
);
1615 js_LeaveLocalRootScope(cx
);
1616 return PN2X_SKIP_CHILD
;
1618 #undef PN2X_SKIP_CHILD
1621 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn
, JSREPORT_ERROR
, JSMSG_BAD_XML_MARKUP
);
1623 js_LeaveLocalRootScope(cx
);
1628 * XML helper, object-ops, and library functions. We start with the helpers,
1629 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1632 GetXMLSetting(JSContext
*cx
, const char *name
, jsval
*vp
)
1636 if (!js_FindClassObject(cx
, NULL
, JSProto_XML
, Valueify(&v
)))
1638 if (!VALUE_IS_FUNCTION(cx
, v
)) {
1642 return JS_GetProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
);
1646 GetBooleanXMLSetting(JSContext
*cx
, const char *name
, JSBool
*bp
)
1650 return GetXMLSetting(cx
, name
, &v
) && JS_ValueToBoolean(cx
, v
, bp
);
1654 GetUint32XMLSetting(JSContext
*cx
, const char *name
, uint32
*uip
)
1658 return GetXMLSetting(cx
, name
, &v
) && JS_ValueToECMAUint32(cx
, v
, uip
);
1662 GetXMLSettingFlags(JSContext
*cx
, uintN
*flagsp
)
1666 if (!GetBooleanXMLSetting(cx
, js_ignoreComments_str
, &flag
[0]) ||
1667 !GetBooleanXMLSetting(cx
, js_ignoreProcessingInstructions_str
, &flag
[1]) ||
1668 !GetBooleanXMLSetting(cx
, js_ignoreWhitespace_str
, &flag
[2]) ||
1669 !GetBooleanXMLSetting(cx
, js_prettyPrinting_str
, &flag
[3])) {
1674 for (size_t n
= 0; n
< 4; ++n
)
1676 *flagsp
|= JS_BIT(n
);
1681 ParseXMLSource(JSContext
*cx
, JSString
*src
)
1684 JSLinearString
*uri
;
1685 size_t urilen
, srclen
, length
, offset
, dstlen
;
1687 const jschar
*srcp
, *endp
;
1689 const char *filename
;
1693 static const char prefix
[] = "<parent xmlns=\"";
1694 static const char middle
[] = "\">";
1695 static const char suffix
[] = "</parent>";
1697 #define constrlen(constr) (sizeof(constr) - 1)
1699 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
1701 uri
= JSVAL_TO_OBJECT(nsval
)->getNameURI();
1702 uri
= js_EscapeAttributeValue(cx
, uri
, JS_FALSE
);
1706 urilen
= uri
->length();
1707 srclen
= src
->length();
1708 length
= constrlen(prefix
) + urilen
+ constrlen(middle
) + srclen
+
1711 chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
1716 js_InflateStringToBuffer(cx
, prefix
, constrlen(prefix
), chars
, &dstlen
);
1718 js_strncpy(chars
+ offset
, uri
->chars(), urilen
);
1720 dstlen
= length
- offset
+ 1;
1721 js_InflateStringToBuffer(cx
, middle
, constrlen(middle
), chars
+ offset
,
1724 srcp
= src
->getChars(cx
);
1729 js_strncpy(chars
+ offset
, srcp
, srclen
);
1731 dstlen
= length
- offset
+ 1;
1732 js_InflateStringToBuffer(cx
, suffix
, constrlen(suffix
), chars
+ offset
,
1734 chars
[offset
+ dstlen
] = 0;
1738 FrameRegsIter
i(cx
);
1739 for (; !i
.done() && !i
.pc(); ++i
)
1740 JS_ASSERT(!i
.fp()->isScriptFrame());
1744 JSStackFrame
*fp
= i
.fp();
1745 op
= (JSOp
) *i
.pc();
1746 if (op
== JSOP_TOXML
|| op
== JSOP_TOXMLLIST
) {
1747 filename
= fp
->script()->filename
;
1748 lineno
= js_FramePCToLineNumber(cx
, fp
);
1749 for (endp
= srcp
+ srclen
; srcp
< endp
; srcp
++) {
1758 if (parser
.init(chars
, length
, filename
, lineno
, cx
->findVersion())) {
1759 JSObject
*scopeChain
= GetScopeChain(cx
);
1764 JSParseNode
*pn
= parser
.parseXMLText(scopeChain
, false);
1766 if (pn
&& GetXMLSettingFlags(cx
, &flags
)) {
1767 AutoNamespaceArray
namespaces(cx
);
1768 if (namespaces
.array
.setCapacity(cx
, 1))
1769 xml
= ParseNodeToXML(&parser
, pn
, &namespaces
.array
, flags
);
1781 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1783 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1786 * for all x belonging to XML:
1787 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1789 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1790 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1792 * Same goes for 10.4.1 Step 7(a).
1794 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1795 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1796 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1797 * undeclared namespaces associated with x not belonging to ancestorNS.
1800 OrphanXMLChild(JSContext
*cx
, JSXML
*xml
, uint32 i
)
1804 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, 0, JSObject
);
1805 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
1808 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
1809 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
1811 ns
->setNamespaceDeclared(JSVAL_VOID
);
1818 ToXML(JSContext
*cx
, jsval v
)
1826 if (JSVAL_IS_PRIMITIVE(v
)) {
1827 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
1830 obj
= JSVAL_TO_OBJECT(v
);
1832 xml
= (JSXML
*) obj
->getPrivate();
1833 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
1834 if (xml
->xml_kids
.length
!= 1)
1836 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
1838 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
1839 return js_GetXMLObject(cx
, xml
);
1845 clasp
= obj
->getClass();
1846 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
1850 if (clasp
!= &js_StringClass
&&
1851 clasp
!= &js_NumberClass
&&
1852 clasp
!= &js_BooleanClass
) {
1857 str
= js_ValueToString(cx
, Valueify(v
));
1862 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1866 xml
= ParseXMLSource(cx
, str
);
1869 length
= JSXML_LENGTH(xml
);
1873 obj
= js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
1876 } else if (length
== 1) {
1877 xml
= OrphanXMLChild(cx
, xml
, 0);
1880 obj
= js_GetXMLObject(cx
, xml
);
1884 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_SYNTAX_ERROR
);
1890 js_ReportValueError(cx
, JSMSG_BAD_XML_CONVERSION
,
1891 JSDVG_IGNORE_STACK
, Valueify(v
), NULL
);
1896 Append(JSContext
*cx
, JSXML
*list
, JSXML
*kid
);
1899 ToXMLList(JSContext
*cx
, jsval v
)
1901 JSObject
*obj
, *listobj
;
1902 JSXML
*xml
, *list
, *kid
;
1907 if (JSVAL_IS_PRIMITIVE(v
)) {
1908 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
1911 obj
= JSVAL_TO_OBJECT(v
);
1913 xml
= (JSXML
*) obj
->getPrivate();
1914 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
1915 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
1918 list
= (JSXML
*) listobj
->getPrivate();
1919 if (!Append(cx
, list
, xml
))
1926 clasp
= obj
->getClass();
1927 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
1931 if (clasp
!= &js_StringClass
&&
1932 clasp
!= &js_NumberClass
&&
1933 clasp
!= &js_BooleanClass
) {
1938 str
= js_ValueToString(cx
, Valueify(v
));
1945 if (!js_EnterLocalRootScope(cx
))
1947 xml
= ParseXMLSource(cx
, str
);
1949 js_LeaveLocalRootScope(cx
);
1952 length
= JSXML_LENGTH(xml
);
1955 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
1957 list
= (JSXML
*) listobj
->getPrivate();
1958 for (i
= 0; i
< length
; i
++) {
1959 kid
= OrphanXMLChild(cx
, xml
, i
);
1960 if (!kid
|| !Append(cx
, list
, kid
)) {
1968 js_LeaveLocalRootScopeWithResult(cx
, listobj
);
1972 js_ReportValueError(cx
, JSMSG_BAD_XMLLIST_CONVERSION
,
1973 JSDVG_IGNORE_STACK
, Valueify(v
), NULL
);
1978 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
1979 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
1980 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
1981 * MakeXMLSpecialString subroutine.
1983 * These functions mutate sb, leaving it empty.
1985 static JSFlatString
*
1986 MakeXMLSpecialString(JSContext
*cx
, StringBuffer
&sb
,
1987 JSString
*str
, JSString
*str2
,
1988 const jschar
*prefix
, size_t prefixlength
,
1989 const jschar
*suffix
, size_t suffixlength
)
1991 if (!sb
.append(prefix
, prefixlength
) || !sb
.append(str
))
1993 if (str2
&& !str2
->empty()) {
1994 if (!sb
.append(' ') || !sb
.append(str2
))
1997 if (!sb
.append(suffix
, suffixlength
))
2000 return sb
.finishString();
2003 static JSFlatString
*
2004 MakeXMLCDATAString(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
)
2006 static const jschar cdata_prefix_ucNstr
[] = {'<', '!', '[',
2007 'C', 'D', 'A', 'T', 'A',
2009 static const jschar cdata_suffix_ucNstr
[] = {']', ']', '>'};
2011 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2012 cdata_prefix_ucNstr
, 9,
2013 cdata_suffix_ucNstr
, 3);
2016 static JSFlatString
*
2017 MakeXMLCommentString(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
)
2019 static const jschar comment_prefix_ucNstr
[] = {'<', '!', '-', '-'};
2020 static const jschar comment_suffix_ucNstr
[] = {'-', '-', '>'};
2022 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2023 comment_prefix_ucNstr
, 4,
2024 comment_suffix_ucNstr
, 3);
2027 static JSFlatString
*
2028 MakeXMLPIString(JSContext
*cx
, StringBuffer
&sb
, JSString
*name
,
2031 static const jschar pi_prefix_ucNstr
[] = {'<', '?'};
2032 static const jschar pi_suffix_ucNstr
[] = {'?', '>'};
2034 return MakeXMLSpecialString(cx
, sb
, name
, value
,
2035 pi_prefix_ucNstr
, 2,
2036 pi_suffix_ucNstr
, 2);
2040 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2041 * equals, a double quote, an attribute value, and a closing double quote.
2044 AppendAttributeValue(JSContext
*cx
, StringBuffer
&sb
, JSString
*valstr
)
2046 if (!sb
.append('='))
2048 valstr
= js_EscapeAttributeValue(cx
, valstr
, JS_TRUE
);
2049 return valstr
&& sb
.append(valstr
);
2053 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2055 * These functions mutate sb, leaving it empty.
2057 static JSFlatString
*
2058 EscapeElementValue(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
, uint32 toSourceFlag
)
2060 size_t length
= str
->length();
2061 const jschar
*start
= str
->getChars(cx
);
2065 for (const jschar
*cp
= start
, *end
= start
+ length
; cp
!= end
; ++cp
) {
2069 if (!sb
.append(js_lt_entity_str
))
2073 if (!sb
.append(js_gt_entity_str
))
2077 if (!sb
.append(js_amp_entity_str
))
2082 * If EscapeElementValue is called by toSource/uneval, we also need
2083 * to escape '{'. See bug 463360.
2086 if (!sb
.append(js_leftcurly_entity_str
))
2096 return sb
.finishString();
2100 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2102 * These functions mutate sb, leaving it empty.
2104 static JSFlatString
*
2105 EscapeAttributeValue(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
, JSBool quote
)
2107 size_t length
= str
->length();
2108 const jschar
*start
= str
->getChars(cx
);
2112 if (quote
&& !sb
.append('"'))
2115 for (const jschar
*cp
= start
, *end
= start
+ length
; cp
!= end
; ++cp
) {
2119 if (!sb
.append(js_quot_entity_str
))
2123 if (!sb
.append(js_lt_entity_str
))
2127 if (!sb
.append(js_amp_entity_str
))
2131 if (!sb
.append("
"))
2135 if (!sb
.append("
"))
2139 if (!sb
.append("	"))
2148 if (quote
&& !sb
.append('"'))
2151 return sb
.finishString();
2154 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2156 GetNamespace(JSContext
*cx
, JSObject
*qn
, const JSXMLArray
*inScopeNSes
)
2158 JSLinearString
*uri
, *prefix
, *nsprefix
;
2159 JSObject
*match
, *ns
;
2163 uri
= qn
->getNameURI();
2164 prefix
= qn
->getNamePrefix();
2167 JSAutoByteString bytes
;
2168 const char *s
= !prefix
?
2170 : js_ValueToPrintable(cx
, StringValue(prefix
), &bytes
);
2172 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_XML_NAMESPACE
, s
);
2176 /* Look for a matching namespace in inScopeNSes, if provided. */
2179 for (i
= 0, n
= inScopeNSes
->length
; i
< n
; i
++) {
2180 ns
= XMLARRAY_MEMBER(inScopeNSes
, i
, JSObject
);
2185 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2186 * If we preserve prefixes, we must match null prefix against
2187 * an empty prefix of ns, in order to avoid generating redundant
2188 * prefixed and default namespaces for cases such as:
2190 * x = <t xmlns="http://foo.com"/>
2191 * print(x.toXMLString());
2193 * Per 10.3.2.1, the namespace attribute in t has an empty string
2194 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2196 * 1. If the [local name] property of a is "xmlns"
2197 * a. Map ns.prefix to the empty string
2199 * But t's name has a null prefix in this implementation, meaning
2200 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2201 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2202 * saying how "no value" maps to an ECMA-357 value -- but it must
2203 * map to the *undefined* prefix value).
2205 * Since "" != undefined (or null, in the current implementation)
2206 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2207 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2208 * This spec bug leads to ToXMLString results that duplicate the
2209 * declared namespace.
2211 if (EqualStrings(ns
->getNameURI(), uri
)) {
2212 nsprefix
= ns
->getNamePrefix();
2213 if (nsprefix
== prefix
||
2214 ((nsprefix
&& prefix
)
2215 ? EqualStrings(nsprefix
, prefix
)
2216 : (nsprefix
? nsprefix
: prefix
)->empty())) {
2224 /* If we didn't match, make a new namespace from qn. */
2226 argv
[0] = prefix
? STRING_TO_JSVAL(prefix
) : JSVAL_VOID
;
2227 argv
[1] = STRING_TO_JSVAL(uri
);
2228 ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, NULL
,
2237 static JSLinearString
*
2238 GeneratePrefix(JSContext
*cx
, JSLinearString
*uri
, JSXMLArray
*decls
)
2240 const jschar
*cp
, *start
, *end
;
2241 size_t length
, newlength
, offset
;
2242 uint32 i
, n
, m
, serial
;
2246 JSLinearString
*nsprefix
, *prefix
;
2248 JS_ASSERT(!uri
->empty());
2251 * If there are no *declared* namespaces, skip all collision detection and
2252 * return a short prefix quickly; an example of such a situation:
2255 * var n = new Namespace("http://example.com/");
2256 * x.@n::att = "val";
2259 * This is necessary for various log10 uses below to be valid.
2261 if (decls
->length
== 0)
2262 return js_NewStringCopyZ(cx
, "a");
2265 * Try peeling off the last filename suffix or pathname component till
2266 * we have a valid XML name. This heuristic will prefer "xul" given
2267 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2268 * likely URI of the form ".../xbl2/2005".
2270 start
= uri
->chars();
2271 end
= start
+ uri
->length();
2273 while (--cp
> start
) {
2274 if (*cp
== '.' || *cp
== '/' || *cp
== ':') {
2277 if (IsXMLName(cp
, length
) && !STARTS_WITH_XML(cp
, length
))
2285 * If the namespace consisted only of non-XML names or names that begin
2286 * case-insensitively with "xml", arbitrarily create a prefix consisting
2287 * of 'a's of size length (allowing dp-calculating code to work with or
2288 * without this branch executing) plus the space for storing a hyphen and
2289 * the serial number (avoiding reallocation if a collision happens).
2293 if (STARTS_WITH_XML(cp
, length
) || !IsXMLName(cp
, length
)) {
2294 newlength
= length
+ 2 + (size_t) log10((double) decls
->length
);
2296 cx
->malloc((newlength
+ 1) * sizeof(jschar
));
2301 for (i
= 0; i
< newlength
; i
++)
2306 * Now search through decls looking for a collision. If we collide with
2307 * an existing prefix, start tacking on a hyphen and a serial number.
2312 for (i
= 0, n
= decls
->length
; i
< n
; i
++) {
2313 ns
= XMLARRAY_MEMBER(decls
, i
, JSObject
);
2314 if (ns
&& (nsprefix
= ns
->getNamePrefix()) &&
2315 nsprefix
->length() == newlength
&&
2316 !memcmp(nsprefix
->chars(), bp
,
2317 newlength
* sizeof(jschar
))) {
2319 newlength
= length
+ 2 + (size_t) log10((double) n
);
2321 cx
->malloc((newlength
+ 1) * sizeof(jschar
));
2324 js_strncpy(bp
, cp
, length
);
2328 JS_ASSERT(serial
<= n
);
2329 dp
= bp
+ length
+ 2 + (size_t) log10((double) serial
);
2331 for (m
= serial
; m
!= 0; m
/= 10)
2332 *--dp
= (jschar
)('0' + m
% 10);
2334 JS_ASSERT(dp
== bp
+ length
);
2343 offset
= cp
- start
;
2344 prefix
= js_NewDependentString(cx
, uri
, offset
, length
);
2346 prefix
= js_NewString(cx
, bp
, newlength
);
2354 namespace_match(const void *a
, const void *b
)
2356 const JSObject
*nsa
= (const JSObject
*) a
;
2357 const JSObject
*nsb
= (const JSObject
*) b
;
2358 JSLinearString
*prefixa
, *prefixb
= nsb
->getNamePrefix();
2361 prefixa
= nsa
->getNamePrefix();
2362 return prefixa
&& EqualStrings(prefixa
, prefixb
);
2364 return EqualStrings(nsa
->getNameURI(), nsb
->getNameURI());
2367 /* ECMA-357 10.2.1 and 10.2.2 */
2368 #define TO_SOURCE_FLAG 0x80000000
2371 XMLToXMLString(JSContext
*cx
, JSXML
*xml
, const JSXMLArray
*ancestorNSes
,
2374 JSBool pretty
, indentKids
;
2375 StringBuffer
sb(cx
);
2377 JSLinearString
*prefix
, *nsuri
;
2378 uint32 i
, n
, nextIndentLevel
;
2380 AutoNamespaceArray
empty(cx
), decls(cx
), ancdecls(cx
);
2382 if (!GetBooleanXMLSetting(cx
, js_prettyPrinting_str
, &pretty
))
2386 if (!sb
.appendN(' ', indentLevel
& ~TO_SOURCE_FLAG
))
2392 switch (xml
->xml_class
) {
2393 case JSXML_CLASS_TEXT
:
2396 str
= ChompXMLWhitespace(cx
, xml
->xml_value
);
2400 str
= xml
->xml_value
;
2402 return EscapeElementValue(cx
, sb
, str
, indentLevel
& TO_SOURCE_FLAG
);
2404 case JSXML_CLASS_ATTRIBUTE
:
2406 return EscapeAttributeValue(cx
, sb
, xml
->xml_value
,
2407 (indentLevel
& TO_SOURCE_FLAG
) != 0);
2409 case JSXML_CLASS_COMMENT
:
2411 return MakeXMLCommentString(cx
, sb
, xml
->xml_value
);
2413 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
2415 return MakeXMLPIString(cx
, sb
, xml
->name
->getQNameLocalName(),
2418 case JSXML_CLASS_LIST
:
2419 /* ECMA-357 10.2.2. */
2421 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
2423 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
2424 if (pretty
&& i
!= 0) {
2425 if (!sb
.append('\n'))
2429 JSString
*kidstr
= XMLToXMLString(cx
, kid
, ancestorNSes
, indentLevel
);
2430 if (!kidstr
|| !sb
.append(kidstr
))
2437 return cx
->runtime
->emptyString
;
2438 return sb
.finishString();
2443 /* After this point, control must flow through label out: to exit. */
2444 if (!js_EnterLocalRootScope(cx
))
2447 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2449 ancestorNSes
= &empty
.array
;
2451 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2453 JSXMLArrayCursor
cursor(&xml
->xml_namespaces
);
2454 while ((ns
= (JSObject
*) cursor
.getNext()) != NULL
) {
2455 if (!IsDeclared(ns
))
2457 if (!XMLARRAY_HAS_MEMBER(ancestorNSes
, ns
, namespace_identity
)) {
2458 /* NOTE: may want to exclude unused namespaces here. */
2459 ns2
= NewXMLNamespace(cx
, ns
->getNamePrefix(), ns
->getNameURI(), JS_TRUE
);
2460 if (!ns2
|| !XMLARRAY_APPEND(cx
, &decls
.array
, ns2
))
2467 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2468 * not own its member references. In the spec, ancdecls has no name, but
2469 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2472 if (!ancdecls
.array
.setCapacity(cx
, ancestorNSes
->length
+ decls
.length()))
2474 for (i
= 0, n
= ancestorNSes
->length
; i
< n
; i
++) {
2475 ns2
= XMLARRAY_MEMBER(ancestorNSes
, i
, JSObject
);
2478 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls
.array
, ns2
, namespace_identity
));
2479 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns2
))
2482 for (i
= 0, n
= decls
.length(); i
< n
; i
++) {
2483 ns2
= XMLARRAY_MEMBER(&decls
.array
, i
, JSObject
);
2486 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls
.array
, ns2
, namespace_identity
));
2487 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns2
))
2491 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2492 ns
= GetNamespace(cx
, xml
->name
, &ancdecls
.array
);
2496 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2497 prefix
= ns
->getNamePrefix();
2500 * Create a namespace prefix that isn't used by any member of decls.
2501 * Assign the new prefix to a copy of ns. Flag this namespace as if
2502 * it were declared, for assertion-testing's sake later below.
2504 * Erratum: if prefix and xml->name are both null (*undefined* in
2505 * ECMA-357), we know that xml was named using the default namespace
2506 * (proof: see GetNamespace and the Namespace constructor called with
2507 * two arguments). So we ought not generate a new prefix here, when
2508 * we can declare ns as the default namespace for xml.
2510 * This helps descendants inherit the namespace instead of redundantly
2511 * redeclaring it with generated prefixes in each descendant.
2513 nsuri
= ns
->getNameURI();
2514 if (!xml
->name
->getNamePrefix()) {
2515 prefix
= cx
->runtime
->emptyString
;
2517 prefix
= GeneratePrefix(cx
, nsuri
, &ancdecls
.array
);
2521 ns
= NewXMLNamespace(cx
, prefix
, nsuri
, JS_TRUE
);
2526 * If the xml->name was unprefixed, we must remove any declared default
2527 * namespace from decls before appending ns. How can you get a default
2528 * namespace in decls that doesn't match the one from name? Apparently
2529 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2530 * to fix this is to update x's in-scope namespaces when setNamespace
2531 * is called, but that's not specified by ECMA-357.
2533 * Likely Erratum here, depending on whether the lack of update to x's
2534 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2535 * erratum or not. Note that changing setNamespace to update the list
2536 * of in-scope namespaces will change x.namespaceDeclarations().
2538 if (prefix
->empty()) {
2539 i
= XMLArrayFindMember(&decls
.array
, ns
, namespace_match
);
2540 if (i
!= XML_NOT_FOUND
)
2541 XMLArrayDelete(cx
, &decls
.array
, i
, JS_TRUE
);
2545 * In the spec, ancdecls has no name, but is always written out as
2546 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2547 * that union in ancdecls, any time we append a namespace strong
2548 * ref to decls, we must also append a weak ref to ancdecls. Order
2549 * matters here: code at label out: releases strong refs in decls.
2551 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns
) ||
2552 !XMLARRAY_APPEND(cx
, &decls
.array
, ns
)) {
2557 /* Format the element or point-tag into sb. */
2558 if (!sb
.append('<'))
2561 if (!prefix
->empty()) {
2562 if (!sb
.append(prefix
) || !sb
.append(':'))
2565 if (!sb
.append(xml
->name
->getQNameLocalName()))
2569 * Step 16 makes a union to avoid writing two loops in step 17, to share
2570 * common attribute value appending spec-code. We prefer two loops for
2571 * faster code and less data overhead.
2574 /* Step 17(b): append attributes. */
2576 JSXMLArrayCursor
cursor(&xml
->xml_attrs
);
2577 while (JSXML
*attr
= (JSXML
*) cursor
.getNext()) {
2578 if (!sb
.append(' '))
2580 ns2
= GetNamespace(cx
, attr
->name
, &ancdecls
.array
);
2584 /* 17(b)(ii): NULL means *undefined* here. */
2585 prefix
= ns2
->getNamePrefix();
2587 prefix
= GeneratePrefix(cx
, ns2
->getNameURI(), &ancdecls
.array
);
2591 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2592 ns2
= NewXMLNamespace(cx
, prefix
, ns2
->getNameURI(), JS_TRUE
);
2597 * In the spec, ancdecls has no name, but is always written out as
2598 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2599 * that union in ancdecls, any time we append a namespace strong
2600 * ref to decls, we must also append a weak ref to ancdecls. Order
2601 * matters here: code at label out: releases strong refs in decls.
2603 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns2
) ||
2604 !XMLARRAY_APPEND(cx
, &decls
.array
, ns2
)) {
2610 if (!prefix
->empty()) {
2611 if (!sb
.append(prefix
) || !sb
.append(':'))
2616 if (!sb
.append(attr
->name
->getQNameLocalName()))
2620 if (!AppendAttributeValue(cx
, sb
, attr
->xml_value
))
2625 /* Step 17(c): append XML namespace declarations. */
2627 JSXMLArrayCursor
cursor(&decls
.array
);
2628 while (JSObject
*ns3
= (JSObject
*) cursor
.getNext()) {
2629 JS_ASSERT(IsDeclared(ns3
));
2631 if (!sb
.append(" xmlns"))
2634 /* 17(c)(ii): NULL means *undefined* here. */
2635 prefix
= ns3
->getNamePrefix();
2637 prefix
= GeneratePrefix(cx
, ns3
->getNameURI(), &ancdecls
.array
);
2640 ns3
->setNamePrefix(prefix
);
2644 if (!prefix
->empty()) {
2645 if (!sb
.append(':') || !sb
.append(prefix
))
2650 if (!AppendAttributeValue(cx
, sb
, ns3
->getNameURI()))
2655 /* Step 18: handle point tags. */
2656 n
= xml
->xml_kids
.length
;
2658 if (!sb
.append("/>"))
2661 /* Steps 19 through 25: handle element content, and open the end-tag. */
2662 if (!sb
.append('>'))
2666 indentKids
= n
> 1 ||
2668 (kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
)) &&
2669 kid
->xml_class
!= JSXML_CLASS_TEXT
);
2672 if (pretty
&& indentKids
) {
2673 if (!GetUint32XMLSetting(cx
, js_prettyIndent_str
, &i
))
2675 nextIndentLevel
= indentLevel
+ i
;
2677 nextIndentLevel
= indentLevel
& TO_SOURCE_FLAG
;
2681 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
2682 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
2683 if (pretty
&& indentKids
) {
2684 if (!sb
.append('\n'))
2688 JSString
*kidstr
= XMLToXMLString(cx
, kid
, &ancdecls
.array
, nextIndentLevel
);
2692 if (!sb
.append(kidstr
))
2697 if (pretty
&& indentKids
) {
2698 if (!sb
.append('\n') ||
2699 !sb
.appendN(' ', indentLevel
& ~TO_SOURCE_FLAG
))
2702 if (!sb
.append("</"))
2706 prefix
= ns
->getNamePrefix();
2707 if (prefix
&& !prefix
->empty()) {
2708 if (!sb
.append(prefix
) || !sb
.append(':'))
2713 if (!sb
.append(xml
->name
->getQNameLocalName()) || !sb
.append('>'))
2717 str
= sb
.finishString();
2719 js_LeaveLocalRootScopeWithResult(cx
, str
);
2725 ToXMLString(JSContext
*cx
, jsval v
, uint32 toSourceFlag
)
2731 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
2732 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2733 JSMSG_BAD_XML_CONVERSION
,
2734 JSVAL_IS_NULL(v
) ? js_null_str
: js_undefined_str
);
2738 if (JSVAL_IS_BOOLEAN(v
) || JSVAL_IS_NUMBER(v
))
2739 return js_ValueToString(cx
, Valueify(v
));
2741 if (JSVAL_IS_STRING(v
)) {
2742 StringBuffer
sb(cx
);
2743 return EscapeElementValue(cx
, sb
, JSVAL_TO_STRING(v
), toSourceFlag
);
2746 obj
= JSVAL_TO_OBJECT(v
);
2747 if (!obj
->isXML()) {
2748 if (!DefaultValue(cx
, obj
, JSTYPE_STRING
, Valueify(&v
)))
2750 str
= js_ValueToString(cx
, Valueify(v
));
2753 StringBuffer
sb(cx
);
2754 return EscapeElementValue(cx
, sb
, str
, toSourceFlag
);
2757 /* Handle non-element cases in this switch, returning from each case. */
2758 xml
= (JSXML
*) obj
->getPrivate();
2759 return XMLToXMLString(cx
, xml
, NULL
, toSourceFlag
| 0);
2763 ToAttributeName(JSContext
*cx
, jsval v
)
2765 JSLinearString
*name
, *uri
, *prefix
;
2770 if (JSVAL_IS_STRING(v
)) {
2771 name
= JSVAL_TO_STRING(v
)->ensureLinear(cx
);
2774 uri
= prefix
= cx
->runtime
->emptyString
;
2776 if (JSVAL_IS_PRIMITIVE(v
)) {
2777 js_ReportValueError(cx
, JSMSG_BAD_XML_ATTR_NAME
,
2778 JSDVG_IGNORE_STACK
, Valueify(v
), NULL
);
2782 obj
= JSVAL_TO_OBJECT(v
);
2783 clasp
= obj
->getClass();
2784 if (clasp
== &js_AttributeNameClass
)
2787 if (clasp
== &js_QNameClass
) {
2789 uri
= qn
->getNameURI();
2790 prefix
= qn
->getNamePrefix();
2791 name
= qn
->getQNameLocalName();
2793 if (clasp
== &js_AnyNameClass
) {
2794 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
2796 JSString
*str
= js_ValueToString(cx
, Valueify(v
));
2799 name
= str
->ensureLinear(cx
);
2803 uri
= prefix
= cx
->runtime
->emptyString
;
2807 qn
= NewXMLAttributeName(cx
, uri
, prefix
, name
);
2814 ReportBadXMLName(JSContext
*cx
, const Value
&idval
)
2816 js_ReportValueError(cx
, JSMSG_BAD_XML_NAME
, JSDVG_IGNORE_STACK
, idval
, NULL
);
2820 IsFunctionQName(JSContext
*cx
, JSObject
*qn
, jsid
*funidp
)
2823 JSLinearString
*uri
;
2825 atom
= cx
->runtime
->atomState
.functionNamespaceURIAtom
;
2826 uri
= qn
->getNameURI();
2828 (uri
== ATOM_TO_STRING(atom
) ||
2829 EqualStrings(uri
, ATOM_TO_STRING(atom
)))) {
2830 return JS_ValueToId(cx
, STRING_TO_JSVAL(qn
->getQNameLocalName()), funidp
);
2832 *funidp
= JSID_VOID
;
2837 js_IsFunctionQName(JSContext
*cx
, JSObject
*obj
, jsid
*funidp
)
2839 if (obj
->getClass() == &js_QNameClass
)
2840 return IsFunctionQName(cx
, obj
, funidp
);
2841 *funidp
= JSID_VOID
;
2846 ToXMLName(JSContext
*cx
, jsval v
, jsid
*funidp
)
2848 JSAtom
*atomizedName
;
2854 if (JSVAL_IS_STRING(v
)) {
2855 name
= JSVAL_TO_STRING(v
);
2857 if (JSVAL_IS_PRIMITIVE(v
)) {
2858 ReportBadXMLName(cx
, Valueify(v
));
2862 obj
= JSVAL_TO_OBJECT(v
);
2863 clasp
= obj
->getClass();
2864 if (clasp
== &js_AttributeNameClass
|| clasp
== &js_QNameClass
)
2866 if (clasp
== &js_AnyNameClass
) {
2867 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
2870 name
= js_ValueToString(cx
, Valueify(v
));
2875 atomizedName
= js_AtomizeString(cx
, name
, 0);
2880 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
2882 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
2884 * First, _P_ should be _s_, to refer to the given string.
2886 * Second, why does ToXMLName applied to the string type throw TypeError
2887 * only for numeric literals without any leading or trailing whitespace?
2889 * If the idea is to reject uint32 property names, then the check needs to
2890 * be stricter, to exclude hexadecimal and floating point literals.
2892 if (js_IdIsIndex(ATOM_TO_JSID(atomizedName
), &index
))
2895 if (*atomizedName
->chars() == '@') {
2896 name
= js_NewDependentString(cx
, name
, 1, name
->length() - 1);
2899 *funidp
= JSID_VOID
;
2900 return ToAttributeName(cx
, STRING_TO_JSVAL(name
));
2904 v
= STRING_TO_JSVAL(name
);
2905 obj
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 1, Valueify(&v
));
2910 if (!IsFunctionQName(cx
, obj
, funidp
))
2915 JSAutoByteString bytes
;
2916 if (js_ValueToPrintable(cx
, StringValue(name
), &bytes
))
2917 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_XML_NAME
, bytes
.ptr());
2921 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
2923 AddInScopeNamespace(JSContext
*cx
, JSXML
*xml
, JSObject
*ns
)
2925 JSLinearString
*prefix
, *prefix2
;
2926 JSObject
*match
, *ns2
;
2929 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
2932 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
2933 prefix
= ns
->getNamePrefix();
2936 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
2937 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
2938 if (ns2
&& EqualStrings(ns2
->getNameURI(), ns
->getNameURI())) {
2943 if (!match
&& !XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_namespaces
, n
, ns
))
2946 if (prefix
->empty() && xml
->name
->getNameURI()->empty())
2949 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2952 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
2953 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
2954 if (ns2
&& (prefix2
= ns2
->getNamePrefix()) &&
2955 EqualStrings(prefix2
, prefix
)) {
2961 if (match
&& !EqualStrings(match
->getNameURI(), ns
->getNameURI())) {
2962 ns2
= XMLARRAY_DELETE(cx
, &xml
->xml_namespaces
, m
, JS_TRUE
,
2964 JS_ASSERT(ns2
== match
);
2965 match
->clearNamePrefix();
2966 if (!AddInScopeNamespace(cx
, xml
, match
))
2969 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
2973 /* OPTION: enforce that descendants have superset namespaces. */
2977 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
2979 Append(JSContext
*cx
, JSXML
*list
, JSXML
*xml
)
2984 JS_ASSERT(list
->xml_class
== JSXML_CLASS_LIST
);
2985 i
= list
->xml_kids
.length
;
2987 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
2988 list
->xml_target
= xml
->xml_target
;
2989 list
->xml_targetprop
= xml
->xml_targetprop
;
2990 n
= JSXML_LENGTH(xml
);
2992 if (!list
->xml_kids
.setCapacity(cx
, k
))
2994 for (j
= 0; j
< n
; j
++) {
2995 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, j
, JSXML
);
2997 XMLARRAY_SET_MEMBER(&list
->xml_kids
, i
+ j
, kid
);
3002 list
->xml_target
= xml
->parent
;
3003 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
3004 list
->xml_targetprop
= NULL
;
3006 list
->xml_targetprop
= xml
->name
;
3007 if (!XMLARRAY_ADD_MEMBER(cx
, &list
->xml_kids
, i
, xml
))
3012 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3014 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
);
3017 DeepCopy(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
, uintN flags
)
3021 /* Our caller may not be protecting newborns with a local root scope. */
3022 if (!js_EnterLocalRootScope(cx
))
3024 copy
= DeepCopyInLRS(cx
, xml
, flags
);
3027 /* Caller provided the object for this copy, hook 'em up. */
3028 obj
->setPrivate(copy
);
3030 } else if (!js_GetXMLObject(cx
, copy
)) {
3034 js_LeaveLocalRootScopeWithResult(cx
, copy
);
3039 * (i) We must be in a local root scope (InLRS).
3040 * (ii) parent must have a rooted object.
3041 * (iii) from's owning object must be locked if not thread-local.
3044 DeepCopySetInLRS(JSContext
*cx
, JSXMLArray
*from
, JSXMLArray
*to
, JSXML
*parent
,
3052 if (!to
->setCapacity(cx
, n
))
3055 JSXMLArrayCursor
cursor(from
);
3057 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
3058 if ((flags
& XSF_IGNORE_COMMENTS
) &&
3059 kid
->xml_class
== JSXML_CLASS_COMMENT
) {
3062 if ((flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
) &&
3063 kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
3066 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3067 (kid
->xml_flags
& XMLF_WHITESPACE_TEXT
)) {
3070 kid2
= DeepCopyInLRS(cx
, kid
, flags
);
3076 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3077 n
> 1 && kid2
->xml_class
== JSXML_CLASS_TEXT
) {
3078 str
= ChompXMLWhitespace(cx
, kid2
->xml_value
);
3083 kid2
->xml_value
= str
;
3086 XMLARRAY_SET_MEMBER(to
, j
, kid2
);
3088 if (parent
->xml_class
!= JSXML_CLASS_LIST
)
3089 kid2
->parent
= parent
;
3098 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
)
3106 JS_CHECK_RECURSION(cx
, return NULL
);
3108 copy
= js_NewXML(cx
, JSXMLClass(xml
->xml_class
));
3113 qn
= NewXMLQName(cx
, qn
->getNameURI(), qn
->getNamePrefix(), qn
->getQNameLocalName());
3120 copy
->xml_flags
= xml
->xml_flags
;
3122 if (JSXML_HAS_VALUE(xml
)) {
3123 copy
->xml_value
= xml
->xml_value
;
3126 ok
= DeepCopySetInLRS(cx
, &xml
->xml_kids
, ©
->xml_kids
, copy
, flags
);
3130 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3131 copy
->xml_target
= xml
->xml_target
;
3132 copy
->xml_targetprop
= xml
->xml_targetprop
;
3134 n
= xml
->xml_namespaces
.length
;
3135 ok
= copy
->xml_namespaces
.setCapacity(cx
, n
);
3138 for (i
= 0; i
< n
; i
++) {
3139 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3142 ns2
= NewXMLNamespace(cx
, ns
->getNamePrefix(), ns
->getNameURI(),
3145 copy
->xml_namespaces
.length
= i
;
3149 XMLARRAY_SET_MEMBER(©
->xml_namespaces
, i
, ns2
);
3152 ok
= DeepCopySetInLRS(cx
, &xml
->xml_attrs
, ©
->xml_attrs
, copy
,
3165 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3167 DeleteByIndex(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3171 if (JSXML_HAS_KIDS(xml
) && index
< xml
->xml_kids
.length
) {
3172 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3175 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3179 typedef JSBool (*JSXMLNameMatcher
)(JSObject
*nameqn
, JSXML
*xml
);
3182 MatchAttrName(JSObject
*nameqn
, JSXML
*attr
)
3184 JSObject
*attrqn
= attr
->name
;
3185 JSLinearString
*localName
= nameqn
->getQNameLocalName();
3186 JSLinearString
*uri
;
3188 return (IS_STAR(localName
) ||
3189 EqualStrings(attrqn
->getQNameLocalName(), localName
)) &&
3190 (!(uri
= nameqn
->getNameURI()) ||
3191 EqualStrings(attrqn
->getNameURI(), uri
));
3195 MatchElemName(JSObject
*nameqn
, JSXML
*elem
)
3197 JSLinearString
*localName
= nameqn
->getQNameLocalName();
3198 JSLinearString
*uri
;
3200 return (IS_STAR(localName
) ||
3201 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3202 EqualStrings(elem
->name
->getQNameLocalName(), localName
))) &&
3203 (!(uri
= nameqn
->getNameURI()) ||
3204 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3205 EqualStrings(elem
->name
->getNameURI(), uri
)));
3208 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3210 DescendantsHelper(JSContext
*cx
, JSXML
*xml
, JSObject
*nameqn
, JSXML
*list
)
3215 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
3217 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
&&
3218 nameqn
->getClass() == &js_AttributeNameClass
) {
3219 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
3220 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3221 if (attr
&& MatchAttrName(nameqn
, attr
)) {
3222 if (!Append(cx
, list
, attr
))
3228 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
3229 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3232 if (nameqn
->getClass() != &js_AttributeNameClass
&&
3233 MatchElemName(nameqn
, kid
)) {
3234 if (!Append(cx
, list
, kid
))
3237 if (!DescendantsHelper(cx
, kid
, nameqn
, list
))
3244 Descendants(JSContext
*cx
, JSXML
*xml
, jsval id
)
3253 nameqn
= ToXMLName(cx
, id
, &funid
);
3257 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
3260 list
= (JSXML
*) listobj
->getPrivate();
3261 if (!JSID_IS_VOID(funid
))
3265 * Protect nameqn's object and strings from GC by linking list to it
3266 * temporarily. The newborn GC root for the last allocated object
3267 * protects listobj, which protects list. Any other object allocations
3268 * occurring beneath DescendantsHelper use local roots.
3270 list
->name
= nameqn
;
3271 if (!js_EnterLocalRootScope(cx
))
3273 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3275 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
3276 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3277 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
3278 ok
= DescendantsHelper(cx
, kid
, nameqn
, list
);
3284 ok
= DescendantsHelper(cx
, xml
, nameqn
, list
);
3286 js_LeaveLocalRootScopeWithResult(cx
, list
);
3293 /* Recursive (JSXML *) parameterized version of Equals. */
3295 XMLEquals(JSContext
*cx
, JSXML
*xml
, JSXML
*vxml
, JSBool
*bp
)
3299 JSXML
*kid
, *vkid
, *attr
, *vattr
;
3300 JSObject
*xobj
, *vobj
;
3303 if (xml
->xml_class
!= vxml
->xml_class
) {
3304 if (xml
->xml_class
== JSXML_CLASS_LIST
&& xml
->xml_kids
.length
== 1) {
3305 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3309 if (vxml
->xml_class
== JSXML_CLASS_LIST
&& vxml
->xml_kids
.length
== 1) {
3310 vxml
= XMLARRAY_MEMBER(&vxml
->xml_kids
, 0, JSXML
);
3322 EqualStrings(qn
->getQNameLocalName(), vqn
->getQNameLocalName()) &&
3323 EqualStrings(qn
->getNameURI(), vqn
->getNameURI());
3330 if (JSXML_HAS_VALUE(xml
)) {
3331 if (!EqualStrings(cx
, xml
->xml_value
, vxml
->xml_value
, bp
))
3333 } else if (xml
->xml_kids
.length
!= vxml
->xml_kids
.length
) {
3337 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
3338 JSXMLArrayCursor
vcursor(&vxml
->xml_kids
);
3340 kid
= (JSXML
*) cursor
.getNext();
3341 vkid
= (JSXML
*) vcursor
.getNext();
3342 if (!kid
|| !vkid
) {
3343 *bp
= !kid
&& !vkid
;
3346 xobj
= js_GetXMLObject(cx
, kid
);
3347 vobj
= js_GetXMLObject(cx
, vkid
);
3348 if (!xobj
|| !vobj
||
3349 !js_TestXMLEquality(cx
, ObjectValue(*xobj
), ObjectValue(*vobj
), bp
))
3356 if (*bp
&& xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3357 n
= xml
->xml_attrs
.length
;
3358 if (n
!= vxml
->xml_attrs
.length
)
3360 for (i
= 0; *bp
&& i
< n
; i
++) {
3361 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3364 j
= XMLARRAY_FIND_MEMBER(&vxml
->xml_attrs
, attr
, attr_identity
);
3365 if (j
== XML_NOT_FOUND
) {
3369 vattr
= XMLARRAY_MEMBER(&vxml
->xml_attrs
, j
, JSXML
);
3372 if (!EqualStrings(cx
, attr
->xml_value
, vattr
->xml_value
, bp
))
3381 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3383 Equals(JSContext
*cx
, JSXML
*xml
, jsval v
, JSBool
*bp
)
3388 if (JSVAL_IS_PRIMITIVE(v
)) {
3390 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3391 if (xml
->xml_kids
.length
== 1) {
3392 vxml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3395 vobj
= js_GetXMLObject(cx
, vxml
);
3398 return js_TestXMLEquality(cx
, ObjectValue(*vobj
), Valueify(v
), bp
);
3400 if (JSVAL_IS_VOID(v
) && xml
->xml_kids
.length
== 0)
3404 vobj
= JSVAL_TO_OBJECT(v
);
3405 if (!vobj
->isXML()) {
3408 vxml
= (JSXML
*) vobj
->getPrivate();
3409 if (!XMLEquals(cx
, xml
, vxml
, bp
))
3417 CheckCycle(JSContext
*cx
, JSXML
*xml
, JSXML
*kid
)
3419 JS_ASSERT(kid
->xml_class
!= JSXML_CLASS_LIST
);
3423 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3424 JSMSG_CYCLIC_VALUE
, js_XML_str
);
3427 } while ((xml
= xml
->parent
) != NULL
);
3432 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3434 Insert(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3441 if (!JSXML_HAS_KIDS(xml
))
3446 if (!JSVAL_IS_PRIMITIVE(v
)) {
3447 vobj
= JSVAL_TO_OBJECT(v
);
3448 if (vobj
->isXML()) {
3449 vxml
= (JSXML
*) vobj
->getPrivate();
3450 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3451 n
= vxml
->xml_kids
.length
;
3454 for (j
= 0; j
< n
; j
++) {
3455 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3458 if (!CheckCycle(cx
, xml
, kid
))
3461 } else if (vxml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3462 /* OPTION: enforce that descendants have superset namespaces. */
3463 if (!CheckCycle(cx
, xml
, vxml
))
3469 str
= js_ValueToString(cx
, Valueify(v
));
3473 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3476 vxml
->xml_value
= str
;
3479 if (i
> xml
->xml_kids
.length
)
3480 i
= xml
->xml_kids
.length
;
3482 if (!XMLArrayInsert(cx
, &xml
->xml_kids
, i
, n
))
3485 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3486 for (j
= 0; j
< n
; j
++) {
3487 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3491 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
+ j
, kid
);
3493 /* OPTION: enforce that descendants have superset namespaces. */
3497 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
3503 IndexToId(JSContext
*cx
, uint32 index
, jsid
*idp
)
3508 if (index
<= JSID_INT_MAX
) {
3509 *idp
= INT_TO_JSID(index
);
3511 str
= js_NumberToString(cx
, (jsdouble
) index
);
3514 atom
= js_AtomizeString(cx
, str
, 0);
3517 *idp
= ATOM_TO_JSID(atom
);
3522 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3524 Replace(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3531 if (!JSXML_HAS_KIDS(xml
))
3536 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3537 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3539 n
= xml
->xml_kids
.length
;
3544 if (!JSVAL_IS_PRIMITIVE(v
)) {
3545 vobj
= JSVAL_TO_OBJECT(v
);
3547 vxml
= (JSXML
*) vobj
->getPrivate();
3550 switch (vxml
? JSXMLClass(vxml
->xml_class
) : JSXML_CLASS_LIMIT
) {
3551 case JSXML_CLASS_ELEMENT
:
3552 /* OPTION: enforce that descendants have superset namespaces. */
3553 if (!CheckCycle(cx
, xml
, vxml
))
3555 case JSXML_CLASS_COMMENT
:
3556 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
3557 case JSXML_CLASS_TEXT
:
3560 case JSXML_CLASS_LIST
:
3562 DeleteByIndex(cx
, xml
, i
);
3563 if (!Insert(cx
, xml
, i
, v
))
3568 str
= js_ValueToString(cx
, Valueify(v
));
3572 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3575 vxml
->xml_value
= str
;
3580 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3584 if (!XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_kids
, i
, vxml
))
3592 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3594 DeleteNamedProperty(JSContext
*cx
, JSXML
*xml
, JSObject
*nameqn
,
3598 uint32 index
, deleteCount
;
3600 JSXMLNameMatcher matcher
;
3602 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3603 array
= &xml
->xml_kids
;
3604 for (index
= 0; index
< array
->length
; index
++) {
3605 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3606 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
)
3607 DeleteNamedProperty(cx
, kid
, nameqn
, attributes
);
3609 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3611 array
= &xml
->xml_attrs
;
3612 matcher
= MatchAttrName
;
3614 array
= &xml
->xml_kids
;
3615 matcher
= MatchElemName
;
3618 for (index
= 0; index
< array
->length
; index
++) {
3619 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3620 if (kid
&& matcher(nameqn
, kid
)) {
3622 XMLArrayDelete(cx
, array
, index
, JS_FALSE
);
3624 } else if (deleteCount
!= 0) {
3625 XMLARRAY_SET_MEMBER(array
,
3626 index
- deleteCount
,
3627 array
->vector
[index
]);
3630 array
->length
-= deleteCount
;
3634 /* ECMA-357 9.2.1.3 index case. */
3636 DeleteListElement(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3638 JSXML
*kid
, *parent
;
3641 JS_ASSERT(xml
->xml_class
== JSXML_CLASS_LIST
);
3643 if (index
< xml
->xml_kids
.length
) {
3644 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3646 parent
= kid
->parent
;
3648 JS_ASSERT(parent
!= xml
);
3649 JS_ASSERT(JSXML_HAS_KIDS(parent
));
3651 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
3652 DeleteNamedProperty(cx
, parent
, kid
->name
, JS_TRUE
);
3654 kidIndex
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
,
3656 JS_ASSERT(kidIndex
!= XML_NOT_FOUND
);
3657 DeleteByIndex(cx
, parent
, kidIndex
);
3660 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3666 SyncInScopeNamespaces(JSContext
*cx
, JSXML
*xml
)
3668 JSXMLArray
*nsarray
;
3672 nsarray
= &xml
->xml_namespaces
;
3673 while ((xml
= xml
->parent
) != NULL
) {
3674 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3675 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3676 if (ns
&& !XMLARRAY_HAS_MEMBER(nsarray
, ns
, namespace_identity
)) {
3677 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
3686 GetNamedProperty(JSContext
*cx
, JSXML
*xml
, JSObject
* nameqn
, JSXML
*list
)
3689 JSXMLNameMatcher matcher
;
3692 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3693 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
3694 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
3695 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
3696 !GetNamedProperty(cx
, kid
, nameqn
, list
)) {
3700 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3701 attrs
= (nameqn
->getClass() == &js_AttributeNameClass
);
3703 array
= &xml
->xml_attrs
;
3704 matcher
= MatchAttrName
;
3706 array
= &xml
->xml_kids
;
3707 matcher
= MatchElemName
;
3710 JSXMLArrayCursor
cursor(array
);
3711 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
3712 if (matcher(nameqn
, kid
)) {
3714 kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
3715 !SyncInScopeNamespaces(cx
, kid
)) {
3718 if (!Append(cx
, list
, kid
))
3727 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3729 GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
3731 JSXML
*xml
, *list
, *kid
;
3733 JSObject
*kidobj
, *listobj
;
3737 xml
= (JSXML
*) GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
3741 if (js_IdIsIndex(id
, &index
)) {
3742 if (!JSXML_HAS_KIDS(xml
)) {
3743 *vp
= (index
== 0) ? OBJECT_TO_JSVAL(obj
) : JSVAL_VOID
;
3746 * ECMA-357 9.2.1.1 starts here.
3748 * Erratum: 9.2 is not completely clear that indexed properties
3749 * correspond to kids, but that's what it seems to say, and it's
3750 * what any sane user would want.
3752 if (index
< xml
->xml_kids
.length
) {
3753 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3758 kidobj
= js_GetXMLObject(cx
, kid
);
3762 *vp
= OBJECT_TO_JSVAL(kidobj
);
3771 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3773 nameqn
= ToXMLName(cx
, IdToJsval(id
), &funid
);
3776 if (!JSID_IS_VOID(funid
))
3777 return GetXMLFunction(cx
, obj
, funid
, vp
);
3779 jsval roots
[2] = { OBJECT_TO_JSVAL(nameqn
), JSVAL_NULL
};
3780 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), Valueify(roots
));
3782 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
3786 roots
[1] = OBJECT_TO_JSVAL(listobj
);
3788 list
= (JSXML
*) listobj
->getPrivate();
3789 if (!GetNamedProperty(cx
, xml
, nameqn
, list
))
3793 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
3794 * given list's [[TargetProperty]] to the property that is being
3795 * appended. This means that any use of the internal [[Get]]
3796 * property returns a list which, when used by e.g. [[Insert]]
3797 * duplicates the last element matched by id. See bug 336921.
3799 list
->xml_target
= xml
;
3800 list
->xml_targetprop
= nameqn
;
3801 *vp
= OBJECT_TO_JSVAL(listobj
);
3806 CopyOnWrite(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
)
3808 JS_ASSERT(xml
->object
!= obj
);
3810 xml
= DeepCopy(cx
, xml
, obj
, 0);
3814 JS_ASSERT(xml
->object
== obj
);
3818 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
3819 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
3822 KidToString(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3827 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3829 return cx
->runtime
->emptyString
;
3830 kidobj
= js_GetXMLObject(cx
, kid
);
3833 return js_ValueToString(cx
, ObjectValue(*kidobj
));
3836 /* Forward declared -- its implementation uses other statics that call it. */
3838 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
);
3840 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
3842 PutProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
3844 JSBool ok
, primitiveAssign
;
3845 enum { OBJ_ROOT
, ID_ROOT
, VAL_ROOT
};
3846 JSXML
*xml
, *vxml
, *rxml
, *kid
, *attr
, *parent
, *copy
, *kid2
, *match
;
3847 JSObject
*vobj
, *nameobj
, *attrobj
, *parentobj
, *kidobj
, *copyobj
;
3848 JSObject
*targetprop
, *nameqn
, *attrqn
;
3849 uint32 index
, i
, j
, k
, n
, q
, matchIndex
;
3850 jsval attrval
, nsval
;
3854 xml
= (JSXML
*) GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
3858 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
3862 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
3864 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
3865 vobj
= JSVAL_TO_OBJECT(*vp
);
3867 vxml
= (JSXML
*) vobj
->getPrivate();
3870 ok
= js_EnterLocalRootScope(cx
);
3874 MUST_FLOW_THROUGH("out");
3876 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
3877 roots
[ID_ROOT
] = IdToJsval(id
);
3878 roots
[VAL_ROOT
] = *vp
;
3879 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), Valueify(roots
));
3881 if (js_IdIsIndex(id
, &index
)) {
3882 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
3883 /* See NOTE in spec: this variation is reserved for future use. */
3884 ReportBadXMLName(cx
, IdToValue(id
));
3889 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
3894 if (xml
->xml_target
) {
3895 ok
= ResolveValue(cx
, xml
->xml_target
, &rxml
);
3900 JS_ASSERT(rxml
->object
);
3906 if (index
>= xml
->xml_kids
.length
) {
3909 if (rxml
->xml_class
== JSXML_CLASS_LIST
) {
3910 if (rxml
->xml_kids
.length
!= 1)
3912 rxml
= XMLARRAY_MEMBER(&rxml
->xml_kids
, 0, JSXML
);
3915 ok
= js_GetXMLObject(cx
, rxml
) != NULL
;
3921 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
3922 * _y.[[Parent]] = r_ where _r_ is the result of
3923 * [[ResolveValue]] called on _x.[[TargetObject]] in
3924 * 2(a)(i). This can result in text parenting text:
3926 * var MYXML = new XML();
3927 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
3929 * (testcase from Werner Sharp <wsharp@macromedia.com>).
3931 * To match insertChildAfter, insertChildBefore,
3932 * prependChild, and setChildren, we should silently
3933 * do nothing in this case.
3935 if (!JSXML_HAS_KIDS(rxml
))
3939 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
3940 targetprop
= xml
->xml_targetprop
;
3941 if (!targetprop
|| IS_STAR(targetprop
->getQNameLocalName())) {
3942 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
3943 kid
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3947 nameobj
= targetprop
;
3948 if (nameobj
->getClass() == &js_AttributeNameClass
) {
3951 * Note that rxml can't be null here, because target
3952 * and targetprop are non-null.
3954 ok
= GetProperty(cx
, rxml
->object
, id
, &attrval
);
3957 if (JSVAL_IS_PRIMITIVE(attrval
)) /* no such attribute */
3959 attrobj
= JSVAL_TO_OBJECT(attrval
);
3960 attr
= (JSXML
*) attrobj
->getPrivate();
3961 if (JSXML_LENGTH(attr
) != 0)
3964 kid
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
3967 kid
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
3972 /* An important bit of 2(c)(ii). */
3973 kid
->name
= targetprop
;
3976 /* Final important bit of 2(c)(ii). */
3980 i
= xml
->xml_kids
.length
;
3981 if (kid
->xml_class
!= JSXML_CLASS_ATTRIBUTE
) {
3983 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
3984 * y.[[Parent]] is here called kid->parent, which we know
3985 * from 2(c)(ii) is _r_, here called rxml. So let's just
3986 * test that! Erratum, the spec should be simpler here.
3989 JS_ASSERT(JSXML_HAS_KIDS(rxml
));
3990 n
= rxml
->xml_kids
.length
;
3992 if (n
!= 0 && i
!= 0) {
3993 for (n
= j
, j
= 0; j
< n
; j
++) {
3994 if (rxml
->xml_kids
.vector
[j
] ==
3995 xml
->xml_kids
.vector
[i
-1]) {
4001 kidobj
= js_GetXMLObject(cx
, kid
);
4004 ok
= Insert(cx
, rxml
, j
+ 1, OBJECT_TO_JSVAL(kidobj
));
4011 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4012 * typo for [[TargetProperty]].
4015 kid
->name
= (vxml
->xml_class
== JSXML_CLASS_LIST
)
4016 ? vxml
->xml_targetprop
4022 ok
= Append(cx
, xml
, kid
);
4029 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4030 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4031 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4034 roots
[VAL_ROOT
] = *vp
;
4038 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
4041 parent
= kid
->parent
;
4042 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4043 nameobj
= kid
->name
;
4044 if (nameobj
->getClass() != &js_AttributeNameClass
) {
4045 nameobj
= NewXMLAttributeName(cx
, nameobj
->getNameURI(), nameobj
->getNamePrefix(),
4046 nameobj
->getQNameLocalName());
4050 id
= OBJECT_TO_JSID(nameobj
);
4054 parentobj
= js_GetXMLObject(cx
, parent
);
4057 ok
= PutProperty(cx
, parentobj
, id
, strict
, vp
);
4062 ok
= GetProperty(cx
, parentobj
, id
, vp
);
4065 attr
= (JSXML
*) JSVAL_TO_OBJECT(*vp
)->getPrivate();
4067 /* 2(e)(iii) - the length check comes from the bug 375406. */
4068 if (attr
->xml_kids
.length
!= 0)
4069 xml
->xml_kids
.vector
[i
] = attr
->xml_kids
.vector
[0];
4074 else if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4078 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4079 * if we do that we never change the parent of each child in the
4080 * list. Since [[Put]] when called on an XML object deeply copies
4081 * the provided list _V_, we also do so here. Perhaps the shallow
4082 * copy was a misguided optimization?
4084 copy
= DeepCopyInLRS(cx
, vxml
, 0);
4087 copyobj
= js_GetXMLObject(cx
, copy
);
4091 JS_ASSERT(parent
!= xml
);
4093 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4094 JS_ASSERT(q
!= XML_NOT_FOUND
);
4095 ok
= Replace(cx
, parent
, q
, OBJECT_TO_JSVAL(copyobj
));
4100 /* Erratum: this loop in the spec is useless. */
4101 for (j
= 0, n
= copy
->xml_kids
.length
; j
< n
; j
++) {
4102 kid2
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
+ j
, JSXML
);
4103 JS_ASSERT(XMLARRAY_MEMBER(©
->xml_kids
, j
, JSXML
)
4111 * Erratum: notice the unhandled zero-length V basis case and
4112 * the off-by-one errors for the n != 0 cases in the spec.
4114 n
= copy
->xml_kids
.length
;
4116 XMLArrayDelete(cx
, &xml
->xml_kids
, i
, JS_TRUE
);
4118 ok
= XMLArrayInsert(cx
, &xml
->xml_kids
, i
+ 1, n
- 1);
4122 for (j
= 0; j
< n
; j
++)
4123 xml
->xml_kids
.vector
[i
+ j
] = copy
->xml_kids
.vector
[j
];
4128 else if (vxml
|| JSXML_HAS_VALUE(kid
)) {
4130 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4131 JS_ASSERT(q
!= XML_NOT_FOUND
);
4132 ok
= Replace(cx
, parent
, q
, *vp
);
4136 vxml
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
, JSXML
);
4139 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4144 * Erratum: _V_ may not be of type XML, but all index-named
4145 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4146 * according to 9.2.1.1 Overview and other places in the spec.
4148 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4149 * or an XML/XMLList object. If *vp is a string, call ToXML
4150 * on it to satisfy the constraint.
4153 JS_ASSERT(JSVAL_IS_STRING(*vp
));
4154 vobj
= ToXML(cx
, *vp
);
4157 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vobj
);
4158 vxml
= (JSXML
*) vobj
->getPrivate();
4160 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
4165 kidobj
= js_GetXMLObject(cx
, kid
);
4168 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.starAtom
);
4169 ok
= PutProperty(cx
, kidobj
, id
, strict
, vp
);
4175 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4177 nameqn
= ToXMLName(cx
, IdToJsval(id
), &funid
);
4180 if (!JSID_IS_VOID(funid
)) {
4181 ok
= js_SetProperty(cx
, obj
, funid
, Valueify(vp
), false);
4185 roots
[ID_ROOT
] = OBJECT_TO_JSVAL(nameobj
);
4187 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4189 * Step 3 of 9.2.1.2.
4190 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4191 * or an r with r.[[Length]] != 1, throw TypeError.
4193 n
= JSXML_LENGTH(xml
);
4197 ok
= ResolveValue(cx
, xml
, &rxml
);
4200 if (!rxml
|| JSXML_LENGTH(rxml
) != 1)
4202 ok
= Append(cx
, xml
, rxml
);
4206 JS_ASSERT(JSXML_LENGTH(xml
) == 1);
4207 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
4210 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
4211 obj
= js_GetXMLObject(cx
, xml
);
4214 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
4216 /* FALL THROUGH to non-list case */
4221 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4222 * effort in ToString or [[DeepCopy]].
4225 if (JSXML_HAS_VALUE(xml
))
4229 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4230 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4231 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4235 rxml
= DeepCopyInLRS(cx
, vxml
, 0);
4236 if (!rxml
|| !js_GetXMLObject(cx
, rxml
))
4239 *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4241 roots
[VAL_ROOT
] = *vp
;
4245 * Erratum: why is this done here, so early? use is way later....
4247 ok
= js_GetDefaultXMLNamespace(cx
, &nsval
);
4251 if (nameobj
->getClass() == &js_AttributeNameClass
) {
4253 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)))
4257 if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4258 n
= vxml
->xml_kids
.length
;
4260 *vp
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4262 JSString
*left
= KidToString(cx
, vxml
, 0);
4266 JSString
*space
= cx
->runtime
->atomState
.spaceAtom
;
4267 for (i
= 1; i
< n
; i
++) {
4268 left
= js_ConcatStrings(cx
, left
, space
);
4271 JSString
*right
= KidToString(cx
, vxml
, i
);
4274 left
= js_ConcatStrings(cx
, left
, right
);
4279 roots
[VAL_ROOT
] = *vp
= STRING_TO_JSVAL(left
);
4282 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4285 roots
[VAL_ROOT
] = *vp
;
4290 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
4291 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
4294 attrqn
= attr
->name
;
4295 if (EqualStrings(attrqn
->getQNameLocalName(), nameqn
->getQNameLocalName())) {
4296 JSLinearString
*uri
= nameqn
->getNameURI();
4297 if (!uri
|| EqualStrings(attrqn
->getNameURI(), uri
)) {
4301 DeleteNamedProperty(cx
, xml
, attrqn
, JS_TRUE
);
4312 JSLinearString
*uri
= nameqn
->getNameURI();
4313 JSLinearString
*left
, *right
;
4315 left
= right
= cx
->runtime
->emptyString
;
4318 right
= nameqn
->getNamePrefix();
4320 nameqn
= NewXMLQName(cx
, left
, right
, nameqn
->getQNameLocalName());
4325 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
4329 attr
->name
= nameqn
;
4332 ok
= XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_attrs
, n
, attr
);
4337 ns
= GetNamespace(cx
, nameqn
, NULL
);
4340 ok
= AddInScopeNamespace(cx
, xml
, ns
);
4346 attr
->xml_value
= JSVAL_TO_STRING(*vp
);
4351 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)) &&
4352 !IS_STAR(nameqn
->getQNameLocalName())) {
4358 primitiveAssign
= !vxml
&& !IS_STAR(nameqn
->getQNameLocalName());
4361 k
= n
= xml
->xml_kids
.length
;
4362 matchIndex
= XML_NOT_FOUND
;
4366 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, k
, JSXML
);
4367 if (kid
&& MatchElemName(nameqn
, kid
)) {
4368 if (matchIndex
!= XML_NOT_FOUND
)
4369 DeleteByIndex(cx
, xml
, matchIndex
);
4376 * Erratum: ECMA-357 specified child insertion inconsistently:
4377 * insertChildBefore and insertChildAfter insert an arbitrary XML
4378 * instance, and therefore can create cycles, but appendChild as
4379 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4380 * its argument. But the "Semantics" in 13.4.4.3 do not include
4381 * any [[DeepCopy]] call.
4383 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4384 * required adding cycle detection, and allowing duplicate kids to
4385 * be created (see comment 6 in the bug). Allowing duplicate kid
4386 * references means the loop above will delete all but the lowest
4387 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4388 * parent. Thus the need to restore parent here. This is covered
4389 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4392 JS_ASSERT(kid2
->parent
== xml
|| !kid2
->parent
);
4398 if (matchIndex
== XML_NOT_FOUND
) {
4403 if (primitiveAssign
) {
4404 JSLinearString
*uri
= nameqn
->getNameURI();
4405 JSLinearString
*left
, *right
;
4407 ns
= JSVAL_TO_OBJECT(nsval
);
4408 left
= ns
->getNameURI();
4409 right
= ns
->getNamePrefix();
4412 right
= nameqn
->getNamePrefix();
4414 nameqn
= NewXMLQName(cx
, left
, right
, nameqn
->getQNameLocalName());
4419 vobj
= js_NewXMLObject(cx
, JSXML_CLASS_ELEMENT
);
4422 vxml
= (JSXML
*) vobj
->getPrivate();
4424 vxml
->name
= nameqn
;
4427 ns
= GetNamespace(cx
, nameqn
, NULL
);
4430 ok
= Replace(cx
, xml
, matchIndex
, OBJECT_TO_JSVAL(vobj
));
4433 ok
= AddInScopeNamespace(cx
, vxml
, ns
);
4440 if (primitiveAssign
) {
4441 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
4442 cursor
.index
= matchIndex
;
4443 kid
= (JSXML
*) cursor
.getCurrent();
4444 if (JSXML_HAS_KIDS(kid
)) {
4445 kid
->xml_kids
.finish(cx
);
4446 kid
->xml_kids
.init();
4447 ok
= kid
->xml_kids
.setCapacity(cx
, 1);
4451 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4453 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4454 if (ok
&& !JSVAL_TO_STRING(*vp
)->empty()) {
4455 roots
[VAL_ROOT
] = *vp
;
4456 if ((JSXML
*) cursor
.getCurrent() == kid
)
4457 ok
= Replace(cx
, kid
, 0, *vp
);
4462 ok
= Replace(cx
, xml
, matchIndex
, *vp
);
4467 js_LeaveLocalRootScope(cx
);
4472 JSAutoByteString bytes
;
4473 if (js_ValueToPrintable(cx
, IdToValue(id
), &bytes
))
4474 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_XMLLIST_PUT
, bytes
.ptr());
4481 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4483 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
)
4485 JSXML
*target
, *base
;
4486 JSObject
*targetprop
;
4490 if (list
->xml_class
!= JSXML_CLASS_LIST
|| list
->xml_kids
.length
!= 0) {
4491 if (!js_GetXMLObject(cx
, list
))
4497 target
= list
->xml_target
;
4498 targetprop
= list
->xml_targetprop
;
4499 if (!target
|| !targetprop
|| IS_STAR(targetprop
->getQNameLocalName())) {
4504 if (targetprop
->getClass() == &js_AttributeNameClass
) {
4509 if (!ResolveValue(cx
, target
, &base
))
4515 if (!js_GetXMLObject(cx
, base
))
4518 id
= OBJECT_TO_JSID(targetprop
);
4519 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4521 target
= (JSXML
*) JSVAL_TO_OBJECT(tv
)->getPrivate();
4523 if (JSXML_LENGTH(target
) == 0) {
4524 if (base
->xml_class
== JSXML_CLASS_LIST
&& JSXML_LENGTH(base
) > 1) {
4528 tv
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4529 if (!PutProperty(cx
, base
->object
, id
, false, &tv
))
4531 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4533 target
= (JSXML
*) JSVAL_TO_OBJECT(tv
)->getPrivate();
4541 HasNamedProperty(JSXML
*xml
, JSObject
*nameqn
)
4545 JSXMLNameMatcher matcher
;
4548 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4550 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
4551 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
4552 found
= HasNamedProperty(kid
, nameqn
);
4559 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
4560 if (nameqn
->getClass() == &js_AttributeNameClass
) {
4561 array
= &xml
->xml_attrs
;
4562 matcher
= MatchAttrName
;
4564 array
= &xml
->xml_kids
;
4565 matcher
= MatchElemName
;
4567 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
4568 JSXML
*kid
= XMLARRAY_MEMBER(array
, i
, JSXML
);
4569 if (kid
&& matcher(nameqn
, kid
))
4578 HasIndexedProperty(JSXML
*xml
, uint32 i
)
4580 if (xml
->xml_class
== JSXML_CLASS_LIST
)
4581 return i
< JSXML_LENGTH(xml
);
4583 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
)
4590 HasSimpleContent(JSXML
*xml
);
4593 HasFunctionProperty(JSContext
*cx
, JSObject
*obj
, jsid funid
, JSBool
*found
)
4599 JS_ASSERT(obj
->getClass() == &js_XMLClass
);
4601 if (!js_LookupProperty(cx
, obj
, funid
, &pobj
, &prop
))
4604 xml
= (JSXML
*) obj
->getPrivate();
4605 if (HasSimpleContent(xml
)) {
4606 AutoObjectRooter
tvr(cx
);
4609 * Search in String.prototype to set found whenever
4610 * GetXMLFunction returns existing function.
4612 if (!js_GetClassPrototype(cx
, NULL
, JSProto_String
, tvr
.addr()))
4615 JS_ASSERT(tvr
.object());
4616 if (!js_LookupProperty(cx
, tvr
.object(), funid
, &pobj
, &prop
))
4620 *found
= (prop
!= NULL
);
4624 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4626 HasProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, JSBool
*found
)
4634 xml
= (JSXML
*) obj
->getPrivate();
4635 if (!js_IdValIsIndex(cx
, id
, &i
, &isIndex
))
4639 *found
= HasIndexedProperty(xml
, i
);
4641 qn
= ToXMLName(cx
, id
, &funid
);
4644 if (!JSID_IS_VOID(funid
)) {
4645 if (!HasFunctionProperty(cx
, obj
, funid
, found
))
4648 *found
= HasNamedProperty(xml
, qn
);
4655 xml_finalize(JSContext
*cx
, JSObject
*obj
)
4657 JSXML
*xml
= (JSXML
*) obj
->getPrivate();
4660 if (xml
->object
== obj
)
4665 xml_trace_vector(JSTracer
*trc
, JSXML
**vec
, uint32 len
)
4670 for (i
= 0; i
< len
; i
++) {
4673 JS_SET_TRACING_INDEX(trc
, "xml_vector", i
);
4680 * XML objects are native. Thus xml_lookupProperty must return a valid
4681 * Shape pointer parameter via *propp to signify "property found". Since the
4682 * only call to xml_lookupProperty is via JSObject::lookupProperty, and then
4683 * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
4684 * JSOP_IN case in the interpreter, the only time we add a Shape here is when
4685 * an unqualified name is being accessed or when "name in xml" is called.
4687 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4688 * giving it an shape with (getter, setter) == (GetProperty, PutProperty).
4690 * NB: xml_deleteProperty must take care to remove any property added here.
4692 * FIXME This clashes with the function namespace implementation which also
4693 * uses native properties. Effectively after xml_lookupProperty any property
4694 * stored previously using assignments to xml.function::name will be removed.
4695 * We partially workaround the problem in GetXMLFunction. There we take
4696 * advantage of the fact that typically function:: is used to access the
4697 * functions from XML.prototype. So when js_GetProperty returns a non-function
4698 * property, we assume that it represents the result of GetProperty setter
4699 * hiding the function and use an extra prototype chain lookup to recover it.
4700 * For a proper solution see bug 355257.
4703 xml_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4712 xml
= (JSXML
*) obj
->getPrivate();
4713 if (js_IdIsIndex(id
, &i
)) {
4714 found
= HasIndexedProperty(xml
, i
);
4716 qn
= ToXMLName(cx
, IdToJsval(id
), &funid
);
4719 if (!JSID_IS_VOID(funid
))
4720 return js_LookupProperty(cx
, obj
, funid
, objp
, propp
);
4721 found
= HasNamedProperty(xml
, qn
);
4727 const Shape
*shape
=
4728 js_AddNativeProperty(cx
, obj
, id
,
4729 Valueify(GetProperty
), Valueify(PutProperty
),
4730 SHAPE_INVALID_SLOT
, JSPROP_ENUMERATE
,
4736 *propp
= (JSProperty
*) shape
;
4742 xml_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*v
,
4743 PropertyOp getter
, StrictPropertyOp setter
, uintN attrs
)
4745 if (IsFunctionObject(*v
) || getter
|| setter
||
4746 (attrs
& JSPROP_ENUMERATE
) == 0 ||
4747 (attrs
& (JSPROP_READONLY
| JSPROP_PERMANENT
| JSPROP_SHARED
))) {
4748 return js_DefineProperty(cx
, obj
, id
, v
, getter
, setter
, attrs
);
4751 jsval tmp
= Jsvalify(*v
);
4752 return PutProperty(cx
, obj
, id
, false, &tmp
);
4756 xml_getProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
4758 if (JSID_IS_DEFAULT_XML_NAMESPACE(id
)) {
4763 return GetProperty(cx
, obj
, id
, Jsvalify(vp
));
4767 xml_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
4769 return PutProperty(cx
, obj
, id
, strict
, Jsvalify(vp
));
4773 xml_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
4776 if (!HasProperty(cx
, obj
, IdToJsval(id
), &found
))
4779 *attrsp
= found
? JSPROP_ENUMERATE
: 0;
4784 xml_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
4787 if (!HasProperty(cx
, obj
, IdToJsval(id
), &found
))
4791 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4792 JSMSG_CANT_SET_XML_ATTRS
);
4799 xml_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
4807 idval
= IdToJsval(id
);
4808 xml
= (JSXML
*) obj
->getPrivate();
4809 if (js_IdIsIndex(id
, &index
)) {
4810 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
4811 /* See NOTE in spec: this variation is reserved for future use. */
4812 ReportBadXMLName(cx
, IdToValue(id
));
4816 /* ECMA-357 9.2.1.3. */
4817 DeleteListElement(cx
, xml
, index
);
4819 nameqn
= ToXMLName(cx
, idval
, &funid
);
4822 if (!JSID_IS_VOID(funid
))
4823 return js_DeleteProperty(cx
, obj
, funid
, rval
, false);
4825 DeleteNamedProperty(cx
, xml
, nameqn
,
4826 nameqn
->getClass() == &js_AttributeNameClass
);
4830 * If this object has its own (mutable) scope, then we may have added a
4831 * property to the scope in xml_lookupProperty for it to return to mean
4832 * "found" and to provide a handle for access operations to call the
4833 * property's getter or setter. But now it's time to remove any such
4834 * property, to purge the property cache and remove the scope entry.
4836 if (!obj
->nativeEmpty() && !js_DeleteProperty(cx
, obj
, id
, rval
, false))
4839 rval
->setBoolean(true);
4844 xml_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
4846 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, rval
);
4850 xml_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
4853 uint32 length
, index
;
4854 JSXMLArrayCursor
*cursor
;
4856 xml
= (JSXML
*)obj
->getPrivate();
4857 length
= JSXML_LENGTH(xml
);
4860 case JSENUMERATE_INIT
:
4861 case JSENUMERATE_INIT_ALL
:
4863 statep
->setInt32(0);
4865 cursor
= cx
->create
<JSXMLArrayCursor
>(&xml
->xml_kids
);
4868 statep
->setPrivate(cursor
);
4871 *idp
= INT_TO_JSID(length
);
4874 case JSENUMERATE_NEXT
:
4875 if (statep
->isInt32(0)) {
4879 cursor
= (JSXMLArrayCursor
*) statep
->toPrivate();
4880 if (cursor
&& cursor
->array
&& (index
= cursor
->index
) < length
) {
4881 *idp
= INT_TO_JSID(index
);
4882 cursor
->index
= index
+ 1;
4887 case JSENUMERATE_DESTROY
:
4888 if (!statep
->isInt32(0)) {
4889 cursor
= (JSXMLArrayCursor
*) statep
->toPrivate();
4891 cx
->destroy(cursor
);
4900 xml_typeOf(JSContext
*cx
, JSObject
*obj
)
4906 xml_hasInstance(JSContext
*cx
, JSObject
*obj
, const Value
*, JSBool
*bp
)
4912 xml_trace(JSTracer
*trc
, JSObject
*obj
)
4914 JSXML
*xml
= (JSXML
*) obj
->getPrivate();
4916 JS_CALL_TRACER(trc
, xml
, JSTRACE_XML
, "private");
4920 xml_fix(JSContext
*cx
, JSObject
*obj
, bool *success
, AutoIdVector
*props
)
4922 JS_ASSERT(obj
->isExtensible());
4928 xml_clear(JSContext
*cx
, JSObject
*obj
)
4933 HasSimpleContent(JSXML
*xml
)
4940 switch (xml
->xml_class
) {
4941 case JSXML_CLASS_COMMENT
:
4942 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
4944 case JSXML_CLASS_LIST
:
4945 if (xml
->xml_kids
.length
== 0)
4947 if (xml
->xml_kids
.length
== 1) {
4948 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
4957 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
4958 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
4959 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
4969 * 11.2.2.1 Step 3(d) onward.
4972 js_GetXMLMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
4974 JS_ASSERT(JS_InstanceOf(cx
, obj
, Jsvalify(&js_XMLClass
), NULL
));
4976 if (JSID_IS_OBJECT(id
)) {
4979 if (!js_IsFunctionQName(cx
, JSID_TO_OBJECT(id
), &funid
))
4981 if (!JSID_IS_VOID(funid
))
4986 * As our callers have a bad habit of passing a pointer to an unrooted
4987 * local value as vp, we use a proper root here.
4989 AutoValueRooter
tvr(cx
);
4990 JSBool ok
= GetXMLFunction(cx
, obj
, id
, Jsvalify(tvr
.addr()));
4996 js_TestXMLEquality(JSContext
*cx
, const Value
&v1
, const Value
&v2
, JSBool
*bp
)
5001 JSString
*str
, *vstr
;
5006 if (v1
.isObject() && v1
.toObject().isXML()) {
5007 obj
= &v1
.toObject();
5011 obj
= &v2
.toObject();
5014 JS_ASSERT(JS_InstanceOf(cx
, obj
, Jsvalify(&js_XMLClass
), NULL
));
5016 xml
= (JSXML
*) obj
->getPrivate();
5018 if (!JSVAL_IS_PRIMITIVE(v
)) {
5019 vobj
= JSVAL_TO_OBJECT(v
);
5021 vxml
= (JSXML
*) vobj
->getPrivate();
5024 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5025 ok
= Equals(cx
, xml
, v
, bp
);
5027 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
5028 ok
= Equals(cx
, vxml
, OBJECT_TO_JSVAL(obj
), bp
);
5030 if (((xml
->xml_class
== JSXML_CLASS_TEXT
||
5031 xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5032 HasSimpleContent(vxml
)) ||
5033 ((vxml
->xml_class
== JSXML_CLASS_TEXT
||
5034 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5035 HasSimpleContent(xml
))) {
5036 ok
= js_EnterLocalRootScope(cx
);
5038 ok
= (str
= js_ValueToString(cx
, ObjectValue(*obj
))) &&
5039 (vstr
= js_ValueToString(cx
, Valueify(v
)));
5041 ok
= EqualStrings(cx
, str
, vstr
, bp
);
5042 js_LeaveLocalRootScope(cx
);
5045 ok
= XMLEquals(cx
, xml
, vxml
, bp
);
5049 ok
= js_EnterLocalRootScope(cx
);
5051 if (HasSimpleContent(xml
)) {
5052 ok
= (str
= js_ValueToString(cx
, ObjectValue(*obj
))) &&
5053 (vstr
= js_ValueToString(cx
, Valueify(v
)));
5055 ok
= EqualStrings(cx
, str
, vstr
, bp
);
5056 } else if (JSVAL_IS_STRING(v
) || JSVAL_IS_NUMBER(v
)) {
5057 str
= js_ValueToString(cx
, ObjectValue(*obj
));
5060 } else if (JSVAL_IS_STRING(v
)) {
5061 ok
= EqualStrings(cx
, str
, JSVAL_TO_STRING(v
), bp
);
5063 ok
= JS_ValueToNumber(cx
, STRING_TO_JSVAL(str
), &d
);
5065 d2
= JSVAL_IS_INT(v
) ? JSVAL_TO_INT(v
)
5066 : JSVAL_TO_DOUBLE(v
);
5067 *bp
= JSDOUBLE_COMPARE(d
, ==, d2
, JS_FALSE
);
5073 js_LeaveLocalRootScope(cx
);
5080 js_ConcatenateXML(JSContext
*cx
, JSObject
*obj
, JSObject
*robj
, Value
*vp
)
5084 JSXML
*list
, *lxml
, *rxml
;
5086 JS_ASSERT(JS_InstanceOf(cx
, obj
, Jsvalify(&js_XMLClass
), NULL
));
5087 ok
= js_EnterLocalRootScope(cx
);
5091 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5097 list
= (JSXML
*) listobj
->getPrivate();
5098 lxml
= (JSXML
*) obj
->getPrivate();
5099 ok
= Append(cx
, list
, lxml
);
5103 JS_ASSERT(robj
->isXML());
5104 rxml
= (JSXML
*) robj
->getPrivate();
5105 ok
= Append(cx
, list
, rxml
);
5109 vp
->setObject(*listobj
);
5111 js_LeaveLocalRootScopeWithResult(cx
, *vp
);
5115 JS_FRIEND_DATA(Class
) js_XMLClass
= {
5117 JSCLASS_HAS_PRIVATE
| JSCLASS_MARK_IS_TRACE
|
5118 JSCLASS_HAS_CACHED_PROTO(JSProto_XML
),
5119 PropertyStub
, /* addProperty */
5120 PropertyStub
, /* delProperty */
5121 PropertyStub
, /* getProperty */
5122 StrictPropertyStub
, /* setProperty */
5127 NULL
, /* reserved0 */
5128 NULL
, /* checkAccess */
5130 NULL
, /* construct */
5131 NULL
, /* xdrObject */
5133 JS_CLASS_TRACE(xml_trace
),
5147 NULL
, /* thisObject */
5153 StartNonListXMLMethod(JSContext
*cx
, jsval
*vp
, JSObject
**objp
)
5159 JS_ASSERT(VALUE_IS_FUNCTION(cx
, *vp
));
5161 *objp
= ToObject(cx
, Valueify(&vp
[1]));
5164 xml
= (JSXML
*) GetInstancePrivate(cx
, *objp
, &js_XMLClass
, Valueify(vp
+ 2));
5165 if (!xml
|| xml
->xml_class
!= JSXML_CLASS_LIST
)
5168 if (xml
->xml_kids
.length
== 1) {
5169 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5171 *objp
= js_GetXMLObject(cx
, xml
);
5174 vp
[1] = OBJECT_TO_JSVAL(*objp
);
5179 fun
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
));
5180 JS_snprintf(numBuf
, sizeof numBuf
, "%u", xml
->xml_kids
.length
);
5181 JSAutoByteString funNameBytes
;
5182 if (const char *funName
= GetFunctionNameBytes(cx
, fun
, &funNameBytes
)) {
5183 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NON_LIST_XML_METHOD
,
5189 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5190 #define XML_METHOD_PROLOG \
5191 JSObject *obj = ToObject(cx, Valueify(&vp[1])); \
5194 JSXML *xml = (JSXML *)GetInstancePrivate(cx, obj, &js_XMLClass, Valueify(vp+2)); \
5198 #define NON_LIST_XML_METHOD_PROLOG \
5200 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5203 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5206 xml_addNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
5210 NON_LIST_XML_METHOD_PROLOG
;
5211 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
5213 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5217 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
5219 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
5221 ns
= JSVAL_TO_OBJECT(*vp
);
5222 if (!AddInScopeNamespace(cx
, xml
, ns
))
5224 ns
->setNamespaceDeclared(JSVAL_TRUE
);
5227 *vp
= OBJECT_TO_JSVAL(obj
);
5232 xml_appendChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
5238 NON_LIST_XML_METHOD_PROLOG
;
5239 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5244 if (!js_GetAnyName(cx
, &name
))
5247 if (!GetProperty(cx
, obj
, name
, &v
))
5250 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5251 vobj
= JSVAL_TO_OBJECT(v
);
5252 JS_ASSERT(vobj
->isXML());
5253 vxml
= (JSXML
*) vobj
->getPrivate();
5254 JS_ASSERT(vxml
->xml_class
== JSXML_CLASS_LIST
);
5256 if (!IndexToId(cx
, vxml
->xml_kids
.length
, &name
))
5258 *vp
= (argc
!= 0) ? vp
[2] : JSVAL_VOID
;
5260 if (!PutProperty(cx
, JSVAL_TO_OBJECT(v
), name
, false, vp
))
5263 *vp
= OBJECT_TO_JSVAL(obj
);
5267 /* XML and XMLList */
5269 xml_attribute(JSContext
*cx
, uintN argc
, jsval
*vp
)
5274 js_ReportMissingArg(cx
, Valueify(*vp
), 0);
5278 qn
= ToAttributeName(cx
, vp
[2]);
5281 vp
[2] = OBJECT_TO_JSVAL(qn
); /* local root */
5283 jsid id
= OBJECT_TO_JSID(qn
);
5284 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
5287 return GetProperty(cx
, obj
, id
, vp
);
5290 /* XML and XMLList */
5292 xml_attributes(JSContext
*cx
, uintN argc
, jsval
*vp
)
5294 jsval name
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
);
5295 JSObject
*qn
= ToAttributeName(cx
, name
);
5299 AutoObjectRooter
tvr(cx
, qn
);
5300 jsid id
= OBJECT_TO_JSID(qn
);
5301 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
5304 return GetProperty(cx
, obj
, id
, vp
);
5308 xml_list_helper(JSContext
*cx
, JSXML
*xml
, jsval
*rval
)
5313 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5317 *rval
= OBJECT_TO_JSVAL(listobj
);
5318 list
= (JSXML
*) listobj
->getPrivate();
5319 list
->xml_target
= xml
;
5324 ValueToId(JSContext
*cx
, jsval v
, AutoIdRooter
*idr
)
5326 if (JSVAL_IS_INT(v
)) {
5327 jsint i
= JSVAL_TO_INT(v
);
5328 if (INT_FITS_IN_JSID(i
))
5329 *idr
->addr() = INT_TO_JSID(i
);
5330 else if (!js_ValueToStringId(cx
, Valueify(v
), idr
->addr()))
5332 } else if (JSVAL_IS_STRING(v
)) {
5333 JSAtom
*atom
= js_AtomizeString(cx
, JSVAL_TO_STRING(v
), 0);
5336 *idr
->addr() = ATOM_TO_JSID(atom
);
5337 } else if (!JSVAL_IS_PRIMITIVE(v
)) {
5338 *idr
->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v
));
5340 ReportBadXMLName(cx
, Valueify(v
));
5347 xml_child_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval name
,
5355 /* ECMA-357 13.4.4.6 */
5356 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
5358 if (!js_IdValIsIndex(cx
, name
, &index
, &isIndex
))
5362 if (index
>= JSXML_LENGTH(xml
)) {
5365 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
5369 kidobj
= js_GetXMLObject(cx
, kid
);
5372 *rval
= OBJECT_TO_JSVAL(kidobj
);
5378 AutoIdRooter
idr(cx
);
5379 if (!ValueToId(cx
, name
, &idr
))
5382 return GetProperty(cx
, obj
, idr
.id(), rval
);
5385 /* XML and XMLList */
5387 xml_child(JSContext
*cx
, uintN argc
, jsval
*vp
)
5394 jsval name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5395 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5396 /* ECMA-357 13.5.4.4 */
5397 list
= xml_list_helper(cx
, xml
, vp
);
5401 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
5402 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
5403 kidobj
= js_GetXMLObject(cx
, kid
);
5406 if (!xml_child_helper(cx
, kidobj
, kid
, name
, &v
))
5408 if (JSVAL_IS_VOID(v
)) {
5409 /* The property didn't exist in this kid. */
5413 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5414 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
5415 if ((!JSXML_HAS_KIDS(vxml
) || vxml
->xml_kids
.length
!= 0) &&
5416 !Append(cx
, list
, vxml
)) {
5423 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5424 if (!xml_child_helper(cx
, obj
, xml
, name
, vp
))
5426 if (JSVAL_IS_VOID(*vp
) && !xml_list_helper(cx
, xml
, vp
))
5432 xml_childIndex(JSContext
*cx
, uintN argc
, jsval
*vp
)
5437 NON_LIST_XML_METHOD_PROLOG
;
5438 parent
= xml
->parent
;
5439 if (!parent
|| xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
5440 *vp
= DOUBLE_TO_JSVAL(js_NaN
);
5443 for (i
= 0, n
= JSXML_LENGTH(parent
); i
< n
; i
++) {
5444 if (XMLARRAY_MEMBER(&parent
->xml_kids
, i
, JSXML
) == xml
)
5448 if (i
<= JSVAL_INT_MAX
)
5449 *vp
= INT_TO_JSVAL(i
);
5451 *vp
= DOUBLE_TO_JSVAL(i
);
5455 /* XML and XMLList */
5457 xml_children(JSContext
*cx
, uintN argc
, jsval
*vp
)
5459 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
5462 jsid name
= ATOM_TO_JSID(cx
->runtime
->atomState
.starAtom
);
5463 return GetProperty(cx
, obj
, name
, vp
);
5466 /* XML and XMLList */
5468 xml_comments_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
5470 JSXML
*list
, *kid
, *vxml
;
5476 list
= xml_list_helper(cx
, xml
, vp
);
5482 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5483 /* 13.5.4.6 Step 2. */
5484 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5485 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5486 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5487 ok
= js_EnterLocalRootScope(cx
);
5490 kidobj
= js_GetXMLObject(cx
, kid
);
5492 ok
= xml_comments_helper(cx
, kidobj
, kid
, &v
);
5497 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
5500 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
5501 if (JSXML_LENGTH(vxml
) != 0) {
5502 ok
= Append(cx
, list
, vxml
);
5509 /* 13.4.4.9 Step 2. */
5510 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5511 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5512 if (kid
&& kid
->xml_class
== JSXML_CLASS_COMMENT
) {
5513 ok
= Append(cx
, list
, kid
);
5524 xml_comments(JSContext
*cx
, uintN argc
, jsval
*vp
)
5527 return xml_comments_helper(cx
, obj
, xml
, vp
);
5530 /* XML and XMLList */
5532 xml_contains(JSContext
*cx
, uintN argc
, jsval
*vp
)
5539 value
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5540 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5542 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
5543 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
5544 kidobj
= js_GetXMLObject(cx
, kid
);
5545 if (!kidobj
|| !js_TestXMLEquality(cx
, ObjectValue(*kidobj
), Valueify(value
), &eq
))
5551 if (!js_TestXMLEquality(cx
, ObjectValue(*obj
), Valueify(value
), &eq
))
5554 *vp
= BOOLEAN_TO_JSVAL(eq
);
5558 /* XML and XMLList */
5560 xml_copy(JSContext
*cx
, uintN argc
, jsval
*vp
)
5565 copy
= DeepCopy(cx
, xml
, NULL
, 0);
5568 *vp
= OBJECT_TO_JSVAL(copy
->object
);
5572 /* XML and XMLList */
5574 xml_descendants(JSContext
*cx
, uintN argc
, jsval
*vp
)
5580 name
= argc
== 0 ? ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
) : vp
[2];
5581 list
= Descendants(cx
, xml
, name
);
5584 *vp
= OBJECT_TO_JSVAL(list
->object
);
5588 /* XML and XMLList */
5590 xml_elements_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
5591 JSObject
*nameqn
, jsval
*vp
)
5599 list
= xml_list_helper(cx
, xml
, vp
);
5603 list
->xml_targetprop
= nameqn
;
5606 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5608 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
5609 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
5610 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5611 ok
= js_EnterLocalRootScope(cx
);
5614 kidobj
= js_GetXMLObject(cx
, kid
);
5616 ok
= xml_elements_helper(cx
, kidobj
, kid
, nameqn
, &v
);
5621 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
5624 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
5625 if (JSXML_LENGTH(vxml
) != 0) {
5626 ok
= Append(cx
, list
, vxml
);
5633 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5634 JSXML
*kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5635 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
5636 MatchElemName(nameqn
, kid
)) {
5637 ok
= Append(cx
, list
, kid
);
5648 xml_elements(JSContext
*cx
, uintN argc
, jsval
*vp
)
5656 name
= (argc
== 0) ? ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
) : vp
[2];
5657 nameqn
= ToXMLName(cx
, name
, &funid
);
5661 if (!JSID_IS_VOID(funid
))
5662 return xml_list_helper(cx
, xml
, vp
) != NULL
;
5664 return xml_elements_helper(cx
, obj
, xml
, nameqn
, vp
);
5667 /* XML and XMLList */
5669 xml_hasOwnProperty(JSContext
*cx
, uintN argc
, jsval
*vp
)
5674 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
5677 if (!InstanceOf(cx
, obj
, &js_XMLClass
, Valueify(vp
+ 2)))
5680 name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5681 if (!HasProperty(cx
, obj
, name
, &found
))
5687 return js_HasOwnPropertyHelper(cx
, js_LookupProperty
, argc
, Valueify(vp
));
5690 /* XML and XMLList */
5692 xml_hasComplexContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
5700 switch (xml
->xml_class
) {
5701 case JSXML_CLASS_ATTRIBUTE
:
5702 case JSXML_CLASS_COMMENT
:
5703 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
5704 case JSXML_CLASS_TEXT
:
5707 case JSXML_CLASS_LIST
:
5708 if (xml
->xml_kids
.length
== 0) {
5710 } else if (xml
->xml_kids
.length
== 1) {
5711 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5713 kidobj
= js_GetXMLObject(cx
, kid
);
5717 xml
= (JSXML
*) obj
->getPrivate();
5724 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
5725 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5726 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5736 /* XML and XMLList */
5738 xml_hasSimpleContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
5741 *vp
= BOOLEAN_TO_JSVAL(HasSimpleContent(xml
));
5746 FindInScopeNamespaces(JSContext
*cx
, JSXML
*xml
, JSXMLArray
*nsarray
)
5748 uint32 length
, i
, j
, n
;
5750 JSLinearString
*prefix
, *prefix2
;
5752 length
= nsarray
->length
;
5754 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
5756 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
5757 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
5761 prefix
= ns
->getNamePrefix();
5762 for (j
= 0; j
< length
; j
++) {
5763 ns2
= XMLARRAY_MEMBER(nsarray
, j
, JSObject
);
5765 prefix2
= ns2
->getNamePrefix();
5766 if ((prefix2
&& prefix
)
5767 ? EqualStrings(prefix2
, prefix
)
5768 : EqualStrings(ns2
->getNameURI(), ns
->getNameURI())) {
5775 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
5780 } while ((xml
= xml
->parent
) != NULL
);
5781 JS_ASSERT(length
== nsarray
->length
);
5787 * Populate a new JS array with elements of array and place the result into
5788 * rval. rval must point to a rooted location.
5791 NamespacesToJSArray(JSContext
*cx
, JSXMLArray
*array
, jsval
*rval
)
5793 JSObject
*arrayobj
= NewDenseEmptyArray(cx
);
5796 *rval
= OBJECT_TO_JSVAL(arrayobj
);
5798 AutoValueRooter
tvr(cx
);
5799 for (uint32 i
= 0, n
= array
->length
; i
< n
; i
++) {
5800 JSObject
*ns
= XMLARRAY_MEMBER(array
, i
, JSObject
);
5803 tvr
.set(ObjectValue(*ns
));
5804 if (!arrayobj
->setProperty(cx
, INT_TO_JSID(i
), tvr
.addr(), false))
5811 xml_inScopeNamespaces(JSContext
*cx
, uintN argc
, jsval
*vp
)
5813 NON_LIST_XML_METHOD_PROLOG
;
5815 AutoNamespaceArray
namespaces(cx
);
5816 return FindInScopeNamespaces(cx
, xml
, &namespaces
.array
) &&
5817 NamespacesToJSArray(cx
, &namespaces
.array
, vp
);
5821 xml_insertChildAfter(JSContext
*cx
, uintN argc
, jsval
*vp
)
5827 NON_LIST_XML_METHOD_PROLOG
;
5828 *vp
= OBJECT_TO_JSVAL(obj
);
5829 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
5833 if (JSVAL_IS_NULL(arg
)) {
5837 if (!VALUE_IS_XML(arg
))
5839 kid
= (JSXML
*) JSVAL_TO_OBJECT(arg
)->getPrivate();
5840 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
5841 if (i
== XML_NOT_FOUND
)
5846 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5849 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
5853 xml_insertChildBefore(JSContext
*cx
, uintN argc
, jsval
*vp
)
5859 NON_LIST_XML_METHOD_PROLOG
;
5860 *vp
= OBJECT_TO_JSVAL(obj
);
5861 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
5865 if (JSVAL_IS_NULL(arg
)) {
5867 i
= xml
->xml_kids
.length
;
5869 if (!VALUE_IS_XML(arg
))
5871 kid
= (JSXML
*) JSVAL_TO_OBJECT(arg
)->getPrivate();
5872 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
5873 if (i
== XML_NOT_FOUND
)
5877 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5880 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
5883 /* XML and XMLList */
5885 xml_length(JSContext
*cx
, uintN argc
, jsval
*vp
)
5888 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
5891 uint32 l
= xml
->xml_kids
.length
;
5892 if (l
<= JSVAL_INT_MAX
)
5893 *vp
= INT_TO_JSVAL(l
);
5895 *vp
= DOUBLE_TO_JSVAL(l
);
5901 xml_localName(JSContext
*cx
, uintN argc
, jsval
*vp
)
5903 NON_LIST_XML_METHOD_PROLOG
;
5904 *vp
= xml
->name
? xml
->name
->getQNameLocalNameVal() : JSVAL_NULL
;
5909 xml_name(JSContext
*cx
, uintN argc
, jsval
*vp
)
5911 NON_LIST_XML_METHOD_PROLOG
;
5912 *vp
= OBJECT_TO_JSVAL(xml
->name
);
5917 xml_namespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
5919 JSLinearString
*prefix
, *nsprefix
;
5923 NON_LIST_XML_METHOD_PROLOG
;
5924 if (argc
== 0 && !JSXML_HAS_NAME(xml
)) {
5932 JSString
*str
= js_ValueToString(cx
, Valueify(vp
[2]));
5935 prefix
= str
->ensureLinear(cx
);
5938 vp
[2] = STRING_TO_JSVAL(prefix
); /* local root */
5941 AutoNamespaceArray
inScopeNSes(cx
);
5942 if (!FindInScopeNamespaces(cx
, xml
, &inScopeNSes
.array
))
5946 ns
= GetNamespace(cx
, xml
->name
, &inScopeNSes
.array
);
5951 for (i
= 0, length
= inScopeNSes
.array
.length
; i
< length
; i
++) {
5952 ns
= XMLARRAY_MEMBER(&inScopeNSes
.array
, i
, JSObject
);
5954 nsprefix
= ns
->getNamePrefix();
5955 if (nsprefix
&& EqualStrings(nsprefix
, prefix
))
5962 *vp
= (!ns
) ? JSVAL_VOID
: OBJECT_TO_JSVAL(ns
);
5967 xml_namespaceDeclarations(JSContext
*cx
, uintN argc
, jsval
*vp
)
5969 NON_LIST_XML_METHOD_PROLOG
;
5970 if (JSXML_HAS_VALUE(xml
))
5973 AutoNamespaceArray
ancestors(cx
);
5974 AutoNamespaceArray
declared(cx
);
5977 while ((yml
= yml
->parent
) != NULL
) {
5978 JS_ASSERT(yml
->xml_class
== JSXML_CLASS_ELEMENT
);
5979 for (uint32 i
= 0, n
= yml
->xml_namespaces
.length
; i
< n
; i
++) {
5980 JSObject
*ns
= XMLARRAY_MEMBER(&yml
->xml_namespaces
, i
, JSObject
);
5981 if (ns
&& !XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
5982 if (!XMLARRAY_APPEND(cx
, &ancestors
.array
, ns
))
5988 for (uint32 i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
5989 JSObject
*ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
5992 if (!IsDeclared(ns
))
5994 if (!XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
5995 if (!XMLARRAY_APPEND(cx
, &declared
.array
, ns
))
6000 return NamespacesToJSArray(cx
, &declared
.array
, vp
);
6003 static const char js_attribute_str
[] = "attribute";
6004 static const char js_text_str
[] = "text";
6006 /* Exported to jsgc.c #ifdef DEBUG. */
6007 const char *js_xml_class_str
[] = {
6011 "processing-instruction",
6017 xml_nodeKind(JSContext
*cx
, uintN argc
, jsval
*vp
)
6021 NON_LIST_XML_METHOD_PROLOG
;
6022 str
= JS_InternString(cx
, js_xml_class_str
[xml
->xml_class
]);
6025 *vp
= STRING_TO_JSVAL(str
);
6030 NormalizingDelete(JSContext
*cx
, JSXML
*xml
, uint32 index
)
6032 if (xml
->xml_class
== JSXML_CLASS_LIST
)
6033 DeleteListElement(cx
, xml
, index
);
6035 DeleteByIndex(cx
, xml
, index
);
6038 /* XML and XMLList */
6040 xml_normalize_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
)
6047 if (!JSXML_HAS_KIDS(xml
))
6050 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6054 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6055 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6058 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6059 kidobj
= js_GetXMLObject(cx
, kid
);
6060 if (!kidobj
|| !xml_normalize_helper(cx
, kidobj
, kid
))
6062 } else if (kid
->xml_class
== JSXML_CLASS_TEXT
) {
6064 (kid2
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
+ 1, JSXML
)) &&
6065 kid2
->xml_class
== JSXML_CLASS_TEXT
) {
6066 str
= js_ConcatStrings(cx
, kid
->xml_value
, kid2
->xml_value
);
6069 NormalizingDelete(cx
, xml
, i
+ 1);
6070 n
= xml
->xml_kids
.length
;
6071 kid
->xml_value
= str
;
6073 if (kid
->xml_value
->empty()) {
6074 NormalizingDelete(cx
, xml
, i
);
6075 n
= xml
->xml_kids
.length
;
6085 xml_normalize(JSContext
*cx
, uintN argc
, jsval
*vp
)
6088 *vp
= OBJECT_TO_JSVAL(obj
);
6089 return xml_normalize_helper(cx
, obj
, xml
);
6092 /* XML and XMLList */
6094 xml_parent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6096 JSXML
*parent
, *kid
;
6098 JSObject
*parentobj
;
6101 parent
= xml
->parent
;
6102 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6104 n
= xml
->xml_kids
.length
;
6108 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
6111 parent
= kid
->parent
;
6112 for (i
= 1; i
< n
; i
++) {
6113 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6114 if (kid
&& kid
->parent
!= parent
)
6124 parentobj
= js_GetXMLObject(cx
, parent
);
6127 *vp
= OBJECT_TO_JSVAL(parentobj
);
6131 /* XML and XMLList */
6133 xml_processingInstructions_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
6134 JSObject
*nameqn
, jsval
*vp
)
6142 list
= xml_list_helper(cx
, xml
, vp
);
6146 list
->xml_targetprop
= nameqn
;
6149 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6150 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6151 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
6152 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
6153 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6154 ok
= js_EnterLocalRootScope(cx
);
6157 kidobj
= js_GetXMLObject(cx
, kid
);
6159 ok
= xml_processingInstructions_helper(cx
, kidobj
, kid
,
6165 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
6168 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
6169 if (JSXML_LENGTH(vxml
) != 0) {
6170 ok
= Append(cx
, list
, vxml
);
6177 /* 13.4.4.28 Step 4. */
6178 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6179 JSXML
*kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6180 if (kid
&& kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
6181 JSLinearString
*localName
= nameqn
->getQNameLocalName();
6182 if (IS_STAR(localName
) ||
6183 EqualStrings(localName
, kid
->name
->getQNameLocalName())) {
6184 ok
= Append(cx
, list
, kid
);
6196 xml_processingInstructions(JSContext
*cx
, uintN argc
, jsval
*vp
)
6204 name
= (argc
== 0) ? ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
) : vp
[2];
6205 nameqn
= ToXMLName(cx
, name
, &funid
);
6208 vp
[2] = OBJECT_TO_JSVAL(nameqn
);
6210 if (!JSID_IS_VOID(funid
))
6211 return xml_list_helper(cx
, xml
, vp
) != NULL
;
6213 return xml_processingInstructions_helper(cx
, obj
, xml
, nameqn
, vp
);
6217 xml_prependChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
6219 NON_LIST_XML_METHOD_PROLOG
;
6220 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6223 *vp
= OBJECT_TO_JSVAL(obj
);
6224 return Insert(cx
, xml
, 0, argc
!= 0 ? vp
[2] : JSVAL_VOID
);
6227 /* XML and XMLList */
6229 xml_propertyIsEnumerable(JSContext
*cx
, uintN argc
, jsval
*vp
)
6237 if (!js_IdValIsIndex(cx
, vp
[2], &index
, &isIndex
))
6241 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6243 *vp
= BOOLEAN_TO_JSVAL(index
< xml
->xml_kids
.length
);
6246 *vp
= BOOLEAN_TO_JSVAL(index
== 0);
6254 namespace_full_match(const void *a
, const void *b
)
6256 const JSObject
*nsa
= (const JSObject
*) a
;
6257 const JSObject
*nsb
= (const JSObject
*) b
;
6258 JSLinearString
*prefixa
= nsa
->getNamePrefix();
6259 JSLinearString
*prefixb
;
6262 prefixb
= nsb
->getNamePrefix();
6263 if (prefixb
&& !EqualStrings(prefixa
, prefixb
))
6266 return EqualStrings(nsa
->getNameURI(), nsb
->getNameURI());
6270 xml_removeNamespace_helper(JSContext
*cx
, JSXML
*xml
, JSObject
*ns
)
6272 JSObject
*thisns
, *attrns
;
6276 thisns
= GetNamespace(cx
, xml
->name
, &xml
->xml_namespaces
);
6281 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
6282 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
6285 attrns
= GetNamespace(cx
, attr
->name
, &xml
->xml_namespaces
);
6291 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_namespaces
, ns
, namespace_full_match
);
6292 if (i
!= XML_NOT_FOUND
)
6293 XMLArrayDelete(cx
, &xml
->xml_namespaces
, i
, JS_TRUE
);
6295 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6296 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6297 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6298 if (!xml_removeNamespace_helper(cx
, kid
, ns
))
6306 xml_removeNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6310 NON_LIST_XML_METHOD_PROLOG
;
6311 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6313 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6317 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
6319 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6320 ns
= JSVAL_TO_OBJECT(*vp
);
6322 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6323 if (!xml_removeNamespace_helper(cx
, xml
, ns
))
6326 *vp
= OBJECT_TO_JSVAL(obj
);
6331 xml_replace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6338 NON_LIST_XML_METHOD_PROLOG
;
6339 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6343 value
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6346 vxml
= VALUE_IS_XML(value
)
6347 ? (JSXML
*) JSVAL_TO_OBJECT(value
)->getPrivate()
6350 if (!JS_ConvertValue(cx
, value
, JSTYPE_STRING
, &vp
[3]))
6354 vxml
= DeepCopy(cx
, vxml
, NULL
, 0);
6357 value
= vp
[3] = OBJECT_TO_JSVAL(vxml
->object
);
6361 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6369 if (!js_IdValIsIndex(cx
, vp
[2], &index
, &haveIndex
))
6375 * Call function QName per spec, not ToXMLName, to avoid attribute
6378 if (!QNameHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
6380 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6381 nameqn
= JSVAL_TO_OBJECT(*vp
);
6383 i
= xml
->xml_kids
.length
;
6384 index
= XML_NOT_FOUND
;
6387 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6388 if (kid
&& MatchElemName(nameqn
, kid
)) {
6389 if (i
!= XML_NOT_FOUND
)
6390 DeleteByIndex(cx
, xml
, i
);
6395 if (index
== XML_NOT_FOUND
)
6399 if (!Replace(cx
, xml
, index
, value
))
6403 *vp
= OBJECT_TO_JSVAL(obj
);
6408 xml_setChildren(JSContext
*cx
, uintN argc
, jsval
*vp
)
6412 if (!StartNonListXMLMethod(cx
, vp
, &obj
))
6415 *vp
= argc
!= 0 ? vp
[2] : JSVAL_VOID
; /* local root */
6416 if (!PutProperty(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.starAtom
), false, vp
))
6419 *vp
= OBJECT_TO_JSVAL(obj
);
6424 xml_setLocalName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6428 JSLinearString
*namestr
;
6430 NON_LIST_XML_METHOD_PROLOG
;
6431 if (!JSXML_HAS_NAME(xml
))
6435 namestr
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6438 if (!JSVAL_IS_PRIMITIVE(name
) &&
6439 JSVAL_TO_OBJECT(name
)->getClass() == &js_QNameClass
) {
6440 nameqn
= JSVAL_TO_OBJECT(name
);
6441 namestr
= nameqn
->getQNameLocalName();
6443 if (!JS_ConvertValue(cx
, name
, JSTYPE_STRING
, &vp
[2]))
6446 namestr
= JSVAL_TO_STRING(name
)->ensureLinear(cx
);
6452 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6456 xml
->name
->setQNameLocalName(namestr
);
6461 xml_setName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6466 JSXMLArray
*nsarray
;
6470 NON_LIST_XML_METHOD_PROLOG
;
6471 if (!JSXML_HAS_NAME(xml
))
6475 name
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6478 if (!JSVAL_IS_PRIMITIVE(name
) &&
6479 JSVAL_TO_OBJECT(name
)->getClass() == &js_QNameClass
&&
6480 !(nameqn
= JSVAL_TO_OBJECT(name
))->getNameURI()) {
6481 name
= vp
[2] = nameqn
->getQNameLocalNameVal();
6485 nameqn
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 1, Valueify(&name
));
6489 /* ECMA-357 13.4.4.35 Step 4. */
6490 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
6491 nameqn
->setNameURI(cx
->runtime
->emptyString
);
6493 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6499 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6500 * in-scope namespaces, either by finding an in-scope namespace with a
6501 * matching uri and setting the new name's prefix to that namespace's
6502 * prefix, or by extending the in-scope namespaces for xml (which are in
6503 * xml->parent if xml is an attribute or a PI).
6505 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
6508 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
6510 nsowner
= xml
->parent
;
6513 if (nameqn
->getNamePrefix()) {
6515 * The name being set has a prefix, which originally came from some
6516 * namespace object (which may be the null namespace, where both the
6517 * prefix and uri are the empty string). We must go through a full
6518 * GetNamespace in case that namespace is in-scope in nsowner.
6520 * If we find such an in-scope namespace, we return true right away,
6521 * in this block. Otherwise, we fall through to the final return of
6522 * AddInScopeNamespace(cx, nsowner, ns).
6524 ns
= GetNamespace(cx
, nameqn
, &nsowner
->xml_namespaces
);
6528 /* XXXbe have to test membership to see whether GetNamespace added */
6529 if (XMLARRAY_HAS_MEMBER(&nsowner
->xml_namespaces
, ns
, NULL
))
6533 * At this point, we know prefix of nameqn is null, so its uri can't
6534 * be the empty string (the null namespace always uses the empty string
6535 * for both prefix and uri).
6537 * This means we must inline GetNamespace and specialize it to match
6538 * uri only, never prefix. If we find a namespace with nameqn's uri
6539 * already in nsowner->xml_namespaces, then all that we need do is set
6540 * prefix of nameqn to that namespace's prefix.
6542 * If no such namespace exists, we can create one without going through
6543 * the constructor, because we know uri of nameqn is non-empty (so
6544 * prefix does not need to be converted from null to empty by QName).
6546 JS_ASSERT(!nameqn
->getNameURI()->empty());
6548 nsarray
= &nsowner
->xml_namespaces
;
6549 for (i
= 0, n
= nsarray
->length
; i
< n
; i
++) {
6550 ns
= XMLARRAY_MEMBER(nsarray
, i
, JSObject
);
6551 if (ns
&& EqualStrings(ns
->getNameURI(), nameqn
->getNameURI())) {
6552 nameqn
->setNamePrefix(ns
->getNamePrefix());
6557 ns
= NewXMLNamespace(cx
, NULL
, nameqn
->getNameURI(), JS_TRUE
);
6562 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
6569 xml_setNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6576 NON_LIST_XML_METHOD_PROLOG
;
6577 if (!JSXML_HAS_NAME(xml
))
6580 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6584 ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, obj
,
6585 argc
== 0 ? 0 : 1, Valueify(vp
+ 2));
6588 vp
[0] = OBJECT_TO_JSVAL(ns
);
6589 ns
->setNamespaceDeclared(JSVAL_TRUE
);
6591 qnargv
[0] = OBJECT_TO_JSVAL(ns
);
6592 qnargv
[1] = OBJECT_TO_JSVAL(xml
->name
);
6593 qn
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 2, Valueify(qnargv
));
6600 * Erratum: the spec fails to update the governing in-scope namespaces.
6601 * See the erratum noted in xml_setName, above.
6603 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
6606 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
6608 nsowner
= xml
->parent
;
6610 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
6616 /* XML and XMLList */
6618 xml_text_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
6620 JSXML
*list
, *kid
, *vxml
;
6626 list
= xml_list_helper(cx
, xml
, vp
);
6630 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6632 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6633 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6634 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6635 ok
= js_EnterLocalRootScope(cx
);
6638 kidobj
= js_GetXMLObject(cx
, kid
);
6640 ok
= xml_text_helper(cx
, kidobj
, kid
, &v
);
6645 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
6648 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
6649 if (JSXML_LENGTH(vxml
) != 0 && !Append(cx
, list
, vxml
))
6654 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6655 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6656 if (kid
&& kid
->xml_class
== JSXML_CLASS_TEXT
) {
6657 if (!Append(cx
, list
, kid
))
6666 xml_text(JSContext
*cx
, uintN argc
, jsval
*vp
)
6669 return xml_text_helper(cx
, obj
, xml
, vp
);
6672 /* XML and XMLList */
6674 xml_toString_helper(JSContext
*cx
, JSXML
*xml
)
6676 JSString
*str
, *kidstr
;
6678 if (xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
||
6679 xml
->xml_class
== JSXML_CLASS_TEXT
) {
6680 return xml
->xml_value
;
6683 if (!HasSimpleContent(xml
))
6684 return ToXMLString(cx
, OBJECT_TO_JSVAL(xml
->object
), 0);
6686 str
= cx
->runtime
->emptyString
;
6687 if (!js_EnterLocalRootScope(cx
))
6689 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
6690 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
6691 if (kid
->xml_class
!= JSXML_CLASS_COMMENT
&&
6692 kid
->xml_class
!= JSXML_CLASS_PROCESSING_INSTRUCTION
) {
6693 kidstr
= xml_toString_helper(cx
, kid
);
6698 str
= js_ConcatStrings(cx
, str
, kidstr
);
6703 js_LeaveLocalRootScopeWithResult(cx
, str
);
6708 xml_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
6710 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
6713 JSString
*str
= ToXMLString(cx
, OBJECT_TO_JSVAL(obj
), TO_SOURCE_FLAG
);
6716 *vp
= STRING_TO_JSVAL(str
);
6721 xml_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
6726 str
= xml_toString_helper(cx
, xml
);
6729 *vp
= STRING_TO_JSVAL(str
);
6733 /* XML and XMLList */
6735 xml_toXMLString(JSContext
*cx
, uintN argc
, jsval
*vp
)
6737 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
6740 JSString
*str
= ToXMLString(cx
, OBJECT_TO_JSVAL(obj
), 0);
6743 *vp
= STRING_TO_JSVAL(str
);
6747 /* XML and XMLList */
6749 xml_valueOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
6751 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
6754 *vp
= OBJECT_TO_JSVAL(obj
);
6758 static JSFunctionSpec xml_methods
[] = {
6759 JS_FN("addNamespace", xml_addNamespace
, 1,0),
6760 JS_FN("appendChild", xml_appendChild
, 1,0),
6761 JS_FN(js_attribute_str
, xml_attribute
, 1,0),
6762 JS_FN("attributes", xml_attributes
, 0,0),
6763 JS_FN("child", xml_child
, 1,0),
6764 JS_FN("childIndex", xml_childIndex
, 0,0),
6765 JS_FN("children", xml_children
, 0,0),
6766 JS_FN("comments", xml_comments
, 0,0),
6767 JS_FN("contains", xml_contains
, 1,0),
6768 JS_FN("copy", xml_copy
, 0,0),
6769 JS_FN("descendants", xml_descendants
, 1,0),
6770 JS_FN("elements", xml_elements
, 1,0),
6771 JS_FN("hasOwnProperty", xml_hasOwnProperty
, 1,0),
6772 JS_FN("hasComplexContent", xml_hasComplexContent
, 1,0),
6773 JS_FN("hasSimpleContent", xml_hasSimpleContent
, 1,0),
6774 JS_FN("inScopeNamespaces", xml_inScopeNamespaces
, 0,0),
6775 JS_FN("insertChildAfter", xml_insertChildAfter
, 2,0),
6776 JS_FN("insertChildBefore", xml_insertChildBefore
, 2,0),
6777 JS_FN(js_length_str
, xml_length
, 0,0),
6778 JS_FN(js_localName_str
, xml_localName
, 0,0),
6779 JS_FN(js_name_str
, xml_name
, 0,0),
6780 JS_FN(js_namespace_str
, xml_namespace
, 1,0),
6781 JS_FN("namespaceDeclarations", xml_namespaceDeclarations
, 0,0),
6782 JS_FN("nodeKind", xml_nodeKind
, 0,0),
6783 JS_FN("normalize", xml_normalize
, 0,0),
6784 JS_FN(js_xml_parent_str
, xml_parent
, 0,0),
6785 JS_FN("processingInstructions",xml_processingInstructions
,1,0),
6786 JS_FN("prependChild", xml_prependChild
, 1,0),
6787 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable
, 1,0),
6788 JS_FN("removeNamespace", xml_removeNamespace
, 1,0),
6789 JS_FN("replace", xml_replace
, 2,0),
6790 JS_FN("setChildren", xml_setChildren
, 1,0),
6791 JS_FN("setLocalName", xml_setLocalName
, 1,0),
6792 JS_FN("setName", xml_setName
, 1,0),
6793 JS_FN("setNamespace", xml_setNamespace
, 1,0),
6794 JS_FN(js_text_str
, xml_text
, 0,0),
6795 JS_FN(js_toSource_str
, xml_toSource
, 0,0),
6796 JS_FN(js_toString_str
, xml_toString
, 0,0),
6797 JS_FN(js_toXMLString_str
, xml_toXMLString
, 0,0),
6798 JS_FN(js_valueOf_str
, xml_valueOf
, 0,0),
6803 CopyXMLSettings(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
6809 /* Note: PRETTY_INDENT is not a boolean setting. */
6810 for (i
= 0; xml_static_props
[i
].name
; i
++) {
6811 name
= xml_static_props
[i
].name
;
6812 if (!JS_GetProperty(cx
, from
, name
, &v
))
6814 if (name
== js_prettyIndent_str
) {
6815 if (!JSVAL_IS_NUMBER(v
))
6818 if (!JSVAL_IS_BOOLEAN(v
))
6821 if (!JS_SetProperty(cx
, to
, name
, &v
))
6829 SetDefaultXMLSettings(JSContext
*cx
, JSObject
*obj
)
6834 /* Note: PRETTY_INDENT is not a boolean setting. */
6835 for (i
= 0; xml_static_props
[i
].name
; i
++) {
6836 v
= (xml_static_props
[i
].name
!= js_prettyIndent_str
)
6837 ? JSVAL_TRUE
: INT_TO_JSVAL(2);
6838 if (!JS_SetProperty(cx
, obj
, xml_static_props
[i
].name
, &v
))
6845 xml_settings(JSContext
*cx
, uintN argc
, jsval
*vp
)
6847 JSObject
*settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
6850 *vp
= OBJECT_TO_JSVAL(settings
);
6851 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
6854 return CopyXMLSettings(cx
, obj
, settings
);
6858 xml_setSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
6864 JSObject
*obj
= ToObject(cx
, Valueify(&vp
[1]));
6867 v
= (argc
== 0) ? JSVAL_VOID
: vp
[2];
6868 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
6869 ok
= SetDefaultXMLSettings(cx
, obj
);
6871 if (JSVAL_IS_PRIMITIVE(v
))
6873 settings
= JSVAL_TO_OBJECT(v
);
6874 ok
= CopyXMLSettings(cx
, settings
, obj
);
6880 xml_defaultSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
6884 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
6887 *vp
= OBJECT_TO_JSVAL(settings
);
6888 return SetDefaultXMLSettings(cx
, settings
);
6891 static JSFunctionSpec xml_static_methods
[] = {
6892 JS_FN("settings", xml_settings
, 0,0),
6893 JS_FN("setSettings", xml_setSettings
, 1,0),
6894 JS_FN("defaultSettings", xml_defaultSettings
, 0,0),
6899 XML(JSContext
*cx
, uintN argc
, Value
*vp
)
6902 JSObject
*xobj
, *vobj
;
6905 jsval v
= argc
? Jsvalify(vp
[2]) : JSVAL_VOID
;
6907 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
6908 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
6910 xobj
= ToXML(cx
, v
);
6913 xml
= (JSXML
*) xobj
->getPrivate();
6915 if (IsConstructing(vp
) && !JSVAL_IS_PRIMITIVE(v
)) {
6916 vobj
= JSVAL_TO_OBJECT(v
);
6917 clasp
= vobj
->getClass();
6918 if (clasp
== &js_XMLClass
||
6919 (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
)) {
6920 copy
= DeepCopy(cx
, xml
, NULL
, 0);
6923 vp
->setObject(*copy
->object
);
6928 vp
->setObject(*xobj
);
6933 XMLList(JSContext
*cx
, uintN argc
, jsval
*vp
)
6935 JSObject
*vobj
, *listobj
;
6938 jsval v
= argc
? vp
[2] : JSVAL_VOID
;
6940 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
6941 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
6943 if (IsConstructing(Valueify(vp
)) && !JSVAL_IS_PRIMITIVE(v
)) {
6944 vobj
= JSVAL_TO_OBJECT(v
);
6945 if (vobj
->isXML()) {
6946 xml
= (JSXML
*) vobj
->getPrivate();
6947 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6948 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
6951 *vp
= OBJECT_TO_JSVAL(listobj
);
6953 list
= (JSXML
*) listobj
->getPrivate();
6954 if (!Append(cx
, list
, xml
))
6961 /* Toggle on XML support since the script has explicitly requested it. */
6962 listobj
= ToXMLList(cx
, v
);
6966 *vp
= OBJECT_TO_JSVAL(listobj
);
6971 JSCList xml_leaks
= JS_INIT_STATIC_CLIST(&xml_leaks
);
6976 js_NewXML(JSContext
*cx
, JSXMLClass xml_class
)
6978 JSXML
*xml
= js_NewGCXML(cx
);
6983 xml
->domnode
= NULL
;
6986 xml
->xml_class
= xml_class
;
6988 if (JSXML_CLASS_HAS_VALUE(xml_class
)) {
6989 xml
->xml_value
= cx
->runtime
->emptyString
;
6991 xml
->xml_kids
.init();
6992 if (xml_class
== JSXML_CLASS_LIST
) {
6993 xml
->xml_target
= NULL
;
6994 xml
->xml_targetprop
= NULL
;
6996 xml
->xml_namespaces
.init();
6997 xml
->xml_attrs
.init();
7002 JS_APPEND_LINK(&xml
->links
, &xml_leaks
);
7003 xml
->serial
= xml_serial
++;
7005 METER(xml_stats
.xml
);
7010 js_TraceXML(JSTracer
*trc
, JSXML
*xml
)
7013 MarkObject(trc
, *xml
->object
, "object");
7015 MarkObject(trc
, *xml
->name
, "name");
7017 JS_CALL_TRACER(trc
, xml
->parent
, JSTRACE_XML
, "xml_parent");
7019 if (JSXML_HAS_VALUE(xml
)) {
7021 MarkString(trc
, xml
->xml_value
, "value");
7025 xml_trace_vector(trc
,
7026 (JSXML
**) xml
->xml_kids
.vector
,
7027 xml
->xml_kids
.length
);
7028 XMLArrayCursorTrace(trc
, xml
->xml_kids
.cursors
);
7029 if (IS_GC_MARKING_TRACER(trc
))
7030 xml
->xml_kids
.trim();
7032 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7033 if (xml
->xml_target
)
7034 JS_CALL_TRACER(trc
, xml
->xml_target
, JSTRACE_XML
, "target");
7035 if (xml
->xml_targetprop
)
7036 MarkObject(trc
, *xml
->xml_targetprop
, "targetprop");
7038 MarkObjectRange(trc
, xml
->xml_namespaces
.length
,
7039 (JSObject
**) xml
->xml_namespaces
.vector
,
7041 XMLArrayCursorTrace(trc
, xml
->xml_namespaces
.cursors
);
7042 if (IS_GC_MARKING_TRACER(trc
))
7043 xml
->xml_namespaces
.trim();
7045 xml_trace_vector(trc
,
7046 (JSXML
**) xml
->xml_attrs
.vector
,
7047 xml
->xml_attrs
.length
);
7048 XMLArrayCursorTrace(trc
, xml
->xml_attrs
.cursors
);
7049 if (IS_GC_MARKING_TRACER(trc
))
7050 xml
->xml_attrs
.trim();
7055 js_NewXMLObject(JSContext
*cx
, JSXMLClass xml_class
)
7057 JSXML
*xml
= js_NewXML(cx
, xml_class
);
7061 AutoXMLRooter
root(cx
, xml
);
7062 return js_GetXMLObject(cx
, xml
);
7066 NewXMLObject(JSContext
*cx
, JSXML
*xml
)
7070 obj
= NewNonFunction
<WithProto::Class
>(cx
, &js_XMLClass
, NULL
, NULL
);
7073 obj
->setPrivate(xml
);
7074 METER(xml_stats
.xmlobj
);
7079 js_GetXMLObject(JSContext
*cx
, JSXML
*xml
)
7085 JS_ASSERT(obj
->getPrivate() == xml
);
7089 obj
= NewXMLObject(cx
, xml
);
7097 js_InitNamespaceClass(JSContext
*cx
, JSObject
*obj
)
7099 return js_InitClass(cx
, obj
, NULL
, &js_NamespaceClass
, Namespace
, 2,
7100 namespace_props
, namespace_methods
, NULL
, NULL
);
7104 js_InitQNameClass(JSContext
*cx
, JSObject
*obj
)
7106 return js_InitClass(cx
, obj
, NULL
, &js_QNameClass
, QName
, 2,
7107 qname_props
, qname_methods
, NULL
, NULL
);
7111 js_InitXMLClass(JSContext
*cx
, JSObject
*obj
)
7113 JSObject
*proto
, *pobj
;
7120 /* Define the isXMLName function. */
7121 if (!JS_DefineFunction(cx
, obj
, js_isXMLName_str
, xml_isXMLName
, 1, 0))
7124 /* Define the XML class constructor and prototype. */
7125 proto
= js_InitClass(cx
, obj
, NULL
, &js_XMLClass
, XML
, 1,
7127 xml_static_props
, xml_static_methods
);
7131 xml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
7134 proto
->setPrivate(xml
);
7135 xml
->object
= proto
;
7136 METER(xml_stats
.xmlobj
);
7139 * Prepare to set default settings on the XML constructor we just made.
7140 * NB: We can't use JS_GetConstructor, because it calls
7141 * JSObject::getProperty, which is xml_getProperty, which creates a new
7142 * XMLList every time! We must instead call js_LookupProperty directly.
7144 if (!js_LookupProperty(cx
, proto
,
7145 ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
7150 shape
= (Shape
*) prop
;
7151 cval
= Jsvalify(pobj
->nativeGetSlot(shape
->slot
));
7152 JS_ASSERT(VALUE_IS_FUNCTION(cx
, cval
));
7154 /* Set default settings. */
7158 if (!xml_setSettings(cx
, 1, vp
))
7161 /* Define the XMLList function and give it the same prototype as XML. */
7162 fun
= JS_DefineFunction(cx
, obj
, js_XMLList_str
, XMLList
, 1, JSFUN_CONSTRUCTOR
);
7165 if (!js_SetClassPrototype(cx
, FUN_OBJECT(fun
), proto
,
7166 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
7173 js_InitXMLClasses(JSContext
*cx
, JSObject
*obj
)
7175 if (!js_InitNamespaceClass(cx
, obj
))
7177 if (!js_InitQNameClass(cx
, obj
))
7179 return js_InitXMLClass(cx
, obj
);
7183 js_GetFunctionNamespace(JSContext
*cx
, Value
*vp
)
7185 JSObject
*global
= cx
->hasfp() ? cx
->fp()->scopeChain().getGlobal() : cx
->globalObject
;
7187 *vp
= global
->getReservedSlot(JSRESERVED_GLOBAL_FUNCTION_NS
);
7188 if (vp
->isUndefined()) {
7189 JSRuntime
*rt
= cx
->runtime
;
7190 JSLinearString
*prefix
= rt
->atomState
.typeAtoms
[JSTYPE_FUNCTION
];
7191 JSLinearString
*uri
= rt
->atomState
.functionNamespaceURIAtom
;
7192 JSObject
*obj
= NewXMLNamespace(cx
, prefix
, uri
, JS_FALSE
);
7197 * Avoid entraining any in-scope Object.prototype. The loss of
7198 * Namespace.prototype is not detectable, as there is no way to
7199 * refer to this instance in scripts. When used to qualify method
7200 * names, its prefix and uri references are copied to the QName.
7201 * The parent remains set and links back to global.
7205 vp
->setObject(*obj
);
7206 if (!js_SetReservedSlot(cx
, global
, JSRESERVED_GLOBAL_FUNCTION_NS
, *vp
))
7214 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7215 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7216 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
7217 * requirement that fp->varobj lie directly on fp->scopeChain, although
7218 * it should be reachable using the prototype chain from a scope object (cf.
7219 * JSOPTION_VAROBJFIX in jsapi.h).
7221 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7222 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7223 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7224 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7225 * the default XML namespace will be set to ("", n.uri). So the uri string
7226 * is really the only usefully stored value of the default namespace.
7229 js_GetDefaultXMLNamespace(JSContext
*cx
, jsval
*vp
)
7231 JSObject
*ns
, *obj
, *tmp
;
7234 JSObject
*scopeChain
= GetScopeChain(cx
);
7237 for (tmp
= scopeChain
; tmp
; tmp
= tmp
->getParent()) {
7238 Class
*clasp
= tmp
->getClass();
7239 if (clasp
== &js_BlockClass
|| clasp
== &js_WithClass
)
7241 if (!tmp
->getProperty(cx
, JS_DEFAULT_XML_NAMESPACE_ID
, Valueify(&v
)))
7243 if (!JSVAL_IS_PRIMITIVE(v
)) {
7250 ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, obj
, 0, NULL
);
7253 v
= OBJECT_TO_JSVAL(ns
);
7254 if (!obj
->defineProperty(cx
, JS_DEFAULT_XML_NAMESPACE_ID
, Valueify(v
),
7255 PropertyStub
, StrictPropertyStub
, JSPROP_PERMANENT
)) {
7263 js_SetDefaultXMLNamespace(JSContext
*cx
, const Value
&v
)
7266 argv
[0].setString(cx
->runtime
->emptyString
);
7268 JSObject
*ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, NULL
, 2, argv
);
7272 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
7273 JSObject
&varobj
= fp
->varobj(cx
);
7274 if (!varobj
.defineProperty(cx
, JS_DEFAULT_XML_NAMESPACE_ID
, ObjectValue(*ns
),
7275 PropertyStub
, StrictPropertyStub
, JSPROP_PERMANENT
)) {
7282 js_ToAttributeName(JSContext
*cx
, Value
*vp
)
7286 qn
= ToAttributeName(cx
, Jsvalify(*vp
));
7294 js_EscapeAttributeValue(JSContext
*cx
, JSString
*str
, JSBool quote
)
7296 StringBuffer
sb(cx
);
7297 return EscapeAttributeValue(cx
, sb
, str
, quote
);
7301 js_AddAttributePart(JSContext
*cx
, JSBool isName
, JSString
*str
, JSString
*str2
)
7303 size_t len
= str
->length();
7304 const jschar
*chars
= str
->getChars(cx
);
7308 size_t len2
= str2
->length();
7309 const jschar
*chars2
= str2
->getChars(cx
);
7313 size_t newlen
= (isName
) ? len
+ 1 + len2
: len
+ 2 + len2
+ 1;
7314 jschar
*newchars
= (jschar
*) cx
->malloc((newlen
+1) * sizeof(jschar
));
7318 js_strncpy(newchars
, chars
, len
);
7322 js_strncpy(newchars
, chars2
, len2
);
7327 js_strncpy(newchars
, chars2
, len2
);
7332 return js_NewString(cx
, newchars
- newlen
, newlen
);
7336 js_EscapeElementValue(JSContext
*cx
, JSString
*str
)
7338 StringBuffer
sb(cx
);
7339 return EscapeElementValue(cx
, sb
, str
, 0);
7343 js_ValueToXMLString(JSContext
*cx
, const Value
&v
)
7345 return ToXMLString(cx
, Jsvalify(v
), 0);
7349 js_GetAnyName(JSContext
*cx
, jsid
*idp
)
7351 JSObject
*global
= cx
->hasfp() ? cx
->fp()->scopeChain().getGlobal() : cx
->globalObject
;
7352 Value v
= global
->getReservedSlot(JSProto_AnyName
);
7353 if (v
.isUndefined()) {
7354 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_AnyNameClass
, NULL
, global
);
7358 JS_ASSERT(!obj
->getProto());
7360 JSRuntime
*rt
= cx
->runtime
;
7361 InitXMLQName(obj
, rt
->emptyString
, rt
->emptyString
,
7362 ATOM_TO_STRING(rt
->atomState
.starAtom
));
7363 METER(xml_stats
.qname
);
7366 if (!js_SetReservedSlot(cx
, global
, JSProto_AnyName
, v
))
7369 *idp
= OBJECT_TO_JSID(&v
.toObject());
7374 js_FindXMLProperty(JSContext
*cx
, const Value
&nameval
, JSObject
**objp
, jsid
*idp
)
7380 JSObject
*obj
, *target
, *proto
, *pobj
;
7385 JS_ASSERT(nameval
.isObject());
7386 nameobj
= &nameval
.toObject();
7387 if (nameobj
->getClass() == &js_AnyNameClass
) {
7388 v
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
);
7389 nameobj
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 1,
7394 JS_ASSERT(nameobj
->getClass() == &js_AttributeNameClass
||
7395 nameobj
->getClass() == &js_QNameClass
);
7399 if (!IsFunctionQName(cx
, qn
, &funid
))
7402 obj
= &js_GetTopStackFrame(cx
)->scopeChain();
7404 /* Skip any With object that can wrap XML. */
7406 while (target
->getClass() == &js_WithClass
) {
7407 proto
= target
->getProto();
7413 if (target
->isXML()) {
7414 if (JSID_IS_VOID(funid
)) {
7415 xml
= (JSXML
*) target
->getPrivate();
7416 found
= HasNamedProperty(xml
, qn
);
7418 if (!HasFunctionProperty(cx
, target
, funid
, &found
))
7422 *idp
= OBJECT_TO_JSID(nameobj
);
7426 } else if (!JSID_IS_VOID(funid
)) {
7427 if (!target
->lookupProperty(cx
, funid
, &pobj
, &prop
))
7435 } while ((obj
= obj
->getParent()) != NULL
);
7437 JSAutoByteString printable
;
7438 JSString
*str
= ConvertQNameToString(cx
, nameobj
);
7439 if (str
&& js_ValueToPrintable(cx
, StringValue(str
), &printable
)) {
7440 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
7441 JSMSG_UNDEFINED_XML_NAME
, printable
.ptr());
7447 GetXMLFunction(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
7449 JS_ASSERT(obj
->isXML());
7452 * See comments before xml_lookupProperty about the need for the proto
7455 JSObject
*target
= obj
;
7456 AutoObjectRooter
tvr(cx
);
7458 if (!js_GetProperty(cx
, target
, id
, Valueify(vp
)))
7460 if (VALUE_IS_FUNCTION(cx
, *vp
))
7462 target
= target
->getProto();
7463 if (target
== NULL
|| !target
->isNative())
7465 tvr
.setObject(target
);
7468 JSXML
*xml
= (JSXML
*) obj
->getPrivate();
7469 if (!HasSimpleContent(xml
))
7472 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
7473 if (!js_GetClassPrototype(cx
, NULL
, JSProto_String
, tvr
.addr()))
7476 JS_ASSERT(tvr
.object());
7477 return tvr
.object()->getProperty(cx
, id
, Valueify(vp
));
7481 GetPrivate(JSContext
*cx
, JSObject
*obj
, const char *method
)
7485 xml
= (JSXML
*) GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
7487 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
7488 JSMSG_INCOMPATIBLE_METHOD
,
7489 js_XML_str
, method
, obj
->getClass()->name
);
7495 js_GetXMLDescendants(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
7499 xml
= GetPrivate(cx
, obj
, "descendants internal method");
7503 list
= Descendants(cx
, xml
, id
);
7506 *vp
= OBJECT_TO_JSVAL(list
->object
);
7511 js_DeleteXMLListElements(JSContext
*cx
, JSObject
*listobj
)
7516 list
= (JSXML
*) listobj
->getPrivate();
7517 for (n
= list
->xml_kids
.length
; n
!= 0; --n
)
7518 DeleteListElement(cx
, list
, 0);
7528 JSXMLArrayCursor cursor
;
7530 JSXMLFilter(JSXML
*list
, JSXMLArray
*array
)
7531 : list(list
), result(NULL
), kid(NULL
), cursor(array
) {}
7537 xmlfilter_trace(JSTracer
*trc
, JSObject
*obj
)
7539 JSXMLFilter
*filter
= (JSXMLFilter
*) obj
->getPrivate();
7543 JS_ASSERT(filter
->list
);
7544 JS_CALL_TRACER(trc
, filter
->list
, JSTRACE_XML
, "list");
7546 JS_CALL_TRACER(trc
, filter
->result
, JSTRACE_XML
, "result");
7548 JS_CALL_TRACER(trc
, filter
->kid
, JSTRACE_XML
, "kid");
7551 * We do not need to trace the cursor as that would be done when
7552 * tracing the filter->list.
7557 xmlfilter_finalize(JSContext
*cx
, JSObject
*obj
)
7559 JSXMLFilter
*filter
= (JSXMLFilter
*) obj
->getPrivate();
7563 cx
->destroy(filter
);
7566 Class js_XMLFilterClass
= {
7568 JSCLASS_HAS_PRIVATE
| JSCLASS_IS_ANONYMOUS
| JSCLASS_MARK_IS_TRACE
,
7569 PropertyStub
, /* addProperty */
7570 PropertyStub
, /* delProperty */
7571 PropertyStub
, /* getProperty */
7572 StrictPropertyStub
, /* setProperty */
7577 NULL
, /* reserved0 */
7578 NULL
, /* checkAccess */
7580 NULL
, /* construct */
7581 NULL
, /* xdrObject */
7582 NULL
, /* hasInstance */
7583 JS_CLASS_TRACE(xmlfilter_trace
)
7587 js_StepXMLListFilter(JSContext
*cx
, JSBool initialized
)
7590 JSObject
*obj
, *filterobj
, *resobj
, *kidobj
;
7592 JSXMLFilter
*filter
;
7595 sp
= Jsvalify(cx
->regs
->sp
);
7598 * We haven't iterated yet, so initialize the filter based on the
7599 * value stored in sp[-2].
7601 if (!VALUE_IS_XML(sp
[-2])) {
7602 js_ReportValueError(cx
, JSMSG_NON_XML_FILTER
, -2, Valueify(sp
[-2]), NULL
);
7605 obj
= JSVAL_TO_OBJECT(sp
[-2]);
7606 xml
= (JSXML
*) obj
->getPrivate();
7608 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7611 obj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
7616 * Root just-created obj. sp[-2] cannot be used yet for rooting
7617 * as it may be the only root holding xml.
7619 sp
[-1] = OBJECT_TO_JSVAL(obj
);
7620 list
= (JSXML
*) obj
->getPrivate();
7621 if (!Append(cx
, list
, xml
))
7625 filterobj
= NewNonFunction
<WithProto::Given
>(cx
, &js_XMLFilterClass
, NULL
, NULL
);
7630 * Init all filter fields before setPrivate exposes it to
7631 * xmlfilter_trace or xmlfilter_finalize.
7633 filter
= cx
->create
<JSXMLFilter
>(list
, &list
->xml_kids
);
7636 filterobj
->setPrivate(filter
);
7638 /* Store filterobj to use in the later iterations. */
7639 sp
[-2] = OBJECT_TO_JSVAL(filterobj
);
7641 resobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
7645 /* This also roots resobj. */
7646 filter
->result
= (JSXML
*) resobj
->getPrivate();
7648 /* We have iterated at least once. */
7649 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp
[-2]));
7650 JS_ASSERT(JSVAL_TO_OBJECT(sp
[-2])->getClass() == &js_XMLFilterClass
);
7651 filter
= (JSXMLFilter
*) JSVAL_TO_OBJECT(sp
[-2])->getPrivate();
7652 JS_ASSERT(filter
->kid
);
7654 /* Check if the filter expression wants to append the element. */
7655 if (js_ValueToBoolean(Valueify(sp
[-1])) &&
7656 !Append(cx
, filter
->result
, filter
->kid
)) {
7661 /* Do the iteration. */
7662 filter
->kid
= (JSXML
*) filter
->cursor
.getNext();
7665 * Do not defer finishing the cursor until the next GC cycle to avoid
7666 * accumulation of dead cursors associated with filter->list.
7668 filter
->cursor
.disconnect();
7669 JS_ASSERT(filter
->result
->object
);
7670 sp
[-2] = OBJECT_TO_JSVAL(filter
->result
->object
);
7673 kidobj
= js_GetXMLObject(cx
, filter
->kid
);
7678 /* Null as kidobj at sp[-1] signals filter termination. */
7679 sp
[-1] = OBJECT_TO_JSVAL(kidobj
);
7684 js_ValueToXMLObject(JSContext
*cx
, const Value
&v
)
7686 return ToXML(cx
, Jsvalify(v
));
7690 js_ValueToXMLListObject(JSContext
*cx
, const Value
&v
)
7692 return ToXMLList(cx
, Jsvalify(v
));
7696 js_NewXMLSpecialObject(JSContext
*cx
, JSXMLClass xml_class
, JSString
*name
,
7704 if (!GetXMLSettingFlags(cx
, &flags
))
7707 if ((xml_class
== JSXML_CLASS_COMMENT
&&
7708 (flags
& XSF_IGNORE_COMMENTS
)) ||
7709 (xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
&&
7710 (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
))) {
7711 return js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
7714 obj
= js_NewXMLObject(cx
, xml_class
);
7717 xml
= (JSXML
*) obj
->getPrivate();
7719 JSLinearString
*linearName
= name
->ensureLinear(cx
);
7722 qn
= NewXMLQName(cx
, cx
->runtime
->emptyString
, NULL
, linearName
);
7727 xml
->xml_value
= value
;
7732 js_MakeXMLCDATAString(JSContext
*cx
, JSString
*str
)
7734 StringBuffer
sb(cx
);
7735 return MakeXMLCDATAString(cx
, sb
, str
);
7739 js_MakeXMLCommentString(JSContext
*cx
, JSString
*str
)
7741 StringBuffer
sb(cx
);
7742 return MakeXMLCommentString(cx
, sb
, str
);
7746 js_MakeXMLPIString(JSContext
*cx
, JSString
*name
, JSString
*str
)
7748 StringBuffer
sb(cx
);
7749 return MakeXMLPIString(cx
, sb
, name
, str
);
7752 #endif /* JS_HAS_XML_SUPPORT */