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 PropertyStub
, /* 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
)
266 obj
= ComputeThisFromVp(cx
, vp
);
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 PropertyStub
, /* 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 PropertyStub
, /* 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 PropertyStub
, /* 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
= ComputeThisFromVp(cx
, vp
);
455 if (!obj
|| !InstanceOf(cx
, obj
, &js_QNameClass
, vp
+ 2))
458 JSString
*str
= ConvertQNameToString(cx
, obj
);
466 static JSFunctionSpec qname_methods
[] = {
467 JS_FN(js_toString_str
, qname_toString
, 0,0),
473 InitXMLQName(JSObject
*obj
, JSLinearString
*uri
, JSLinearString
*prefix
,
474 JSLinearString
*localName
)
476 JS_ASSERT(obj
->isQName());
477 JS_ASSERT(JSVAL_IS_VOID(obj
->getNamePrefixVal()));
478 JS_ASSERT(JSVAL_IS_VOID(obj
->getNameURIVal()));
479 JS_ASSERT(JSVAL_IS_VOID(obj
->getQNameLocalNameVal()));
481 obj
->setNameURI(uri
);
483 obj
->setNamePrefix(prefix
);
485 obj
->setQNameLocalName(localName
);
489 NewXMLQName(JSContext
*cx
, JSLinearString
*uri
, JSLinearString
*prefix
,
490 JSLinearString
*localName
)
492 JSObject
*obj
= NewBuiltinClassInstanceXML(cx
, &js_QNameClass
);
495 InitXMLQName(obj
, uri
, prefix
, localName
);
496 METER(xml_stats
.qname
);
501 NewXMLAttributeName(JSContext
*cx
, JSLinearString
*uri
, JSLinearString
*prefix
,
502 JSLinearString
*localName
)
505 * AttributeName is an internal anonymous class which instances are not
506 * exposed to scripts.
508 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_AttributeNameClass
, NULL
, NULL
);
511 JS_ASSERT(obj
->isQName());
512 InitXMLQName(obj
, uri
, prefix
, localName
);
513 METER(xml_stats
.qname
);
518 js_ConstructXMLQNameObject(JSContext
*cx
, const Value
&nsval
, const Value
&lnval
)
524 * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
525 * production, step 2.
527 if (nsval
.isObject() &&
528 nsval
.toObject().getClass() == &js_AnyNameClass
) {
534 return js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 2, argv
);
538 IsXMLName(const jschar
*cp
, size_t n
)
544 if (n
!= 0 && JS_ISXMLNSSTART(*cp
)) {
556 js_IsXMLName(JSContext
*cx
, jsval v
)
558 JSLinearString
*name
= NULL
;
559 JSErrorReporter older
;
562 * Inline specialization of the QName constructor called with v passed as
563 * the only argument, to compute the localName for the constructed qname,
564 * without actually allocating the object or computing its uri and prefix.
565 * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
567 if (!JSVAL_IS_PRIMITIVE(v
) &&
568 JSVAL_TO_OBJECT(v
)->isQName()) {
569 name
= JSVAL_TO_OBJECT(v
)->getQNameLocalName();
571 older
= JS_SetErrorReporter(cx
, NULL
);
572 JSString
*str
= js_ValueToString(cx
, Valueify(v
));
574 name
= str
->ensureLinear(cx
);
575 JS_SetErrorReporter(cx
, older
);
577 JS_ClearPendingException(cx
);
582 return IsXMLName(name
->chars(), name
->length());
586 * When argc is -1, it indicates argv is empty but the code should behave as
587 * if argc is 1 and argv[0] is JSVAL_VOID.
590 NamespaceHelper(JSContext
*cx
, JSObject
*obj
, intN argc
, jsval
*argv
,
593 jsval urival
, prefixval
;
595 JSBool isNamespace
, isQName
;
597 JSLinearString
*empty
, *prefix
, *uri
;
599 isNamespace
= isQName
= JS_FALSE
;
600 #ifdef __GNUC__ /* suppress bogus gcc warnings */
606 urival
= argv
[argc
> 1];
607 if (!JSVAL_IS_PRIMITIVE(urival
)) {
608 uriobj
= JSVAL_TO_OBJECT(urival
);
609 clasp
= uriobj
->getClass();
610 isNamespace
= (clasp
== &js_NamespaceClass
);
611 isQName
= (clasp
== &js_QNameClass
);
616 /* Namespace called as function. */
617 if (argc
== 1 && isNamespace
) {
618 /* Namespace called with one Namespace argument is identity. */
623 obj
= NewBuiltinClassInstanceXML(cx
, &js_NamespaceClass
);
627 *rval
= OBJECT_TO_JSVAL(obj
);
628 METER(xml_stats
.xmlnamespace
);
630 empty
= cx
->runtime
->emptyString
;
631 obj
->setNamePrefix(empty
);
632 obj
->setNameURI(empty
);
634 if (argc
== 1 || argc
== -1) {
636 obj
->setNameURI(uriobj
->getNameURI());
637 obj
->setNamePrefix(uriobj
->getNamePrefix());
638 } else if (isQName
&& (uri
= uriobj
->getNameURI())) {
639 obj
->setNameURI(uri
);
640 obj
->setNamePrefix(uriobj
->getNamePrefix());
642 JSString
*str
= js_ValueToString(cx
, Valueify(urival
));
645 uri
= str
->ensureLinear(cx
);
648 obj
->setNameURI(uri
);
650 obj
->clearNamePrefix();
652 } else if (argc
== 2) {
653 if (!isQName
|| !(uri
= uriobj
->getNameURI())) {
654 JSString
*str
= js_ValueToString(cx
, Valueify(urival
));
657 uri
= str
->ensureLinear(cx
);
661 obj
->setNameURI(uri
);
665 if (!JSVAL_IS_VOID(prefixval
)) {
666 JSString
*str
= js_ValueToString(cx
, Valueify(prefixval
));
670 JSAutoByteString bytes
;
671 if (js_ValueToPrintable(cx
, StringValue(str
), &bytes
)) {
672 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
673 JSMSG_BAD_XML_NAMESPACE
, bytes
.ptr());
678 } else if (JSVAL_IS_VOID(prefixval
) || !js_IsXMLName(cx
, prefixval
)) {
679 obj
->clearNamePrefix();
681 JSString
*str
= js_ValueToString(cx
, Valueify(prefixval
));
684 prefix
= str
->ensureLinear(cx
);
687 obj
->setNamePrefix(prefix
);
694 Namespace(JSContext
*cx
, uintN argc
, Value
*vp
)
696 JSObject
*thisobj
= NULL
;
697 (void)IsConstructing_PossiblyWithGivenThisObject(vp
, &thisobj
);
698 return NamespaceHelper(cx
, thisobj
, argc
, Jsvalify(vp
+ 2), Jsvalify(vp
));
702 * When argc is -1, it indicates argv is empty but the code should behave as
703 * if argc is 1 and argv[0] is JSVAL_VOID.
706 QNameHelper(JSContext
*cx
, JSObject
*obj
, intN argc
, jsval
*argv
, jsval
*rval
)
708 jsval nameval
, nsval
;
709 JSBool isQName
, isNamespace
;
711 JSLinearString
*uri
, *prefix
, *name
;
715 nameval
= JSVAL_VOID
;
718 nameval
= argv
[argc
> 1];
720 !JSVAL_IS_PRIMITIVE(nameval
) &&
721 JSVAL_TO_OBJECT(nameval
)->getClass() == &js_QNameClass
;
725 /* QName called as function. */
726 if (argc
== 1 && isQName
) {
727 /* QName called with one QName argument is identity. */
732 /* Create and return a new QName object exactly as if constructed. */
733 obj
= NewBuiltinClassInstanceXML(cx
, &js_QNameClass
);
737 *rval
= OBJECT_TO_JSVAL(obj
);
738 METER(xml_stats
.qname
);
741 /* If namespace is not specified and name is a QName, clone it. */
742 qn
= JSVAL_TO_OBJECT(nameval
);
744 uri
= qn
->getNameURI();
745 prefix
= qn
->getNamePrefix();
746 name
= qn
->getQNameLocalName();
750 /* Namespace and qname were passed -- use the qname's localName. */
751 nameval
= qn
->getQNameLocalNameVal();
755 name
= cx
->runtime
->emptyString
;
756 } else if (argc
< 0) {
757 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
759 JSString
*str
= js_ValueToString(cx
, Valueify(nameval
));
762 name
= str
->ensureLinear(cx
);
765 argv
[argc
> 1] = STRING_TO_JSVAL(name
);
768 if (argc
> 1 && !JSVAL_IS_VOID(argv
[0])) {
770 } else if (IS_STAR(name
)) {
773 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
775 JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval
));
776 JS_ASSERT(JSVAL_TO_OBJECT(nsval
)->getClass() ==
780 if (JSVAL_IS_NULL(nsval
)) {
781 /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
785 * Inline specialization of the Namespace constructor called with
786 * nsval passed as the only argument, to compute the uri and prefix
787 * for the constructed namespace, without actually allocating the
788 * object or computing other members. See ECMA-357 13.3.2 6(a) and
791 isNamespace
= isQName
= JS_FALSE
;
792 if (!JSVAL_IS_PRIMITIVE(nsval
)) {
793 obj2
= JSVAL_TO_OBJECT(nsval
);
794 isNamespace
= (obj2
->getClass() == &js_NamespaceClass
);
795 isQName
= (obj2
->getClass() == &js_QNameClass
);
797 #ifdef __GNUC__ /* suppress bogus gcc warnings */
802 uri
= obj2
->getNameURI();
803 prefix
= obj2
->getNamePrefix();
804 } else if (isQName
&& (uri
= obj2
->getNameURI())) {
806 prefix
= obj2
->getNamePrefix();
809 JSString
*str
= js_ValueToString(cx
, Valueify(nsval
));
812 uri
= str
->ensureLinear(cx
);
815 argv
[0] = STRING_TO_JSVAL(uri
); /* local root */
817 /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
818 prefix
= uri
->empty() ? cx
->runtime
->emptyString
: NULL
;
823 InitXMLQName(obj
, uri
, prefix
, name
);
828 QName(JSContext
*cx
, uintN argc
, Value
*vp
)
830 JSObject
*thisobj
= NULL
;
831 (void)IsConstructing_PossiblyWithGivenThisObject(vp
, &thisobj
);
832 return QNameHelper(cx
, thisobj
, argc
, Jsvalify(vp
+ 2), Jsvalify(vp
));
836 * XMLArray library functions.
839 namespace_identity(const void *a
, const void *b
)
841 const JSObject
*nsa
= (const JSObject
*) a
;
842 const JSObject
*nsb
= (const JSObject
*) b
;
843 JSLinearString
*prefixa
= nsa
->getNamePrefix();
844 JSLinearString
*prefixb
= nsb
->getNamePrefix();
846 if (prefixa
&& prefixb
) {
847 if (!EqualStrings(prefixa
, prefixb
))
850 if (prefixa
|| prefixb
)
853 return EqualStrings(nsa
->getNameURI(), nsb
->getNameURI());
857 attr_identity(const void *a
, const void *b
)
859 const JSXML
*xmla
= (const JSXML
*) a
;
860 const JSXML
*xmlb
= (const JSXML
*) b
;
862 return qname_identity(xmla
->name
, xmlb
->name
);
866 JSXMLArrayCursor::trace(JSTracer
*trc
) {
870 for (JSXMLArrayCursor
*cursor
= this; cursor
; cursor
= cursor
->next
)
871 js::gc::MarkGCThing(trc
, cursor
->root
, "cursor_root", index
++);
875 XMLArrayCursorTrace(JSTracer
*trc
, JSXMLArrayCursor
*cursor
)
880 /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
882 JSXMLArray::setCapacity(JSContext
*cx
, uint32 newCapacity
)
884 if (newCapacity
== 0) {
885 /* We could let realloc(p, 0) free this, but purify gets confused. */
897 #if JS_BITS_PER_WORD == 32
898 (size_t)newCapacity
> ~(size_t)0 / sizeof(void *) ||
900 !(tmp
= (void **) js_realloc(vector
, newCapacity
* sizeof(void *)))) {
902 JS_ReportOutOfMemory(cx
);
907 capacity
= JSXML_PRESET_CAPACITY
| newCapacity
;
914 if (capacity
& JSXML_PRESET_CAPACITY
)
916 if (length
< capacity
)
917 setCapacity(NULL
, length
);
921 JSXMLArray::finish(JSContext
*cx
)
925 while (JSXMLArrayCursor
*cursor
= cursors
)
926 cursor
->disconnect();
929 memset(this, 0xd5, sizeof *this);
933 #define XML_NOT_FOUND ((uint32) -1)
936 XMLArrayFindMember(const JSXMLArray
*array
, void *elt
, JSIdentityOp identity
)
941 /* The identity op must not reallocate array->vector. */
942 vector
= array
->vector
;
944 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
945 if (identity(vector
[i
], elt
))
949 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
950 if (vector
[i
] == elt
)
954 return XML_NOT_FOUND
;
958 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
959 * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
960 * should be greater than increment.
962 #define LINEAR_THRESHOLD 256
963 #define LINEAR_INCREMENT 32
966 XMLArrayAddMember(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, void *elt
)
972 if (index
>= array
->length
) {
973 if (index
>= JSXML_CAPACITY(array
)) {
974 /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
975 capacity
= index
+ 1;
976 if (index
>= LINEAR_THRESHOLD
) {
977 capacity
= JS_ROUNDUP(capacity
, LINEAR_INCREMENT
);
979 JS_CEILING_LOG2(log2
, capacity
);
980 capacity
= JS_BIT(log2
);
983 #if JS_BITS_PER_WORD == 32
984 (size_t)capacity
> ~(size_t)0 / sizeof(void *) ||
987 js_realloc(array
->vector
, capacity
* sizeof(void *)))) {
988 JS_ReportOutOfMemory(cx
);
991 array
->capacity
= capacity
;
992 array
->vector
= vector
;
993 for (i
= array
->length
; i
< index
; i
++)
996 array
->length
= index
+ 1;
999 array
->vector
[index
] = elt
;
1004 XMLArrayInsert(JSContext
*cx
, JSXMLArray
*array
, uint32 i
, uint32 n
)
1007 JSXMLArrayCursor
*cursor
;
1011 if (!array
->setCapacity(cx
, j
+ n
))
1014 array
->length
= j
+ n
;
1015 JS_ASSERT(n
!= (uint32
)-1);
1018 array
->vector
[j
+ n
] = array
->vector
[j
];
1021 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1022 if (cursor
->index
> i
)
1029 XMLArrayDelete(JSContext
*cx
, JSXMLArray
*array
, uint32 index
, JSBool compress
)
1032 void **vector
, *elt
;
1033 JSXMLArrayCursor
*cursor
;
1035 length
= array
->length
;
1036 if (index
>= length
)
1039 vector
= array
->vector
;
1040 elt
= vector
[index
];
1042 while (++index
< length
)
1043 vector
[index
-1] = vector
[index
];
1044 array
->length
= length
- 1;
1045 array
->capacity
= JSXML_CAPACITY(array
);
1047 vector
[index
] = NULL
;
1050 for (cursor
= array
->cursors
; cursor
; cursor
= cursor
->next
) {
1051 if (cursor
->index
> index
)
1058 XMLArrayTruncate(JSContext
*cx
, JSXMLArray
*array
, uint32 length
)
1062 JS_ASSERT(!array
->cursors
);
1063 if (length
>= array
->length
)
1068 cx
->free(array
->vector
);
1071 vector
= (void **) js_realloc(array
->vector
, length
* sizeof(void *));
1076 if (array
->length
> length
)
1077 array
->length
= length
;
1078 array
->capacity
= length
;
1079 array
->vector
= vector
;
1082 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1083 #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1085 #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1086 ? (t *) (a)->vector[i] \
1088 #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1089 if ((a)->length <= (i)) \
1090 (a)->length = (i) + 1; \
1091 ((a)->vector[i] = (void *)(e)); \
1093 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1094 #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1095 #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1096 #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1097 #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1100 * Define XML setting property strings and constants early, so everyone can
1101 * use the same names.
1103 static const char js_ignoreComments_str
[] = "ignoreComments";
1104 static const char js_ignoreProcessingInstructions_str
[]
1105 = "ignoreProcessingInstructions";
1106 static const char js_ignoreWhitespace_str
[] = "ignoreWhitespace";
1107 static const char js_prettyPrinting_str
[] = "prettyPrinting";
1108 static const char js_prettyIndent_str
[] = "prettyIndent";
1110 #define XSF_IGNORE_COMMENTS JS_BIT(0)
1111 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1112 #define XSF_IGNORE_WHITESPACE JS_BIT(2)
1113 #define XSF_PRETTY_PRINTING JS_BIT(3)
1115 static JSPropertySpec xml_static_props
[] = {
1116 {js_ignoreComments_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1117 {js_ignoreProcessingInstructions_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1118 {js_ignoreWhitespace_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1119 {js_prettyPrinting_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1120 {js_prettyIndent_str
, 0, JSPROP_PERMANENT
, NULL
, NULL
},
1124 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1125 #define IS_XML(str) \
1126 (str->length() == 3 && IS_XML_CHARS(str->chars()))
1128 #define IS_XMLNS(str) \
1129 (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1131 #define IS_XML_CHARS(chars) \
1132 (JS_TOLOWER((chars)[0]) == 'x' && \
1133 JS_TOLOWER((chars)[1]) == 'm' && \
1134 JS_TOLOWER((chars)[2]) == 'l')
1136 #define HAS_NS_AFTER_XML(chars) \
1137 (JS_TOLOWER((chars)[3]) == 'n' && \
1138 JS_TOLOWER((chars)[4]) == 's')
1140 #define IS_XMLNS_CHARS(chars) \
1141 (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1143 #define STARTS_WITH_XML(chars,length) \
1144 (length >= 3 && IS_XML_CHARS(chars))
1146 static const char xml_namespace_str
[] = "http://www.w3.org/XML/1998/namespace";
1147 static const char xmlns_namespace_str
[] = "http://www.w3.org/2000/xmlns/";
1150 ParseNodeToQName(Parser
*parser
, JSParseNode
*pn
,
1151 JSXMLArray
*inScopeNSes
, JSBool isAttributeName
)
1153 JSContext
*cx
= parser
->context
;
1154 JSLinearString
*str
, *uri
, *prefix
, *localName
;
1155 size_t length
, offset
;
1156 const jschar
*start
, *limit
, *colon
;
1159 JSLinearString
*nsprefix
;
1161 JS_ASSERT(pn
->pn_arity
== PN_NULLARY
);
1163 start
= str
->chars();
1164 length
= str
->length();
1165 JS_ASSERT(length
!= 0 && *start
!= '@');
1166 JS_ASSERT(length
!= 1 || *start
!= '*');
1168 uri
= cx
->runtime
->emptyString
;
1169 limit
= start
+ length
;
1170 colon
= js_strchr_limit(start
, ':', limit
);
1172 offset
= colon
- start
;
1173 prefix
= js_NewDependentString(cx
, str
, 0, offset
);
1177 if (STARTS_WITH_XML(start
, offset
)) {
1179 uri
= JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx
, xml_namespace_str
));
1182 } else if (offset
== 5 && HAS_NS_AFTER_XML(start
)) {
1183 uri
= JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx
, xmlns_namespace_str
));
1191 n
= inScopeNSes
->length
;
1194 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSObject
);
1195 nsprefix
= ns
->getNamePrefix();
1196 if (nsprefix
&& EqualStrings(nsprefix
, prefix
)) {
1197 uri
= ns
->getNameURI();
1204 Value v
= StringValue(prefix
);
1205 JSAutoByteString bytes
;
1206 if (js_ValueToPrintable(parser
->context
, v
, &bytes
)) {
1207 ReportCompileErrorNumber(parser
->context
, &parser
->tokenStream
, pn
,
1208 JSREPORT_ERROR
, JSMSG_BAD_XML_NAMESPACE
, bytes
.ptr());
1213 localName
= js_NewStringCopyN(parser
->context
, colon
+ 1, length
- (offset
+ 1));
1217 if (isAttributeName
) {
1219 * An unprefixed attribute is not in any namespace, so set prefix
1220 * as well as uri to the empty string.
1225 * Loop from back to front looking for the closest declared default
1228 n
= inScopeNSes
->length
;
1231 ns
= XMLARRAY_MEMBER(inScopeNSes
, n
, JSObject
);
1232 nsprefix
= ns
->getNamePrefix();
1233 if (!nsprefix
|| nsprefix
->empty()) {
1234 uri
= ns
->getNameURI();
1238 prefix
= uri
->empty() ? parser
->context
->runtime
->emptyString
: NULL
;
1243 return NewXMLQName(parser
->context
, uri
, prefix
, localName
);
1247 ChompXMLWhitespace(JSContext
*cx
, JSString
*str
)
1249 size_t length
, newlength
, offset
;
1250 const jschar
*cp
, *start
, *end
;
1253 length
= str
->length();
1254 start
= str
->getChars(cx
);
1258 for (cp
= start
, end
= cp
+ length
; cp
< end
; cp
++) {
1260 if (!JS_ISXMLSPACE(c
))
1265 if (!JS_ISXMLSPACE(c
))
1269 newlength
= end
- cp
;
1270 if (newlength
== length
)
1272 offset
= cp
- start
;
1273 return js_NewDependentString(cx
, str
, offset
, newlength
);
1277 ParseNodeToXML(Parser
*parser
, JSParseNode
*pn
,
1278 JSXMLArray
*inScopeNSes
, uintN flags
)
1280 JSContext
*cx
= parser
->context
;
1281 JSXML
*xml
, *kid
, *attr
, *attrj
;
1282 JSLinearString
*str
;
1283 uint32 length
, n
, i
, j
;
1284 JSParseNode
*pn2
, *pn3
, *head
, **pnp
;
1286 JSObject
*qn
, *attrjqn
;
1287 JSXMLClass xml_class
;
1290 if (!JS_CHECK_STACK_SIZE(cx
->stackLimit
, &stackDummy
)) {
1291 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn
, JSREPORT_ERROR
,
1292 JSMSG_OVER_RECURSED
);
1296 #define PN2X_SKIP_CHILD ((JSXML *) 1)
1299 * Cases return early to avoid common code that gets an outermost xml's
1300 * object, which protects GC-things owned by xml and its descendants from
1301 * garbage collection.
1304 if (!js_EnterLocalRootScope(cx
))
1306 switch (pn
->pn_type
) {
1308 length
= inScopeNSes
->length
;
1310 xml
= ParseNodeToXML(parser
, pn2
, inScopeNSes
, flags
);
1317 if (!xml
->xml_kids
.setCapacity(cx
, n
))
1321 while ((pn2
= pn2
->pn_next
) != NULL
) {
1322 if (!pn2
->pn_next
) {
1323 /* Don't append the end tag! */
1324 JS_ASSERT(pn2
->pn_type
== TOK_XMLETAGO
);
1328 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1329 n
> 1 && pn2
->pn_type
== TOK_XMLSPACE
) {
1334 kid
= ParseNodeToXML(parser
, pn2
, inScopeNSes
, flags
);
1335 if (kid
== PN2X_SKIP_CHILD
) {
1343 /* Store kid in xml right away, to protect it from GC. */
1344 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1348 /* XXX where is this documented in an XML spec, or in E4X? */
1349 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
1350 n
> 1 && kid
->xml_class
== JSXML_CLASS_TEXT
) {
1351 JSString
*str
= ChompXMLWhitespace(cx
, kid
->xml_value
);
1354 kid
->xml_value
= str
;
1359 if (n
< pn
->pn_count
- 2)
1360 xml
->xml_kids
.trim();
1361 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1365 xml
= js_NewXML(cx
, JSXML_CLASS_LIST
);
1370 if (!xml
->xml_kids
.setCapacity(cx
, n
))
1374 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
1376 * Always ignore insignificant whitespace in lists -- we shouldn't
1377 * condition this on an XML.ignoreWhitespace setting when the list
1378 * constructor is XMLList (note XML/XMLList unification hazard).
1380 if (pn2
->pn_type
== TOK_XMLSPACE
) {
1385 kid
= ParseNodeToXML(parser
, pn2
, inScopeNSes
, flags
);
1386 if (kid
== PN2X_SKIP_CHILD
) {
1394 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, kid
);
1398 if (n
< pn
->pn_count
)
1399 xml
->xml_kids
.trim();
1404 length
= inScopeNSes
->length
;
1406 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
);
1407 if (pn2
->pn_arity
== PN_LIST
)
1410 xml
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
1414 /* First pass: check syntax and process namespace declarations. */
1415 JS_ASSERT(pn
->pn_count
>= 1);
1416 n
= pn
->pn_count
- 1;
1417 pnp
= &pn2
->pn_next
;
1419 while ((pn2
= *pnp
) != NULL
) {
1421 const jschar
*chars
;
1423 if (pn2
->pn_type
!= TOK_XMLNAME
|| pn2
->pn_arity
!= PN_NULLARY
)
1426 /* Enforce "Well-formedness constraint: Unique Att Spec". */
1427 for (pn3
= head
; pn3
!= pn2
; pn3
= pn3
->pn_next
->pn_next
) {
1428 if (pn3
->pn_atom
== pn2
->pn_atom
) {
1429 Value v
= StringValue(ATOM_TO_STRING(pn2
->pn_atom
));
1430 JSAutoByteString bytes
;
1431 if (js_ValueToPrintable(cx
, v
, &bytes
)) {
1432 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn2
,
1433 JSREPORT_ERROR
, JSMSG_DUPLICATE_XML_ATTR
,
1440 JSAtom
*atom
= pn2
->pn_atom
;
1443 if (pn2
->pn_type
!= TOK_XMLATTR
)
1446 chars
= atom
->chars();
1447 length
= atom
->length();
1449 IS_XMLNS_CHARS(chars
) &&
1450 (length
== 5 || chars
[5] == ':')) {
1451 JSLinearString
*uri
, *prefix
;
1453 uri
= ATOM_TO_STRING(pn2
->pn_atom
);
1455 /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1456 prefix
= cx
->runtime
->emptyString
;
1458 prefix
= js_NewStringCopyN(cx
, chars
+ 6, length
- 6);
1464 * Once the new ns is appended to xml->xml_namespaces, it is
1465 * protected from GC by the object that owns xml -- which is
1466 * either xml->object if outermost, or the object owning xml's
1467 * oldest ancestor if !outermost.
1469 ns
= NewXMLNamespace(cx
, prefix
, uri
, JS_TRUE
);
1474 * Don't add a namespace that's already in scope. If someone
1475 * extracts a child property from its parent via [[Get]], then
1476 * we enforce the invariant, noted many times in ECMA-357, that
1477 * the child's namespaces form a possibly-improper superset of
1478 * its ancestors' namespaces.
1480 if (!XMLARRAY_HAS_MEMBER(inScopeNSes
, ns
, namespace_identity
)) {
1481 if (!XMLARRAY_APPEND(cx
, inScopeNSes
, ns
) ||
1482 !XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
)) {
1489 *pnp
= pn2
->pn_next
;
1490 /* XXXbe recycle pn2 */
1494 pnp
= &pn2
->pn_next
;
1497 xml
->xml_namespaces
.trim();
1499 /* Second pass: process tag name and attributes, using namespaces. */
1501 qn
= ParseNodeToQName(parser
, pn2
, inScopeNSes
, JS_FALSE
);
1506 JS_ASSERT((n
& 1) == 0);
1508 if (!xml
->xml_attrs
.setCapacity(cx
, n
))
1511 for (i
= 0; (pn2
= pn2
->pn_next
) != NULL
; i
++) {
1512 qn
= ParseNodeToQName(parser
, pn2
, inScopeNSes
, JS_TRUE
);
1514 xml
->xml_attrs
.length
= i
;
1519 * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1520 * this time checking local name and namespace URI.
1522 for (j
= 0; j
< i
; j
++) {
1523 attrj
= XMLARRAY_MEMBER(&xml
->xml_attrs
, j
, JSXML
);
1524 attrjqn
= attrj
->name
;
1525 if (EqualStrings(attrjqn
->getNameURI(), qn
->getNameURI()) &&
1526 EqualStrings(attrjqn
->getQNameLocalName(), qn
->getQNameLocalName())) {
1527 Value v
= StringValue(ATOM_TO_STRING(pn2
->pn_atom
));
1528 JSAutoByteString bytes
;
1529 if (js_ValueToPrintable(cx
, v
, &bytes
)) {
1530 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn2
,
1531 JSREPORT_ERROR
, JSMSG_DUPLICATE_XML_ATTR
,
1540 JS_ASSERT(pn2
->pn_type
== TOK_XMLATTR
);
1542 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
1546 XMLARRAY_SET_MEMBER(&xml
->xml_attrs
, i
, attr
);
1549 attr
->xml_value
= ATOM_TO_STRING(pn2
->pn_atom
);
1552 /* Point tag closes its own namespace scope. */
1553 if (pn
->pn_type
== TOK_XMLPTAGC
)
1554 XMLARRAY_TRUNCATE(cx
, inScopeNSes
, length
);
1560 case TOK_XMLCOMMENT
:
1562 str
= ATOM_TO_STRING(pn
->pn_atom
);
1564 if (pn
->pn_type
== TOK_XMLCOMMENT
) {
1565 if (flags
& XSF_IGNORE_COMMENTS
)
1567 xml_class
= JSXML_CLASS_COMMENT
;
1568 } else if (pn
->pn_type
== TOK_XMLPI
) {
1570 Value v
= StringValue(str
);
1571 JSAutoByteString bytes
;
1572 if (js_ValueToPrintable(cx
, v
, &bytes
)) {
1573 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn
,
1574 JSREPORT_ERROR
, JSMSG_RESERVED_ID
, bytes
.ptr());
1579 if (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
)
1582 qn
= ParseNodeToQName(parser
, pn
, inScopeNSes
, JS_FALSE
);
1587 ? ATOM_TO_STRING(pn
->pn_atom2
)
1588 : cx
->runtime
->emptyString
;
1589 xml_class
= JSXML_CLASS_PROCESSING_INSTRUCTION
;
1591 /* CDATA section content, or element text. */
1592 xml_class
= JSXML_CLASS_TEXT
;
1595 xml
= js_NewXML(cx
, xml_class
);
1599 if (pn
->pn_type
== TOK_XMLSPACE
)
1600 xml
->xml_flags
|= XMLF_WHITESPACE_TEXT
;
1601 xml
->xml_value
= str
;
1608 js_LeaveLocalRootScopeWithResult(cx
, xml
);
1612 js_LeaveLocalRootScope(cx
);
1613 return PN2X_SKIP_CHILD
;
1615 #undef PN2X_SKIP_CHILD
1618 ReportCompileErrorNumber(cx
, &parser
->tokenStream
, pn
, JSREPORT_ERROR
, JSMSG_BAD_XML_MARKUP
);
1620 js_LeaveLocalRootScope(cx
);
1625 * XML helper, object-ops, and library functions. We start with the helpers,
1626 * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1629 GetXMLSetting(JSContext
*cx
, const char *name
, jsval
*vp
)
1633 if (!js_FindClassObject(cx
, NULL
, JSProto_XML
, Valueify(&v
)))
1635 if (!VALUE_IS_FUNCTION(cx
, v
)) {
1639 return JS_GetProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
);
1643 GetBooleanXMLSetting(JSContext
*cx
, const char *name
, JSBool
*bp
)
1647 return GetXMLSetting(cx
, name
, &v
) && JS_ValueToBoolean(cx
, v
, bp
);
1651 GetUint32XMLSetting(JSContext
*cx
, const char *name
, uint32
*uip
)
1655 return GetXMLSetting(cx
, name
, &v
) && JS_ValueToECMAUint32(cx
, v
, uip
);
1659 GetXMLSettingFlags(JSContext
*cx
, uintN
*flagsp
)
1663 if (!GetBooleanXMLSetting(cx
, js_ignoreComments_str
, &flag
[0]) ||
1664 !GetBooleanXMLSetting(cx
, js_ignoreProcessingInstructions_str
, &flag
[1]) ||
1665 !GetBooleanXMLSetting(cx
, js_ignoreWhitespace_str
, &flag
[2]) ||
1666 !GetBooleanXMLSetting(cx
, js_prettyPrinting_str
, &flag
[3])) {
1671 for (size_t n
= 0; n
< 4; ++n
)
1673 *flagsp
|= JS_BIT(n
);
1678 ParseXMLSource(JSContext
*cx
, JSString
*src
)
1681 JSLinearString
*uri
;
1682 size_t urilen
, srclen
, length
, offset
, dstlen
;
1684 const jschar
*srcp
, *endp
;
1686 const char *filename
;
1690 static const char prefix
[] = "<parent xmlns=\"";
1691 static const char middle
[] = "\">";
1692 static const char suffix
[] = "</parent>";
1694 #define constrlen(constr) (sizeof(constr) - 1)
1696 if (!js_GetDefaultXMLNamespace(cx
, &nsval
))
1698 uri
= JSVAL_TO_OBJECT(nsval
)->getNameURI();
1699 uri
= js_EscapeAttributeValue(cx
, uri
, JS_FALSE
);
1703 urilen
= uri
->length();
1704 srclen
= src
->length();
1705 length
= constrlen(prefix
) + urilen
+ constrlen(middle
) + srclen
+
1708 chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
1713 js_InflateStringToBuffer(cx
, prefix
, constrlen(prefix
), chars
, &dstlen
);
1715 js_strncpy(chars
+ offset
, uri
->chars(), urilen
);
1717 dstlen
= length
- offset
+ 1;
1718 js_InflateStringToBuffer(cx
, middle
, constrlen(middle
), chars
+ offset
,
1721 srcp
= src
->getChars(cx
);
1726 js_strncpy(chars
+ offset
, srcp
, srclen
);
1728 dstlen
= length
- offset
+ 1;
1729 js_InflateStringToBuffer(cx
, suffix
, constrlen(suffix
), chars
+ offset
,
1731 chars
[offset
+ dstlen
] = 0;
1735 FrameRegsIter
i(cx
);
1736 for (; !i
.done() && !i
.pc(); ++i
)
1737 JS_ASSERT(!i
.fp()->isScriptFrame());
1741 JSStackFrame
*fp
= i
.fp();
1742 op
= (JSOp
) *i
.pc();
1743 if (op
== JSOP_TOXML
|| op
== JSOP_TOXMLLIST
) {
1744 filename
= fp
->script()->filename
;
1745 lineno
= js_FramePCToLineNumber(cx
, fp
);
1746 for (endp
= srcp
+ srclen
; srcp
< endp
; srcp
++) {
1755 if (parser
.init(chars
, length
, filename
, lineno
)) {
1756 JSObject
*scopeChain
= GetScopeChain(cx
);
1761 JSParseNode
*pn
= parser
.parseXMLText(scopeChain
, false);
1763 if (pn
&& GetXMLSettingFlags(cx
, &flags
)) {
1764 AutoNamespaceArray
namespaces(cx
);
1765 if (namespaces
.array
.setCapacity(cx
, 1))
1766 xml
= ParseNodeToXML(&parser
, pn
, &namespaces
.array
, flags
);
1778 * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1780 * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1783 * for all x belonging to XML:
1784 * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1786 * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1787 * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1789 * Same goes for 10.4.1 Step 7(a).
1791 * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1792 * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1793 * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1794 * undeclared namespaces associated with x not belonging to ancestorNS.
1797 OrphanXMLChild(JSContext
*cx
, JSXML
*xml
, uint32 i
)
1801 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, 0, JSObject
);
1802 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
1805 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
1806 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
1808 ns
->setNamespaceDeclared(JSVAL_VOID
);
1815 ToXML(JSContext
*cx
, jsval v
)
1823 if (JSVAL_IS_PRIMITIVE(v
)) {
1824 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
1827 obj
= JSVAL_TO_OBJECT(v
);
1829 xml
= (JSXML
*) obj
->getPrivate();
1830 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
1831 if (xml
->xml_kids
.length
!= 1)
1833 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
1835 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
1836 return js_GetXMLObject(cx
, xml
);
1842 clasp
= obj
->getClass();
1843 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
1847 if (clasp
!= &js_StringClass
&&
1848 clasp
!= &js_NumberClass
&&
1849 clasp
!= &js_BooleanClass
) {
1854 str
= js_ValueToString(cx
, Valueify(v
));
1859 #ifdef __GNUC__ /* suppress bogus gcc warnings */
1863 xml
= ParseXMLSource(cx
, str
);
1866 length
= JSXML_LENGTH(xml
);
1870 obj
= js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
1873 } else if (length
== 1) {
1874 xml
= OrphanXMLChild(cx
, xml
, 0);
1877 obj
= js_GetXMLObject(cx
, xml
);
1881 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_SYNTAX_ERROR
);
1887 js_ReportValueError(cx
, JSMSG_BAD_XML_CONVERSION
,
1888 JSDVG_IGNORE_STACK
, Valueify(v
), NULL
);
1893 Append(JSContext
*cx
, JSXML
*list
, JSXML
*kid
);
1896 ToXMLList(JSContext
*cx
, jsval v
)
1898 JSObject
*obj
, *listobj
;
1899 JSXML
*xml
, *list
, *kid
;
1904 if (JSVAL_IS_PRIMITIVE(v
)) {
1905 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
1908 obj
= JSVAL_TO_OBJECT(v
);
1910 xml
= (JSXML
*) obj
->getPrivate();
1911 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
1912 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
1915 list
= (JSXML
*) listobj
->getPrivate();
1916 if (!Append(cx
, list
, xml
))
1923 clasp
= obj
->getClass();
1924 if (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
) {
1928 if (clasp
!= &js_StringClass
&&
1929 clasp
!= &js_NumberClass
&&
1930 clasp
!= &js_BooleanClass
) {
1935 str
= js_ValueToString(cx
, Valueify(v
));
1942 if (!js_EnterLocalRootScope(cx
))
1944 xml
= ParseXMLSource(cx
, str
);
1946 js_LeaveLocalRootScope(cx
);
1949 length
= JSXML_LENGTH(xml
);
1952 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
1954 list
= (JSXML
*) listobj
->getPrivate();
1955 for (i
= 0; i
< length
; i
++) {
1956 kid
= OrphanXMLChild(cx
, xml
, i
);
1957 if (!kid
|| !Append(cx
, list
, kid
)) {
1965 js_LeaveLocalRootScopeWithResult(cx
, listobj
);
1969 js_ReportValueError(cx
, JSMSG_BAD_XMLLIST_CONVERSION
,
1970 JSDVG_IGNORE_STACK
, Valueify(v
), NULL
);
1975 * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
1976 * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
1977 * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
1978 * MakeXMLSpecialString subroutine.
1980 * These functions mutate sb, leaving it empty.
1982 static JSFlatString
*
1983 MakeXMLSpecialString(JSContext
*cx
, StringBuffer
&sb
,
1984 JSString
*str
, JSString
*str2
,
1985 const jschar
*prefix
, size_t prefixlength
,
1986 const jschar
*suffix
, size_t suffixlength
)
1988 if (!sb
.append(prefix
, prefixlength
) || !sb
.append(str
))
1990 if (str2
&& !str2
->empty()) {
1991 if (!sb
.append(' ') || !sb
.append(str2
))
1994 if (!sb
.append(suffix
, suffixlength
))
1997 return sb
.finishString();
2000 static JSFlatString
*
2001 MakeXMLCDATAString(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
)
2003 static const jschar cdata_prefix_ucNstr
[] = {'<', '!', '[',
2004 'C', 'D', 'A', 'T', 'A',
2006 static const jschar cdata_suffix_ucNstr
[] = {']', ']', '>'};
2008 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2009 cdata_prefix_ucNstr
, 9,
2010 cdata_suffix_ucNstr
, 3);
2013 static JSFlatString
*
2014 MakeXMLCommentString(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
)
2016 static const jschar comment_prefix_ucNstr
[] = {'<', '!', '-', '-'};
2017 static const jschar comment_suffix_ucNstr
[] = {'-', '-', '>'};
2019 return MakeXMLSpecialString(cx
, sb
, str
, NULL
,
2020 comment_prefix_ucNstr
, 4,
2021 comment_suffix_ucNstr
, 3);
2024 static JSFlatString
*
2025 MakeXMLPIString(JSContext
*cx
, StringBuffer
&sb
, JSString
*name
,
2028 static const jschar pi_prefix_ucNstr
[] = {'<', '?'};
2029 static const jschar pi_suffix_ucNstr
[] = {'?', '>'};
2031 return MakeXMLSpecialString(cx
, sb
, name
, value
,
2032 pi_prefix_ucNstr
, 2,
2033 pi_suffix_ucNstr
, 2);
2037 * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2038 * equals, a double quote, an attribute value, and a closing double quote.
2041 AppendAttributeValue(JSContext
*cx
, StringBuffer
&sb
, JSString
*valstr
)
2043 if (!sb
.append('='))
2045 valstr
= js_EscapeAttributeValue(cx
, valstr
, JS_TRUE
);
2046 return valstr
&& sb
.append(valstr
);
2050 * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2052 * These functions mutate sb, leaving it empty.
2054 static JSFlatString
*
2055 EscapeElementValue(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
, uint32 toSourceFlag
)
2057 size_t length
= str
->length();
2058 const jschar
*start
= str
->getChars(cx
);
2062 for (const jschar
*cp
= start
, *end
= start
+ length
; cp
!= end
; ++cp
) {
2066 if (!sb
.append(js_lt_entity_str
))
2070 if (!sb
.append(js_gt_entity_str
))
2074 if (!sb
.append(js_amp_entity_str
))
2079 * If EscapeElementValue is called by toSource/uneval, we also need
2080 * to escape '{'. See bug 463360.
2083 if (!sb
.append(js_leftcurly_entity_str
))
2093 return sb
.finishString();
2097 * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2099 * These functions mutate sb, leaving it empty.
2101 static JSFlatString
*
2102 EscapeAttributeValue(JSContext
*cx
, StringBuffer
&sb
, JSString
*str
, JSBool quote
)
2104 size_t length
= str
->length();
2105 const jschar
*start
= str
->getChars(cx
);
2109 if (quote
&& !sb
.append('"'))
2112 for (const jschar
*cp
= start
, *end
= start
+ length
; cp
!= end
; ++cp
) {
2116 if (!sb
.append(js_quot_entity_str
))
2120 if (!sb
.append(js_lt_entity_str
))
2124 if (!sb
.append(js_amp_entity_str
))
2128 if (!sb
.append("
"))
2132 if (!sb
.append("
"))
2136 if (!sb
.append("	"))
2145 if (quote
&& !sb
.append('"'))
2148 return sb
.finishString();
2151 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2153 GetNamespace(JSContext
*cx
, JSObject
*qn
, const JSXMLArray
*inScopeNSes
)
2155 JSLinearString
*uri
, *prefix
, *nsprefix
;
2156 JSObject
*match
, *ns
;
2160 uri
= qn
->getNameURI();
2161 prefix
= qn
->getNamePrefix();
2164 JSAutoByteString bytes
;
2165 const char *s
= !prefix
?
2167 : js_ValueToPrintable(cx
, StringValue(prefix
), &bytes
);
2169 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_XML_NAMESPACE
, s
);
2173 /* Look for a matching namespace in inScopeNSes, if provided. */
2176 for (i
= 0, n
= inScopeNSes
->length
; i
< n
; i
++) {
2177 ns
= XMLARRAY_MEMBER(inScopeNSes
, i
, JSObject
);
2182 * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2183 * If we preserve prefixes, we must match null prefix against
2184 * an empty prefix of ns, in order to avoid generating redundant
2185 * prefixed and default namespaces for cases such as:
2187 * x = <t xmlns="http://foo.com"/>
2188 * print(x.toXMLString());
2190 * Per 10.3.2.1, the namespace attribute in t has an empty string
2191 * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2193 * 1. If the [local name] property of a is "xmlns"
2194 * a. Map ns.prefix to the empty string
2196 * But t's name has a null prefix in this implementation, meaning
2197 * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2198 * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2199 * saying how "no value" maps to an ECMA-357 value -- but it must
2200 * map to the *undefined* prefix value).
2202 * Since "" != undefined (or null, in the current implementation)
2203 * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2204 * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2205 * This spec bug leads to ToXMLString results that duplicate the
2206 * declared namespace.
2208 if (EqualStrings(ns
->getNameURI(), uri
)) {
2209 nsprefix
= ns
->getNamePrefix();
2210 if (nsprefix
== prefix
||
2211 ((nsprefix
&& prefix
)
2212 ? EqualStrings(nsprefix
, prefix
)
2213 : (nsprefix
? nsprefix
: prefix
)->empty())) {
2221 /* If we didn't match, make a new namespace from qn. */
2223 argv
[0] = prefix
? STRING_TO_JSVAL(prefix
) : JSVAL_VOID
;
2224 argv
[1] = STRING_TO_JSVAL(uri
);
2225 ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, NULL
,
2234 static JSLinearString
*
2235 GeneratePrefix(JSContext
*cx
, JSLinearString
*uri
, JSXMLArray
*decls
)
2237 const jschar
*cp
, *start
, *end
;
2238 size_t length
, newlength
, offset
;
2239 uint32 i
, n
, m
, serial
;
2243 JSLinearString
*nsprefix
, *prefix
;
2245 JS_ASSERT(!uri
->empty());
2248 * If there are no *declared* namespaces, skip all collision detection and
2249 * return a short prefix quickly; an example of such a situation:
2252 * var n = new Namespace("http://example.com/");
2253 * x.@n::att = "val";
2256 * This is necessary for various log10 uses below to be valid.
2258 if (decls
->length
== 0)
2259 return js_NewStringCopyZ(cx
, "a");
2262 * Try peeling off the last filename suffix or pathname component till
2263 * we have a valid XML name. This heuristic will prefer "xul" given
2264 * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2265 * likely URI of the form ".../xbl2/2005".
2267 start
= uri
->chars();
2268 end
= start
+ uri
->length();
2270 while (--cp
> start
) {
2271 if (*cp
== '.' || *cp
== '/' || *cp
== ':') {
2274 if (IsXMLName(cp
, length
) && !STARTS_WITH_XML(cp
, length
))
2282 * If the namespace consisted only of non-XML names or names that begin
2283 * case-insensitively with "xml", arbitrarily create a prefix consisting
2284 * of 'a's of size length (allowing dp-calculating code to work with or
2285 * without this branch executing) plus the space for storing a hyphen and
2286 * the serial number (avoiding reallocation if a collision happens).
2290 if (STARTS_WITH_XML(cp
, length
) || !IsXMLName(cp
, length
)) {
2291 newlength
= length
+ 2 + (size_t) log10((double) decls
->length
);
2293 cx
->malloc((newlength
+ 1) * sizeof(jschar
));
2298 for (i
= 0; i
< newlength
; i
++)
2303 * Now search through decls looking for a collision. If we collide with
2304 * an existing prefix, start tacking on a hyphen and a serial number.
2309 for (i
= 0, n
= decls
->length
; i
< n
; i
++) {
2310 ns
= XMLARRAY_MEMBER(decls
, i
, JSObject
);
2311 if (ns
&& (nsprefix
= ns
->getNamePrefix()) &&
2312 nsprefix
->length() == newlength
&&
2313 !memcmp(nsprefix
->chars(), bp
,
2314 newlength
* sizeof(jschar
))) {
2316 newlength
= length
+ 2 + (size_t) log10((double) n
);
2318 cx
->malloc((newlength
+ 1) * sizeof(jschar
));
2321 js_strncpy(bp
, cp
, length
);
2325 JS_ASSERT(serial
<= n
);
2326 dp
= bp
+ length
+ 2 + (size_t) log10((double) serial
);
2328 for (m
= serial
; m
!= 0; m
/= 10)
2329 *--dp
= (jschar
)('0' + m
% 10);
2331 JS_ASSERT(dp
== bp
+ length
);
2340 offset
= cp
- start
;
2341 prefix
= js_NewDependentString(cx
, uri
, offset
, length
);
2343 prefix
= js_NewString(cx
, bp
, newlength
);
2351 namespace_match(const void *a
, const void *b
)
2353 const JSObject
*nsa
= (const JSObject
*) a
;
2354 const JSObject
*nsb
= (const JSObject
*) b
;
2355 JSLinearString
*prefixa
, *prefixb
= nsb
->getNamePrefix();
2358 prefixa
= nsa
->getNamePrefix();
2359 return prefixa
&& EqualStrings(prefixa
, prefixb
);
2361 return EqualStrings(nsa
->getNameURI(), nsb
->getNameURI());
2364 /* ECMA-357 10.2.1 and 10.2.2 */
2365 #define TO_SOURCE_FLAG 0x80000000
2368 XMLToXMLString(JSContext
*cx
, JSXML
*xml
, const JSXMLArray
*ancestorNSes
,
2371 JSBool pretty
, indentKids
;
2372 StringBuffer
sb(cx
);
2374 JSLinearString
*prefix
, *nsuri
;
2375 uint32 i
, n
, nextIndentLevel
;
2377 AutoNamespaceArray
empty(cx
), decls(cx
), ancdecls(cx
);
2379 if (!GetBooleanXMLSetting(cx
, js_prettyPrinting_str
, &pretty
))
2383 if (!sb
.appendN(' ', indentLevel
& ~TO_SOURCE_FLAG
))
2389 switch (xml
->xml_class
) {
2390 case JSXML_CLASS_TEXT
:
2393 str
= ChompXMLWhitespace(cx
, xml
->xml_value
);
2397 str
= xml
->xml_value
;
2399 return EscapeElementValue(cx
, sb
, str
, indentLevel
& TO_SOURCE_FLAG
);
2401 case JSXML_CLASS_ATTRIBUTE
:
2403 return EscapeAttributeValue(cx
, sb
, xml
->xml_value
,
2404 (indentLevel
& TO_SOURCE_FLAG
) != 0);
2406 case JSXML_CLASS_COMMENT
:
2408 return MakeXMLCommentString(cx
, sb
, xml
->xml_value
);
2410 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
2412 return MakeXMLPIString(cx
, sb
, xml
->name
->getQNameLocalName(),
2415 case JSXML_CLASS_LIST
:
2416 /* ECMA-357 10.2.2. */
2418 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
2420 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
2421 if (pretty
&& i
!= 0) {
2422 if (!sb
.append('\n'))
2426 JSString
*kidstr
= XMLToXMLString(cx
, kid
, ancestorNSes
, indentLevel
);
2427 if (!kidstr
|| !sb
.append(kidstr
))
2434 return cx
->runtime
->emptyString
;
2435 return sb
.finishString();
2440 /* After this point, control must flow through label out: to exit. */
2441 if (!js_EnterLocalRootScope(cx
))
2444 /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2446 ancestorNSes
= &empty
.array
;
2448 /* Clone in-scope namespaces not in ancestorNSes into decls. */
2450 JSXMLArrayCursor
cursor(&xml
->xml_namespaces
);
2451 while ((ns
= (JSObject
*) cursor
.getNext()) != NULL
) {
2452 if (!IsDeclared(ns
))
2454 if (!XMLARRAY_HAS_MEMBER(ancestorNSes
, ns
, namespace_identity
)) {
2455 /* NOTE: may want to exclude unused namespaces here. */
2456 ns2
= NewXMLNamespace(cx
, ns
->getNamePrefix(), ns
->getNameURI(), JS_TRUE
);
2457 if (!ns2
|| !XMLARRAY_APPEND(cx
, &decls
.array
, ns2
))
2464 * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2465 * not own its member references. In the spec, ancdecls has no name, but
2466 * is always written out as (AncestorNamespaces U namespaceDeclarations).
2469 if (!ancdecls
.array
.setCapacity(cx
, ancestorNSes
->length
+ decls
.length()))
2471 for (i
= 0, n
= ancestorNSes
->length
; i
< n
; i
++) {
2472 ns2
= XMLARRAY_MEMBER(ancestorNSes
, i
, JSObject
);
2475 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls
.array
, ns2
, namespace_identity
));
2476 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns2
))
2479 for (i
= 0, n
= decls
.length(); i
< n
; i
++) {
2480 ns2
= XMLARRAY_MEMBER(&decls
.array
, i
, JSObject
);
2483 JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls
.array
, ns2
, namespace_identity
));
2484 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns2
))
2488 /* Step 11, except we don't clone ns unless its prefix is undefined. */
2489 ns
= GetNamespace(cx
, xml
->name
, &ancdecls
.array
);
2493 /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2494 prefix
= ns
->getNamePrefix();
2497 * Create a namespace prefix that isn't used by any member of decls.
2498 * Assign the new prefix to a copy of ns. Flag this namespace as if
2499 * it were declared, for assertion-testing's sake later below.
2501 * Erratum: if prefix and xml->name are both null (*undefined* in
2502 * ECMA-357), we know that xml was named using the default namespace
2503 * (proof: see GetNamespace and the Namespace constructor called with
2504 * two arguments). So we ought not generate a new prefix here, when
2505 * we can declare ns as the default namespace for xml.
2507 * This helps descendants inherit the namespace instead of redundantly
2508 * redeclaring it with generated prefixes in each descendant.
2510 nsuri
= ns
->getNameURI();
2511 if (!xml
->name
->getNamePrefix()) {
2512 prefix
= cx
->runtime
->emptyString
;
2514 prefix
= GeneratePrefix(cx
, nsuri
, &ancdecls
.array
);
2518 ns
= NewXMLNamespace(cx
, prefix
, nsuri
, JS_TRUE
);
2523 * If the xml->name was unprefixed, we must remove any declared default
2524 * namespace from decls before appending ns. How can you get a default
2525 * namespace in decls that doesn't match the one from name? Apparently
2526 * by calling x.setNamespace(ns) where ns has no prefix. The other way
2527 * to fix this is to update x's in-scope namespaces when setNamespace
2528 * is called, but that's not specified by ECMA-357.
2530 * Likely Erratum here, depending on whether the lack of update to x's
2531 * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2532 * erratum or not. Note that changing setNamespace to update the list
2533 * of in-scope namespaces will change x.namespaceDeclarations().
2535 if (prefix
->empty()) {
2536 i
= XMLArrayFindMember(&decls
.array
, ns
, namespace_match
);
2537 if (i
!= XML_NOT_FOUND
)
2538 XMLArrayDelete(cx
, &decls
.array
, i
, JS_TRUE
);
2542 * In the spec, ancdecls has no name, but is always written out as
2543 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2544 * that union in ancdecls, any time we append a namespace strong
2545 * ref to decls, we must also append a weak ref to ancdecls. Order
2546 * matters here: code at label out: releases strong refs in decls.
2548 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns
) ||
2549 !XMLARRAY_APPEND(cx
, &decls
.array
, ns
)) {
2554 /* Format the element or point-tag into sb. */
2555 if (!sb
.append('<'))
2558 if (!prefix
->empty()) {
2559 if (!sb
.append(prefix
) || !sb
.append(':'))
2562 if (!sb
.append(xml
->name
->getQNameLocalName()))
2566 * Step 16 makes a union to avoid writing two loops in step 17, to share
2567 * common attribute value appending spec-code. We prefer two loops for
2568 * faster code and less data overhead.
2571 /* Step 17(b): append attributes. */
2573 JSXMLArrayCursor
cursor(&xml
->xml_attrs
);
2574 while (JSXML
*attr
= (JSXML
*) cursor
.getNext()) {
2575 if (!sb
.append(' '))
2577 ns2
= GetNamespace(cx
, attr
->name
, &ancdecls
.array
);
2581 /* 17(b)(ii): NULL means *undefined* here. */
2582 prefix
= ns2
->getNamePrefix();
2584 prefix
= GeneratePrefix(cx
, ns2
->getNameURI(), &ancdecls
.array
);
2588 /* Again, we avoid copying ns2 until we know it's prefix-less. */
2589 ns2
= NewXMLNamespace(cx
, prefix
, ns2
->getNameURI(), JS_TRUE
);
2594 * In the spec, ancdecls has no name, but is always written out as
2595 * (AncestorNamespaces U namespaceDeclarations). Since we compute
2596 * that union in ancdecls, any time we append a namespace strong
2597 * ref to decls, we must also append a weak ref to ancdecls. Order
2598 * matters here: code at label out: releases strong refs in decls.
2600 if (!XMLARRAY_APPEND(cx
, &ancdecls
.array
, ns2
) ||
2601 !XMLARRAY_APPEND(cx
, &decls
.array
, ns2
)) {
2607 if (!prefix
->empty()) {
2608 if (!sb
.append(prefix
) || !sb
.append(':'))
2613 if (!sb
.append(attr
->name
->getQNameLocalName()))
2617 if (!AppendAttributeValue(cx
, sb
, attr
->xml_value
))
2622 /* Step 17(c): append XML namespace declarations. */
2624 JSXMLArrayCursor
cursor(&decls
.array
);
2625 while (JSObject
*ns3
= (JSObject
*) cursor
.getNext()) {
2626 JS_ASSERT(IsDeclared(ns3
));
2628 if (!sb
.append(" xmlns"))
2631 /* 17(c)(ii): NULL means *undefined* here. */
2632 prefix
= ns3
->getNamePrefix();
2634 prefix
= GeneratePrefix(cx
, ns3
->getNameURI(), &ancdecls
.array
);
2637 ns3
->setNamePrefix(prefix
);
2641 if (!prefix
->empty()) {
2642 if (!sb
.append(':') || !sb
.append(prefix
))
2647 if (!AppendAttributeValue(cx
, sb
, ns3
->getNameURI()))
2652 /* Step 18: handle point tags. */
2653 n
= xml
->xml_kids
.length
;
2655 if (!sb
.append("/>"))
2658 /* Steps 19 through 25: handle element content, and open the end-tag. */
2659 if (!sb
.append('>'))
2663 indentKids
= n
> 1 ||
2665 (kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
)) &&
2666 kid
->xml_class
!= JSXML_CLASS_TEXT
);
2669 if (pretty
&& indentKids
) {
2670 if (!GetUint32XMLSetting(cx
, js_prettyIndent_str
, &i
))
2672 nextIndentLevel
= indentLevel
+ i
;
2674 nextIndentLevel
= indentLevel
& TO_SOURCE_FLAG
;
2678 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
2679 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
2680 if (pretty
&& indentKids
) {
2681 if (!sb
.append('\n'))
2685 JSString
*kidstr
= XMLToXMLString(cx
, kid
, &ancdecls
.array
, nextIndentLevel
);
2689 if (!sb
.append(kidstr
))
2694 if (pretty
&& indentKids
) {
2695 if (!sb
.append('\n') ||
2696 !sb
.appendN(' ', indentLevel
& ~TO_SOURCE_FLAG
))
2699 if (!sb
.append("</"))
2703 prefix
= ns
->getNamePrefix();
2704 if (prefix
&& !prefix
->empty()) {
2705 if (!sb
.append(prefix
) || !sb
.append(':'))
2710 if (!sb
.append(xml
->name
->getQNameLocalName()) || !sb
.append('>'))
2714 str
= sb
.finishString();
2716 js_LeaveLocalRootScopeWithResult(cx
, str
);
2722 ToXMLString(JSContext
*cx
, jsval v
, uint32 toSourceFlag
)
2728 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
2729 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2730 JSMSG_BAD_XML_CONVERSION
,
2731 JSVAL_IS_NULL(v
) ? js_null_str
: js_undefined_str
);
2735 if (JSVAL_IS_BOOLEAN(v
) || JSVAL_IS_NUMBER(v
))
2736 return js_ValueToString(cx
, Valueify(v
));
2738 if (JSVAL_IS_STRING(v
)) {
2739 StringBuffer
sb(cx
);
2740 return EscapeElementValue(cx
, sb
, JSVAL_TO_STRING(v
), toSourceFlag
);
2743 obj
= JSVAL_TO_OBJECT(v
);
2744 if (!obj
->isXML()) {
2745 if (!DefaultValue(cx
, obj
, JSTYPE_STRING
, Valueify(&v
)))
2747 str
= js_ValueToString(cx
, Valueify(v
));
2750 StringBuffer
sb(cx
);
2751 return EscapeElementValue(cx
, sb
, str
, toSourceFlag
);
2754 /* Handle non-element cases in this switch, returning from each case. */
2755 xml
= (JSXML
*) obj
->getPrivate();
2756 return XMLToXMLString(cx
, xml
, NULL
, toSourceFlag
| 0);
2760 ToAttributeName(JSContext
*cx
, jsval v
)
2762 JSLinearString
*name
, *uri
, *prefix
;
2767 if (JSVAL_IS_STRING(v
)) {
2768 name
= JSVAL_TO_STRING(v
)->ensureLinear(cx
);
2771 uri
= prefix
= cx
->runtime
->emptyString
;
2773 if (JSVAL_IS_PRIMITIVE(v
)) {
2774 js_ReportValueError(cx
, JSMSG_BAD_XML_ATTR_NAME
,
2775 JSDVG_IGNORE_STACK
, Valueify(v
), NULL
);
2779 obj
= JSVAL_TO_OBJECT(v
);
2780 clasp
= obj
->getClass();
2781 if (clasp
== &js_AttributeNameClass
)
2784 if (clasp
== &js_QNameClass
) {
2786 uri
= qn
->getNameURI();
2787 prefix
= qn
->getNamePrefix();
2788 name
= qn
->getQNameLocalName();
2790 if (clasp
== &js_AnyNameClass
) {
2791 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
2793 JSString
*str
= js_ValueToString(cx
, Valueify(v
));
2796 name
= str
->ensureLinear(cx
);
2800 uri
= prefix
= cx
->runtime
->emptyString
;
2804 qn
= NewXMLAttributeName(cx
, uri
, prefix
, name
);
2811 ReportBadXMLName(JSContext
*cx
, const Value
&idval
)
2813 js_ReportValueError(cx
, JSMSG_BAD_XML_NAME
, JSDVG_IGNORE_STACK
, idval
, NULL
);
2817 IsFunctionQName(JSContext
*cx
, JSObject
*qn
, jsid
*funidp
)
2820 JSLinearString
*uri
;
2822 atom
= cx
->runtime
->atomState
.functionNamespaceURIAtom
;
2823 uri
= qn
->getNameURI();
2825 (uri
== ATOM_TO_STRING(atom
) ||
2826 EqualStrings(uri
, ATOM_TO_STRING(atom
)))) {
2827 return JS_ValueToId(cx
, STRING_TO_JSVAL(qn
->getQNameLocalName()), funidp
);
2829 *funidp
= JSID_VOID
;
2834 js_IsFunctionQName(JSContext
*cx
, JSObject
*obj
, jsid
*funidp
)
2836 if (obj
->getClass() == &js_QNameClass
)
2837 return IsFunctionQName(cx
, obj
, funidp
);
2838 *funidp
= JSID_VOID
;
2843 ToXMLName(JSContext
*cx
, jsval v
, jsid
*funidp
)
2845 JSAtom
*atomizedName
;
2851 if (JSVAL_IS_STRING(v
)) {
2852 name
= JSVAL_TO_STRING(v
);
2854 if (JSVAL_IS_PRIMITIVE(v
)) {
2855 ReportBadXMLName(cx
, Valueify(v
));
2859 obj
= JSVAL_TO_OBJECT(v
);
2860 clasp
= obj
->getClass();
2861 if (clasp
== &js_AttributeNameClass
|| clasp
== &js_QNameClass
)
2863 if (clasp
== &js_AnyNameClass
) {
2864 name
= ATOM_TO_STRING(cx
->runtime
->atomState
.starAtom
);
2867 name
= js_ValueToString(cx
, Valueify(v
));
2872 atomizedName
= js_AtomizeString(cx
, name
, 0);
2877 * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
2879 * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
2881 * First, _P_ should be _s_, to refer to the given string.
2883 * Second, why does ToXMLName applied to the string type throw TypeError
2884 * only for numeric literals without any leading or trailing whitespace?
2886 * If the idea is to reject uint32 property names, then the check needs to
2887 * be stricter, to exclude hexadecimal and floating point literals.
2889 if (js_IdIsIndex(ATOM_TO_JSID(atomizedName
), &index
))
2892 if (*atomizedName
->chars() == '@') {
2893 name
= js_NewDependentString(cx
, name
, 1, name
->length() - 1);
2896 *funidp
= JSID_VOID
;
2897 return ToAttributeName(cx
, STRING_TO_JSVAL(name
));
2901 v
= STRING_TO_JSVAL(name
);
2902 obj
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 1, Valueify(&v
));
2907 if (!IsFunctionQName(cx
, obj
, funidp
))
2912 JSAutoByteString bytes
;
2913 if (js_ValueToPrintable(cx
, StringValue(name
), &bytes
))
2914 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_XML_NAME
, bytes
.ptr());
2918 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
2920 AddInScopeNamespace(JSContext
*cx
, JSXML
*xml
, JSObject
*ns
)
2922 JSLinearString
*prefix
, *prefix2
;
2923 JSObject
*match
, *ns2
;
2926 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
2929 /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
2930 prefix
= ns
->getNamePrefix();
2933 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
2934 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
2935 if (ns2
&& EqualStrings(ns2
->getNameURI(), ns
->getNameURI())) {
2940 if (!match
&& !XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_namespaces
, n
, ns
))
2943 if (prefix
->empty() && xml
->name
->getNameURI()->empty())
2946 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2949 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
2950 ns2
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
2951 if (ns2
&& (prefix2
= ns2
->getNamePrefix()) &&
2952 EqualStrings(prefix2
, prefix
)) {
2958 if (match
&& !EqualStrings(match
->getNameURI(), ns
->getNameURI())) {
2959 ns2
= XMLARRAY_DELETE(cx
, &xml
->xml_namespaces
, m
, JS_TRUE
,
2961 JS_ASSERT(ns2
== match
);
2962 match
->clearNamePrefix();
2963 if (!AddInScopeNamespace(cx
, xml
, match
))
2966 if (!XMLARRAY_APPEND(cx
, &xml
->xml_namespaces
, ns
))
2970 /* OPTION: enforce that descendants have superset namespaces. */
2974 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
2976 Append(JSContext
*cx
, JSXML
*list
, JSXML
*xml
)
2981 JS_ASSERT(list
->xml_class
== JSXML_CLASS_LIST
);
2982 i
= list
->xml_kids
.length
;
2984 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
2985 list
->xml_target
= xml
->xml_target
;
2986 list
->xml_targetprop
= xml
->xml_targetprop
;
2987 n
= JSXML_LENGTH(xml
);
2989 if (!list
->xml_kids
.setCapacity(cx
, k
))
2991 for (j
= 0; j
< n
; j
++) {
2992 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, j
, JSXML
);
2994 XMLARRAY_SET_MEMBER(&list
->xml_kids
, i
+ j
, kid
);
2999 list
->xml_target
= xml
->parent
;
3000 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
3001 list
->xml_targetprop
= NULL
;
3003 list
->xml_targetprop
= xml
->name
;
3004 if (!XMLARRAY_ADD_MEMBER(cx
, &list
->xml_kids
, i
, xml
))
3009 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3011 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
);
3014 DeepCopy(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
, uintN flags
)
3018 /* Our caller may not be protecting newborns with a local root scope. */
3019 if (!js_EnterLocalRootScope(cx
))
3021 copy
= DeepCopyInLRS(cx
, xml
, flags
);
3024 /* Caller provided the object for this copy, hook 'em up. */
3025 obj
->setPrivate(copy
);
3027 } else if (!js_GetXMLObject(cx
, copy
)) {
3031 js_LeaveLocalRootScopeWithResult(cx
, copy
);
3036 * (i) We must be in a local root scope (InLRS).
3037 * (ii) parent must have a rooted object.
3038 * (iii) from's owning object must be locked if not thread-local.
3041 DeepCopySetInLRS(JSContext
*cx
, JSXMLArray
*from
, JSXMLArray
*to
, JSXML
*parent
,
3049 if (!to
->setCapacity(cx
, n
))
3052 JSXMLArrayCursor
cursor(from
);
3054 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
3055 if ((flags
& XSF_IGNORE_COMMENTS
) &&
3056 kid
->xml_class
== JSXML_CLASS_COMMENT
) {
3059 if ((flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
) &&
3060 kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
3063 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3064 (kid
->xml_flags
& XMLF_WHITESPACE_TEXT
)) {
3067 kid2
= DeepCopyInLRS(cx
, kid
, flags
);
3073 if ((flags
& XSF_IGNORE_WHITESPACE
) &&
3074 n
> 1 && kid2
->xml_class
== JSXML_CLASS_TEXT
) {
3075 str
= ChompXMLWhitespace(cx
, kid2
->xml_value
);
3080 kid2
->xml_value
= str
;
3083 XMLARRAY_SET_MEMBER(to
, j
, kid2
);
3085 if (parent
->xml_class
!= JSXML_CLASS_LIST
)
3086 kid2
->parent
= parent
;
3095 DeepCopyInLRS(JSContext
*cx
, JSXML
*xml
, uintN flags
)
3103 JS_CHECK_RECURSION(cx
, return NULL
);
3105 copy
= js_NewXML(cx
, JSXMLClass(xml
->xml_class
));
3110 qn
= NewXMLQName(cx
, qn
->getNameURI(), qn
->getNamePrefix(), qn
->getQNameLocalName());
3117 copy
->xml_flags
= xml
->xml_flags
;
3119 if (JSXML_HAS_VALUE(xml
)) {
3120 copy
->xml_value
= xml
->xml_value
;
3123 ok
= DeepCopySetInLRS(cx
, &xml
->xml_kids
, ©
->xml_kids
, copy
, flags
);
3127 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3128 copy
->xml_target
= xml
->xml_target
;
3129 copy
->xml_targetprop
= xml
->xml_targetprop
;
3131 n
= xml
->xml_namespaces
.length
;
3132 ok
= copy
->xml_namespaces
.setCapacity(cx
, n
);
3135 for (i
= 0; i
< n
; i
++) {
3136 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3139 ns2
= NewXMLNamespace(cx
, ns
->getNamePrefix(), ns
->getNameURI(),
3142 copy
->xml_namespaces
.length
= i
;
3146 XMLARRAY_SET_MEMBER(©
->xml_namespaces
, i
, ns2
);
3149 ok
= DeepCopySetInLRS(cx
, &xml
->xml_attrs
, ©
->xml_attrs
, copy
,
3162 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3164 DeleteByIndex(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3168 if (JSXML_HAS_KIDS(xml
) && index
< xml
->xml_kids
.length
) {
3169 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3172 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3176 typedef JSBool (*JSXMLNameMatcher
)(JSObject
*nameqn
, JSXML
*xml
);
3179 MatchAttrName(JSObject
*nameqn
, JSXML
*attr
)
3181 JSObject
*attrqn
= attr
->name
;
3182 JSLinearString
*localName
= nameqn
->getQNameLocalName();
3183 JSLinearString
*uri
;
3185 return (IS_STAR(localName
) ||
3186 EqualStrings(attrqn
->getQNameLocalName(), localName
)) &&
3187 (!(uri
= nameqn
->getNameURI()) ||
3188 EqualStrings(attrqn
->getNameURI(), uri
));
3192 MatchElemName(JSObject
*nameqn
, JSXML
*elem
)
3194 JSLinearString
*localName
= nameqn
->getQNameLocalName();
3195 JSLinearString
*uri
;
3197 return (IS_STAR(localName
) ||
3198 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3199 EqualStrings(elem
->name
->getQNameLocalName(), localName
))) &&
3200 (!(uri
= nameqn
->getNameURI()) ||
3201 (elem
->xml_class
== JSXML_CLASS_ELEMENT
&&
3202 EqualStrings(elem
->name
->getNameURI(), uri
)));
3205 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3207 DescendantsHelper(JSContext
*cx
, JSXML
*xml
, JSObject
*nameqn
, JSXML
*list
)
3212 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
3214 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
&&
3215 nameqn
->getClass() == &js_AttributeNameClass
) {
3216 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
3217 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3218 if (attr
&& MatchAttrName(nameqn
, attr
)) {
3219 if (!Append(cx
, list
, attr
))
3225 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
3226 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3229 if (nameqn
->getClass() != &js_AttributeNameClass
&&
3230 MatchElemName(nameqn
, kid
)) {
3231 if (!Append(cx
, list
, kid
))
3234 if (!DescendantsHelper(cx
, kid
, nameqn
, list
))
3241 Descendants(JSContext
*cx
, JSXML
*xml
, jsval id
)
3250 nameqn
= ToXMLName(cx
, id
, &funid
);
3254 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
3257 list
= (JSXML
*) listobj
->getPrivate();
3258 if (!JSID_IS_VOID(funid
))
3262 * Protect nameqn's object and strings from GC by linking list to it
3263 * temporarily. The newborn GC root for the last allocated object
3264 * protects listobj, which protects list. Any other object allocations
3265 * occurring beneath DescendantsHelper use local roots.
3267 list
->name
= nameqn
;
3268 if (!js_EnterLocalRootScope(cx
))
3270 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3272 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
3273 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3274 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
3275 ok
= DescendantsHelper(cx
, kid
, nameqn
, list
);
3281 ok
= DescendantsHelper(cx
, xml
, nameqn
, list
);
3283 js_LeaveLocalRootScopeWithResult(cx
, list
);
3290 /* Recursive (JSXML *) parameterized version of Equals. */
3292 XMLEquals(JSContext
*cx
, JSXML
*xml
, JSXML
*vxml
, JSBool
*bp
)
3296 JSXML
*kid
, *vkid
, *attr
, *vattr
;
3297 JSObject
*xobj
, *vobj
;
3300 if (xml
->xml_class
!= vxml
->xml_class
) {
3301 if (xml
->xml_class
== JSXML_CLASS_LIST
&& xml
->xml_kids
.length
== 1) {
3302 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3306 if (vxml
->xml_class
== JSXML_CLASS_LIST
&& vxml
->xml_kids
.length
== 1) {
3307 vxml
= XMLARRAY_MEMBER(&vxml
->xml_kids
, 0, JSXML
);
3319 EqualStrings(qn
->getQNameLocalName(), vqn
->getQNameLocalName()) &&
3320 EqualStrings(qn
->getNameURI(), vqn
->getNameURI());
3327 if (JSXML_HAS_VALUE(xml
)) {
3328 if (!EqualStrings(cx
, xml
->xml_value
, vxml
->xml_value
, bp
))
3330 } else if (xml
->xml_kids
.length
!= vxml
->xml_kids
.length
) {
3334 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
3335 JSXMLArrayCursor
vcursor(&vxml
->xml_kids
);
3337 kid
= (JSXML
*) cursor
.getNext();
3338 vkid
= (JSXML
*) vcursor
.getNext();
3339 if (!kid
|| !vkid
) {
3340 *bp
= !kid
&& !vkid
;
3343 xobj
= js_GetXMLObject(cx
, kid
);
3344 vobj
= js_GetXMLObject(cx
, vkid
);
3345 if (!xobj
|| !vobj
||
3346 !js_TestXMLEquality(cx
, ObjectValue(*xobj
), ObjectValue(*vobj
), bp
))
3353 if (*bp
&& xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3354 n
= xml
->xml_attrs
.length
;
3355 if (n
!= vxml
->xml_attrs
.length
)
3357 for (i
= 0; *bp
&& i
< n
; i
++) {
3358 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
3361 j
= XMLARRAY_FIND_MEMBER(&vxml
->xml_attrs
, attr
, attr_identity
);
3362 if (j
== XML_NOT_FOUND
) {
3366 vattr
= XMLARRAY_MEMBER(&vxml
->xml_attrs
, j
, JSXML
);
3369 if (!EqualStrings(cx
, attr
->xml_value
, vattr
->xml_value
, bp
))
3378 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3380 Equals(JSContext
*cx
, JSXML
*xml
, jsval v
, JSBool
*bp
)
3385 if (JSVAL_IS_PRIMITIVE(v
)) {
3387 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3388 if (xml
->xml_kids
.length
== 1) {
3389 vxml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
3392 vobj
= js_GetXMLObject(cx
, vxml
);
3395 return js_TestXMLEquality(cx
, ObjectValue(*vobj
), Valueify(v
), bp
);
3397 if (JSVAL_IS_VOID(v
) && xml
->xml_kids
.length
== 0)
3401 vobj
= JSVAL_TO_OBJECT(v
);
3402 if (!vobj
->isXML()) {
3405 vxml
= (JSXML
*) vobj
->getPrivate();
3406 if (!XMLEquals(cx
, xml
, vxml
, bp
))
3414 CheckCycle(JSContext
*cx
, JSXML
*xml
, JSXML
*kid
)
3416 JS_ASSERT(kid
->xml_class
!= JSXML_CLASS_LIST
);
3420 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3421 JSMSG_CYCLIC_VALUE
, js_XML_str
);
3424 } while ((xml
= xml
->parent
) != NULL
);
3429 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3431 Insert(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3438 if (!JSXML_HAS_KIDS(xml
))
3443 if (!JSVAL_IS_PRIMITIVE(v
)) {
3444 vobj
= JSVAL_TO_OBJECT(v
);
3445 if (vobj
->isXML()) {
3446 vxml
= (JSXML
*) vobj
->getPrivate();
3447 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3448 n
= vxml
->xml_kids
.length
;
3451 for (j
= 0; j
< n
; j
++) {
3452 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3455 if (!CheckCycle(cx
, xml
, kid
))
3458 } else if (vxml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3459 /* OPTION: enforce that descendants have superset namespaces. */
3460 if (!CheckCycle(cx
, xml
, vxml
))
3466 str
= js_ValueToString(cx
, Valueify(v
));
3470 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3473 vxml
->xml_value
= str
;
3476 if (i
> xml
->xml_kids
.length
)
3477 i
= xml
->xml_kids
.length
;
3479 if (!XMLArrayInsert(cx
, &xml
->xml_kids
, i
, n
))
3482 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
3483 for (j
= 0; j
< n
; j
++) {
3484 kid
= XMLARRAY_MEMBER(&vxml
->xml_kids
, j
, JSXML
);
3488 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
+ j
, kid
);
3490 /* OPTION: enforce that descendants have superset namespaces. */
3494 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
3500 IndexToId(JSContext
*cx
, uint32 index
, jsid
*idp
)
3505 if (index
<= JSID_INT_MAX
) {
3506 *idp
= INT_TO_JSID(index
);
3508 str
= js_NumberToString(cx
, (jsdouble
) index
);
3511 atom
= js_AtomizeString(cx
, str
, 0);
3514 *idp
= ATOM_TO_JSID(atom
);
3519 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3521 Replace(JSContext
*cx
, JSXML
*xml
, uint32 i
, jsval v
)
3528 if (!JSXML_HAS_KIDS(xml
))
3533 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3534 * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3536 n
= xml
->xml_kids
.length
;
3541 if (!JSVAL_IS_PRIMITIVE(v
)) {
3542 vobj
= JSVAL_TO_OBJECT(v
);
3544 vxml
= (JSXML
*) vobj
->getPrivate();
3547 switch (vxml
? JSXMLClass(vxml
->xml_class
) : JSXML_CLASS_LIMIT
) {
3548 case JSXML_CLASS_ELEMENT
:
3549 /* OPTION: enforce that descendants have superset namespaces. */
3550 if (!CheckCycle(cx
, xml
, vxml
))
3552 case JSXML_CLASS_COMMENT
:
3553 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
3554 case JSXML_CLASS_TEXT
:
3557 case JSXML_CLASS_LIST
:
3559 DeleteByIndex(cx
, xml
, i
);
3560 if (!Insert(cx
, xml
, i
, v
))
3565 str
= js_ValueToString(cx
, Valueify(v
));
3569 vxml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3572 vxml
->xml_value
= str
;
3577 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
3581 if (!XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_kids
, i
, vxml
))
3589 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3591 DeleteNamedProperty(JSContext
*cx
, JSXML
*xml
, JSObject
*nameqn
,
3595 uint32 index
, deleteCount
;
3597 JSXMLNameMatcher matcher
;
3599 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3600 array
= &xml
->xml_kids
;
3601 for (index
= 0; index
< array
->length
; index
++) {
3602 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3603 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
)
3604 DeleteNamedProperty(cx
, kid
, nameqn
, attributes
);
3606 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3608 array
= &xml
->xml_attrs
;
3609 matcher
= MatchAttrName
;
3611 array
= &xml
->xml_kids
;
3612 matcher
= MatchElemName
;
3615 for (index
= 0; index
< array
->length
; index
++) {
3616 kid
= XMLARRAY_MEMBER(array
, index
, JSXML
);
3617 if (kid
&& matcher(nameqn
, kid
)) {
3619 XMLArrayDelete(cx
, array
, index
, JS_FALSE
);
3621 } else if (deleteCount
!= 0) {
3622 XMLARRAY_SET_MEMBER(array
,
3623 index
- deleteCount
,
3624 array
->vector
[index
]);
3627 array
->length
-= deleteCount
;
3631 /* ECMA-357 9.2.1.3 index case. */
3633 DeleteListElement(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3635 JSXML
*kid
, *parent
;
3638 JS_ASSERT(xml
->xml_class
== JSXML_CLASS_LIST
);
3640 if (index
< xml
->xml_kids
.length
) {
3641 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3643 parent
= kid
->parent
;
3645 JS_ASSERT(parent
!= xml
);
3646 JS_ASSERT(JSXML_HAS_KIDS(parent
));
3648 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
3649 DeleteNamedProperty(cx
, parent
, kid
->name
, JS_TRUE
);
3651 kidIndex
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
,
3653 JS_ASSERT(kidIndex
!= XML_NOT_FOUND
);
3654 DeleteByIndex(cx
, parent
, kidIndex
);
3657 XMLArrayDelete(cx
, &xml
->xml_kids
, index
, JS_TRUE
);
3663 SyncInScopeNamespaces(JSContext
*cx
, JSXML
*xml
)
3665 JSXMLArray
*nsarray
;
3669 nsarray
= &xml
->xml_namespaces
;
3670 while ((xml
= xml
->parent
) != NULL
) {
3671 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
3672 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
3673 if (ns
&& !XMLARRAY_HAS_MEMBER(nsarray
, ns
, namespace_identity
)) {
3674 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
3683 GetNamedProperty(JSContext
*cx
, JSXML
*xml
, JSObject
* nameqn
, JSXML
*list
)
3686 JSXMLNameMatcher matcher
;
3689 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
3690 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
3691 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
3692 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
3693 !GetNamedProperty(cx
, kid
, nameqn
, list
)) {
3697 } else if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
3698 attrs
= (nameqn
->getClass() == &js_AttributeNameClass
);
3700 array
= &xml
->xml_attrs
;
3701 matcher
= MatchAttrName
;
3703 array
= &xml
->xml_kids
;
3704 matcher
= MatchElemName
;
3707 JSXMLArrayCursor
cursor(array
);
3708 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
3709 if (matcher(nameqn
, kid
)) {
3711 kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
3712 !SyncInScopeNamespaces(cx
, kid
)) {
3715 if (!Append(cx
, list
, kid
))
3724 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3726 GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
3728 JSXML
*xml
, *list
, *kid
;
3730 JSObject
*kidobj
, *listobj
;
3734 xml
= (JSXML
*) GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
3738 if (js_IdIsIndex(id
, &index
)) {
3739 if (!JSXML_HAS_KIDS(xml
)) {
3740 *vp
= (index
== 0) ? OBJECT_TO_JSVAL(obj
) : JSVAL_VOID
;
3743 * ECMA-357 9.2.1.1 starts here.
3745 * Erratum: 9.2 is not completely clear that indexed properties
3746 * correspond to kids, but that's what it seems to say, and it's
3747 * what any sane user would want.
3749 if (index
< xml
->xml_kids
.length
) {
3750 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3755 kidobj
= js_GetXMLObject(cx
, kid
);
3759 *vp
= OBJECT_TO_JSVAL(kidobj
);
3768 * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3770 nameqn
= ToXMLName(cx
, IdToJsval(id
), &funid
);
3773 if (!JSID_IS_VOID(funid
))
3774 return GetXMLFunction(cx
, obj
, funid
, vp
);
3776 jsval roots
[2] = { OBJECT_TO_JSVAL(nameqn
), JSVAL_NULL
};
3777 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), Valueify(roots
));
3779 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
3783 roots
[1] = OBJECT_TO_JSVAL(listobj
);
3785 list
= (JSXML
*) listobj
->getPrivate();
3786 if (!GetNamedProperty(cx
, xml
, nameqn
, list
))
3790 * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
3791 * given list's [[TargetProperty]] to the property that is being
3792 * appended. This means that any use of the internal [[Get]]
3793 * property returns a list which, when used by e.g. [[Insert]]
3794 * duplicates the last element matched by id. See bug 336921.
3796 list
->xml_target
= xml
;
3797 list
->xml_targetprop
= nameqn
;
3798 *vp
= OBJECT_TO_JSVAL(listobj
);
3803 CopyOnWrite(JSContext
*cx
, JSXML
*xml
, JSObject
*obj
)
3805 JS_ASSERT(xml
->object
!= obj
);
3807 xml
= DeepCopy(cx
, xml
, obj
, 0);
3811 JS_ASSERT(xml
->object
== obj
);
3815 #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
3816 (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
3819 KidToString(JSContext
*cx
, JSXML
*xml
, uint32 index
)
3824 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
3826 return cx
->runtime
->emptyString
;
3827 kidobj
= js_GetXMLObject(cx
, kid
);
3830 return js_ValueToString(cx
, ObjectValue(*kidobj
));
3833 /* Forward declared -- its implementation uses other statics that call it. */
3835 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
);
3837 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
3839 PutProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
3841 JSBool ok
, primitiveAssign
;
3842 enum { OBJ_ROOT
, ID_ROOT
, VAL_ROOT
};
3843 JSXML
*xml
, *vxml
, *rxml
, *kid
, *attr
, *parent
, *copy
, *kid2
, *match
;
3844 JSObject
*vobj
, *nameobj
, *attrobj
, *parentobj
, *kidobj
, *copyobj
;
3845 JSObject
*targetprop
, *nameqn
, *attrqn
;
3846 uint32 index
, i
, j
, k
, n
, q
, matchIndex
;
3847 jsval attrval
, nsval
;
3851 xml
= (JSXML
*) GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
3855 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
3859 /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
3861 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
3862 vobj
= JSVAL_TO_OBJECT(*vp
);
3864 vxml
= (JSXML
*) vobj
->getPrivate();
3867 ok
= js_EnterLocalRootScope(cx
);
3871 MUST_FLOW_THROUGH("out");
3873 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
3874 roots
[ID_ROOT
] = IdToJsval(id
);
3875 roots
[VAL_ROOT
] = *vp
;
3876 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), Valueify(roots
));
3878 if (js_IdIsIndex(id
, &index
)) {
3879 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
3880 /* See NOTE in spec: this variation is reserved for future use. */
3881 ReportBadXMLName(cx
, IdToValue(id
));
3886 * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
3891 if (xml
->xml_target
) {
3892 ok
= ResolveValue(cx
, xml
->xml_target
, &rxml
);
3897 JS_ASSERT(rxml
->object
);
3903 if (index
>= xml
->xml_kids
.length
) {
3906 if (rxml
->xml_class
== JSXML_CLASS_LIST
) {
3907 if (rxml
->xml_kids
.length
!= 1)
3909 rxml
= XMLARRAY_MEMBER(&rxml
->xml_kids
, 0, JSXML
);
3912 ok
= js_GetXMLObject(cx
, rxml
) != NULL
;
3918 * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
3919 * _y.[[Parent]] = r_ where _r_ is the result of
3920 * [[ResolveValue]] called on _x.[[TargetObject]] in
3921 * 2(a)(i). This can result in text parenting text:
3923 * var MYXML = new XML();
3924 * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
3926 * (testcase from Werner Sharp <wsharp@macromedia.com>).
3928 * To match insertChildAfter, insertChildBefore,
3929 * prependChild, and setChildren, we should silently
3930 * do nothing in this case.
3932 if (!JSXML_HAS_KIDS(rxml
))
3936 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
3937 targetprop
= xml
->xml_targetprop
;
3938 if (!targetprop
|| IS_STAR(targetprop
->getQNameLocalName())) {
3939 /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
3940 kid
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
3944 nameobj
= targetprop
;
3945 if (nameobj
->getClass() == &js_AttributeNameClass
) {
3948 * Note that rxml can't be null here, because target
3949 * and targetprop are non-null.
3951 ok
= GetProperty(cx
, rxml
->object
, id
, &attrval
);
3954 if (JSVAL_IS_PRIMITIVE(attrval
)) /* no such attribute */
3956 attrobj
= JSVAL_TO_OBJECT(attrval
);
3957 attr
= (JSXML
*) attrobj
->getPrivate();
3958 if (JSXML_LENGTH(attr
) != 0)
3961 kid
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
3964 kid
= js_NewXML(cx
, JSXML_CLASS_ELEMENT
);
3969 /* An important bit of 2(c)(ii). */
3970 kid
->name
= targetprop
;
3973 /* Final important bit of 2(c)(ii). */
3977 i
= xml
->xml_kids
.length
;
3978 if (kid
->xml_class
!= JSXML_CLASS_ATTRIBUTE
) {
3980 * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
3981 * y.[[Parent]] is here called kid->parent, which we know
3982 * from 2(c)(ii) is _r_, here called rxml. So let's just
3983 * test that! Erratum, the spec should be simpler here.
3986 JS_ASSERT(JSXML_HAS_KIDS(rxml
));
3987 n
= rxml
->xml_kids
.length
;
3989 if (n
!= 0 && i
!= 0) {
3990 for (n
= j
, j
= 0; j
< n
; j
++) {
3991 if (rxml
->xml_kids
.vector
[j
] ==
3992 xml
->xml_kids
.vector
[i
-1]) {
3998 kidobj
= js_GetXMLObject(cx
, kid
);
4001 ok
= Insert(cx
, rxml
, j
+ 1, OBJECT_TO_JSVAL(kidobj
));
4008 * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4009 * typo for [[TargetProperty]].
4012 kid
->name
= (vxml
->xml_class
== JSXML_CLASS_LIST
)
4013 ? vxml
->xml_targetprop
4019 ok
= Append(cx
, xml
, kid
);
4026 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4027 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4028 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4031 roots
[VAL_ROOT
] = *vp
;
4035 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
4038 parent
= kid
->parent
;
4039 if (kid
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4040 nameobj
= kid
->name
;
4041 if (nameobj
->getClass() != &js_AttributeNameClass
) {
4042 nameobj
= NewXMLAttributeName(cx
, nameobj
->getNameURI(), nameobj
->getNamePrefix(),
4043 nameobj
->getQNameLocalName());
4047 id
= OBJECT_TO_JSID(nameobj
);
4051 parentobj
= js_GetXMLObject(cx
, parent
);
4054 ok
= PutProperty(cx
, parentobj
, id
, vp
);
4059 ok
= GetProperty(cx
, parentobj
, id
, vp
);
4062 attr
= (JSXML
*) JSVAL_TO_OBJECT(*vp
)->getPrivate();
4064 /* 2(e)(iii) - the length check comes from the bug 375406. */
4065 if (attr
->xml_kids
.length
!= 0)
4066 xml
->xml_kids
.vector
[i
] = attr
->xml_kids
.vector
[0];
4071 else if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4075 * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4076 * if we do that we never change the parent of each child in the
4077 * list. Since [[Put]] when called on an XML object deeply copies
4078 * the provided list _V_, we also do so here. Perhaps the shallow
4079 * copy was a misguided optimization?
4081 copy
= DeepCopyInLRS(cx
, vxml
, 0);
4084 copyobj
= js_GetXMLObject(cx
, copy
);
4088 JS_ASSERT(parent
!= xml
);
4090 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4091 JS_ASSERT(q
!= XML_NOT_FOUND
);
4092 ok
= Replace(cx
, parent
, q
, OBJECT_TO_JSVAL(copyobj
));
4097 /* Erratum: this loop in the spec is useless. */
4098 for (j
= 0, n
= copy
->xml_kids
.length
; j
< n
; j
++) {
4099 kid2
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
+ j
, JSXML
);
4100 JS_ASSERT(XMLARRAY_MEMBER(©
->xml_kids
, j
, JSXML
)
4108 * Erratum: notice the unhandled zero-length V basis case and
4109 * the off-by-one errors for the n != 0 cases in the spec.
4111 n
= copy
->xml_kids
.length
;
4113 XMLArrayDelete(cx
, &xml
->xml_kids
, i
, JS_TRUE
);
4115 ok
= XMLArrayInsert(cx
, &xml
->xml_kids
, i
+ 1, n
- 1);
4119 for (j
= 0; j
< n
; j
++)
4120 xml
->xml_kids
.vector
[i
+ j
] = copy
->xml_kids
.vector
[j
];
4125 else if (vxml
|| JSXML_HAS_VALUE(kid
)) {
4127 q
= XMLARRAY_FIND_MEMBER(&parent
->xml_kids
, kid
, NULL
);
4128 JS_ASSERT(q
!= XML_NOT_FOUND
);
4129 ok
= Replace(cx
, parent
, q
, *vp
);
4133 vxml
= XMLARRAY_MEMBER(&parent
->xml_kids
, q
, JSXML
);
4136 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4141 * Erratum: _V_ may not be of type XML, but all index-named
4142 * properties _x[i]_ in an XMLList _x_ must be of type XML,
4143 * according to 9.2.1.1 Overview and other places in the spec.
4145 * Thanks to 2(d), we know _V_ (*vp here) is either a string
4146 * or an XML/XMLList object. If *vp is a string, call ToXML
4147 * on it to satisfy the constraint.
4150 JS_ASSERT(JSVAL_IS_STRING(*vp
));
4151 vobj
= ToXML(cx
, *vp
);
4154 roots
[VAL_ROOT
] = *vp
= OBJECT_TO_JSVAL(vobj
);
4155 vxml
= (JSXML
*) vobj
->getPrivate();
4157 XMLARRAY_SET_MEMBER(&xml
->xml_kids
, i
, vxml
);
4162 kidobj
= js_GetXMLObject(cx
, kid
);
4165 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.starAtom
);
4166 ok
= PutProperty(cx
, kidobj
, id
, vp
);
4172 * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4174 nameqn
= ToXMLName(cx
, IdToJsval(id
), &funid
);
4177 if (!JSID_IS_VOID(funid
)) {
4178 ok
= js_SetProperty(cx
, obj
, funid
, Valueify(vp
), false);
4182 roots
[ID_ROOT
] = OBJECT_TO_JSVAL(nameobj
);
4184 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4186 * Step 3 of 9.2.1.2.
4187 * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4188 * or an r with r.[[Length]] != 1, throw TypeError.
4190 n
= JSXML_LENGTH(xml
);
4194 ok
= ResolveValue(cx
, xml
, &rxml
);
4197 if (!rxml
|| JSXML_LENGTH(rxml
) != 1)
4199 ok
= Append(cx
, xml
, rxml
);
4203 JS_ASSERT(JSXML_LENGTH(xml
) == 1);
4204 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
4207 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
4208 obj
= js_GetXMLObject(cx
, xml
);
4211 roots
[OBJ_ROOT
] = OBJECT_TO_JSVAL(obj
);
4213 /* FALL THROUGH to non-list case */
4218 * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4219 * effort in ToString or [[DeepCopy]].
4222 if (JSXML_HAS_VALUE(xml
))
4226 vxml
->xml_class
== JSXML_CLASS_TEXT
||
4227 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
4228 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4232 rxml
= DeepCopyInLRS(cx
, vxml
, 0);
4233 if (!rxml
|| !js_GetXMLObject(cx
, rxml
))
4236 *vp
= OBJECT_TO_JSVAL(vxml
->object
);
4238 roots
[VAL_ROOT
] = *vp
;
4242 * Erratum: why is this done here, so early? use is way later....
4244 ok
= js_GetDefaultXMLNamespace(cx
, &nsval
);
4248 if (nameobj
->getClass() == &js_AttributeNameClass
) {
4250 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)))
4254 if (vxml
&& vxml
->xml_class
== JSXML_CLASS_LIST
) {
4255 n
= vxml
->xml_kids
.length
;
4257 *vp
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4259 JSString
*left
= KidToString(cx
, vxml
, 0);
4263 JSString
*space
= cx
->runtime
->atomState
.spaceAtom
;
4264 for (i
= 1; i
< n
; i
++) {
4265 left
= js_ConcatStrings(cx
, left
, space
);
4268 JSString
*right
= KidToString(cx
, vxml
, i
);
4271 left
= js_ConcatStrings(cx
, left
, right
);
4276 roots
[VAL_ROOT
] = *vp
= STRING_TO_JSVAL(left
);
4279 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4282 roots
[VAL_ROOT
] = *vp
;
4287 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
4288 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
4291 attrqn
= attr
->name
;
4292 if (EqualStrings(attrqn
->getQNameLocalName(), nameqn
->getQNameLocalName())) {
4293 JSLinearString
*uri
= nameqn
->getNameURI();
4294 if (!uri
|| EqualStrings(attrqn
->getNameURI(), uri
)) {
4298 DeleteNamedProperty(cx
, xml
, attrqn
, JS_TRUE
);
4309 JSLinearString
*uri
= nameqn
->getNameURI();
4310 JSLinearString
*left
, *right
;
4312 left
= right
= cx
->runtime
->emptyString
;
4315 right
= nameqn
->getNamePrefix();
4317 nameqn
= NewXMLQName(cx
, left
, right
, nameqn
->getQNameLocalName());
4322 attr
= js_NewXML(cx
, JSXML_CLASS_ATTRIBUTE
);
4326 attr
->name
= nameqn
;
4329 ok
= XMLARRAY_ADD_MEMBER(cx
, &xml
->xml_attrs
, n
, attr
);
4334 ns
= GetNamespace(cx
, nameqn
, NULL
);
4337 ok
= AddInScopeNamespace(cx
, xml
, ns
);
4343 attr
->xml_value
= JSVAL_TO_STRING(*vp
);
4348 if (!js_IsXMLName(cx
, OBJECT_TO_JSVAL(nameobj
)) &&
4349 !IS_STAR(nameqn
->getQNameLocalName())) {
4355 primitiveAssign
= !vxml
&& !IS_STAR(nameqn
->getQNameLocalName());
4358 k
= n
= xml
->xml_kids
.length
;
4359 matchIndex
= XML_NOT_FOUND
;
4363 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, k
, JSXML
);
4364 if (kid
&& MatchElemName(nameqn
, kid
)) {
4365 if (matchIndex
!= XML_NOT_FOUND
)
4366 DeleteByIndex(cx
, xml
, matchIndex
);
4373 * Erratum: ECMA-357 specified child insertion inconsistently:
4374 * insertChildBefore and insertChildAfter insert an arbitrary XML
4375 * instance, and therefore can create cycles, but appendChild as
4376 * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4377 * its argument. But the "Semantics" in 13.4.4.3 do not include
4378 * any [[DeepCopy]] call.
4380 * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4381 * required adding cycle detection, and allowing duplicate kids to
4382 * be created (see comment 6 in the bug). Allowing duplicate kid
4383 * references means the loop above will delete all but the lowest
4384 * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4385 * parent. Thus the need to restore parent here. This is covered
4386 * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4389 JS_ASSERT(kid2
->parent
== xml
|| !kid2
->parent
);
4395 if (matchIndex
== XML_NOT_FOUND
) {
4400 if (primitiveAssign
) {
4401 JSLinearString
*uri
= nameqn
->getNameURI();
4402 JSLinearString
*left
, *right
;
4404 ns
= JSVAL_TO_OBJECT(nsval
);
4405 left
= ns
->getNameURI();
4406 right
= ns
->getNamePrefix();
4409 right
= nameqn
->getNamePrefix();
4411 nameqn
= NewXMLQName(cx
, left
, right
, nameqn
->getQNameLocalName());
4416 vobj
= js_NewXMLObject(cx
, JSXML_CLASS_ELEMENT
);
4419 vxml
= (JSXML
*) vobj
->getPrivate();
4421 vxml
->name
= nameqn
;
4424 ns
= GetNamespace(cx
, nameqn
, NULL
);
4427 ok
= Replace(cx
, xml
, matchIndex
, OBJECT_TO_JSVAL(vobj
));
4430 ok
= AddInScopeNamespace(cx
, vxml
, ns
);
4437 if (primitiveAssign
) {
4438 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
4439 cursor
.index
= matchIndex
;
4440 kid
= (JSXML
*) cursor
.getCurrent();
4441 if (JSXML_HAS_KIDS(kid
)) {
4442 kid
->xml_kids
.finish(cx
);
4443 kid
->xml_kids
.init();
4444 ok
= kid
->xml_kids
.setCapacity(cx
, 1);
4448 /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4450 ok
= JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
);
4451 if (ok
&& !JSVAL_TO_STRING(*vp
)->empty()) {
4452 roots
[VAL_ROOT
] = *vp
;
4453 if ((JSXML
*) cursor
.getCurrent() == kid
)
4454 ok
= Replace(cx
, kid
, 0, *vp
);
4459 ok
= Replace(cx
, xml
, matchIndex
, *vp
);
4464 js_LeaveLocalRootScope(cx
);
4469 JSAutoByteString bytes
;
4470 if (js_ValueToPrintable(cx
, IdToValue(id
), &bytes
))
4471 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_XMLLIST_PUT
, bytes
.ptr());
4478 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4480 ResolveValue(JSContext
*cx
, JSXML
*list
, JSXML
**result
)
4482 JSXML
*target
, *base
;
4483 JSObject
*targetprop
;
4487 if (list
->xml_class
!= JSXML_CLASS_LIST
|| list
->xml_kids
.length
!= 0) {
4488 if (!js_GetXMLObject(cx
, list
))
4494 target
= list
->xml_target
;
4495 targetprop
= list
->xml_targetprop
;
4496 if (!target
|| !targetprop
|| IS_STAR(targetprop
->getQNameLocalName())) {
4501 if (targetprop
->getClass() == &js_AttributeNameClass
) {
4506 if (!ResolveValue(cx
, target
, &base
))
4512 if (!js_GetXMLObject(cx
, base
))
4515 id
= OBJECT_TO_JSID(targetprop
);
4516 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4518 target
= (JSXML
*) JSVAL_TO_OBJECT(tv
)->getPrivate();
4520 if (JSXML_LENGTH(target
) == 0) {
4521 if (base
->xml_class
== JSXML_CLASS_LIST
&& JSXML_LENGTH(base
) > 1) {
4525 tv
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
4526 if (!PutProperty(cx
, base
->object
, id
, &tv
))
4528 if (!GetProperty(cx
, base
->object
, id
, &tv
))
4530 target
= (JSXML
*) JSVAL_TO_OBJECT(tv
)->getPrivate();
4538 HasNamedProperty(JSXML
*xml
, JSObject
*nameqn
)
4542 JSXMLNameMatcher matcher
;
4545 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
4547 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
4548 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
4549 found
= HasNamedProperty(kid
, nameqn
);
4556 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
4557 if (nameqn
->getClass() == &js_AttributeNameClass
) {
4558 array
= &xml
->xml_attrs
;
4559 matcher
= MatchAttrName
;
4561 array
= &xml
->xml_kids
;
4562 matcher
= MatchElemName
;
4564 for (i
= 0, n
= array
->length
; i
< n
; i
++) {
4565 JSXML
*kid
= XMLARRAY_MEMBER(array
, i
, JSXML
);
4566 if (kid
&& matcher(nameqn
, kid
))
4575 HasIndexedProperty(JSXML
*xml
, uint32 i
)
4577 if (xml
->xml_class
== JSXML_CLASS_LIST
)
4578 return i
< JSXML_LENGTH(xml
);
4580 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
)
4587 HasSimpleContent(JSXML
*xml
);
4590 HasFunctionProperty(JSContext
*cx
, JSObject
*obj
, jsid funid
, JSBool
*found
)
4596 JS_ASSERT(obj
->getClass() == &js_XMLClass
);
4598 if (!js_LookupProperty(cx
, obj
, funid
, &pobj
, &prop
))
4601 xml
= (JSXML
*) obj
->getPrivate();
4602 if (HasSimpleContent(xml
)) {
4603 AutoObjectRooter
tvr(cx
);
4606 * Search in String.prototype to set found whenever
4607 * GetXMLFunction returns existing function.
4609 if (!js_GetClassPrototype(cx
, NULL
, JSProto_String
, tvr
.addr()))
4612 JS_ASSERT(tvr
.object());
4613 if (!js_LookupProperty(cx
, tvr
.object(), funid
, &pobj
, &prop
))
4617 *found
= (prop
!= NULL
);
4621 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4623 HasProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, JSBool
*found
)
4631 xml
= (JSXML
*) obj
->getPrivate();
4632 if (!js_IdValIsIndex(cx
, id
, &i
, &isIndex
))
4636 *found
= HasIndexedProperty(xml
, i
);
4638 qn
= ToXMLName(cx
, id
, &funid
);
4641 if (!JSID_IS_VOID(funid
)) {
4642 if (!HasFunctionProperty(cx
, obj
, funid
, found
))
4645 *found
= HasNamedProperty(xml
, qn
);
4652 xml_finalize(JSContext
*cx
, JSObject
*obj
)
4654 JSXML
*xml
= (JSXML
*) obj
->getPrivate();
4657 if (xml
->object
== obj
)
4662 xml_trace_vector(JSTracer
*trc
, JSXML
**vec
, uint32 len
)
4667 for (i
= 0; i
< len
; i
++) {
4670 JS_SET_TRACING_INDEX(trc
, "xml_vector", i
);
4677 * XML objects are native. Thus xml_lookupProperty must return a valid
4678 * Shape pointer parameter via *propp to signify "property found". Since the
4679 * only call to xml_lookupProperty is via JSObject::lookupProperty, and then
4680 * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
4681 * JSOP_IN case in the interpreter, the only time we add a Shape here is when
4682 * an unqualified name is being accessed or when "name in xml" is called.
4684 * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4685 * giving it an shape with (getter, setter) == (GetProperty, PutProperty).
4687 * NB: xml_deleteProperty must take care to remove any property added here.
4689 * FIXME This clashes with the function namespace implementation which also
4690 * uses native properties. Effectively after xml_lookupProperty any property
4691 * stored previously using assignments to xml.function::name will be removed.
4692 * We partially workaround the problem in GetXMLFunction. There we take
4693 * advantage of the fact that typically function:: is used to access the
4694 * functions from XML.prototype. So when js_GetProperty returns a non-function
4695 * property, we assume that it represents the result of GetProperty setter
4696 * hiding the function and use an extra prototype chain lookup to recover it.
4697 * For a proper solution see bug 355257.
4700 xml_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4709 xml
= (JSXML
*) obj
->getPrivate();
4710 if (js_IdIsIndex(id
, &i
)) {
4711 found
= HasIndexedProperty(xml
, i
);
4713 qn
= ToXMLName(cx
, IdToJsval(id
), &funid
);
4716 if (!JSID_IS_VOID(funid
))
4717 return js_LookupProperty(cx
, obj
, funid
, objp
, propp
);
4718 found
= HasNamedProperty(xml
, qn
);
4724 const Shape
*shape
=
4725 js_AddNativeProperty(cx
, obj
, id
,
4726 Valueify(GetProperty
), Valueify(PutProperty
),
4727 SHAPE_INVALID_SLOT
, JSPROP_ENUMERATE
,
4733 *propp
= (JSProperty
*) shape
;
4739 xml_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*v
,
4740 PropertyOp getter
, PropertyOp setter
, uintN attrs
)
4742 if (IsFunctionObject(*v
) || getter
|| setter
||
4743 (attrs
& JSPROP_ENUMERATE
) == 0 ||
4744 (attrs
& (JSPROP_READONLY
| JSPROP_PERMANENT
| JSPROP_SHARED
))) {
4745 return js_DefineProperty(cx
, obj
, id
, v
, getter
, setter
, attrs
);
4748 jsval tmp
= Jsvalify(*v
);
4749 return PutProperty(cx
, obj
, id
, &tmp
);
4753 xml_getProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
4755 if (JSID_IS_DEFAULT_XML_NAMESPACE(id
)) {
4760 return GetProperty(cx
, obj
, id
, Jsvalify(vp
));
4764 xml_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
4766 return PutProperty(cx
, obj
, id
, Jsvalify(vp
));
4770 xml_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
4773 if (!HasProperty(cx
, obj
, IdToJsval(id
), &found
))
4776 *attrsp
= found
? JSPROP_ENUMERATE
: 0;
4781 xml_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
4784 if (!HasProperty(cx
, obj
, IdToJsval(id
), &found
))
4788 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4789 JSMSG_CANT_SET_XML_ATTRS
);
4796 xml_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
4804 idval
= IdToJsval(id
);
4805 xml
= (JSXML
*) obj
->getPrivate();
4806 if (js_IdIsIndex(id
, &index
)) {
4807 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
4808 /* See NOTE in spec: this variation is reserved for future use. */
4809 ReportBadXMLName(cx
, IdToValue(id
));
4813 /* ECMA-357 9.2.1.3. */
4814 DeleteListElement(cx
, xml
, index
);
4816 nameqn
= ToXMLName(cx
, idval
, &funid
);
4819 if (!JSID_IS_VOID(funid
))
4820 return js_DeleteProperty(cx
, obj
, funid
, rval
, false);
4822 DeleteNamedProperty(cx
, xml
, nameqn
,
4823 nameqn
->getClass() == &js_AttributeNameClass
);
4827 * If this object has its own (mutable) scope, then we may have added a
4828 * property to the scope in xml_lookupProperty for it to return to mean
4829 * "found" and to provide a handle for access operations to call the
4830 * property's getter or setter. But now it's time to remove any such
4831 * property, to purge the property cache and remove the scope entry.
4833 if (!obj
->nativeEmpty() && !js_DeleteProperty(cx
, obj
, id
, rval
, false))
4836 rval
->setBoolean(true);
4841 xml_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
4843 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, rval
);
4847 xml_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
4850 uint32 length
, index
;
4851 JSXMLArrayCursor
*cursor
;
4853 xml
= (JSXML
*)obj
->getPrivate();
4854 length
= JSXML_LENGTH(xml
);
4857 case JSENUMERATE_INIT
:
4858 case JSENUMERATE_INIT_ALL
:
4860 statep
->setInt32(0);
4862 cursor
= cx
->create
<JSXMLArrayCursor
>(&xml
->xml_kids
);
4865 statep
->setPrivate(cursor
);
4868 *idp
= INT_TO_JSID(length
);
4871 case JSENUMERATE_NEXT
:
4872 if (statep
->isInt32(0)) {
4876 cursor
= (JSXMLArrayCursor
*) statep
->toPrivate();
4877 if (cursor
&& cursor
->array
&& (index
= cursor
->index
) < length
) {
4878 *idp
= INT_TO_JSID(index
);
4879 cursor
->index
= index
+ 1;
4884 case JSENUMERATE_DESTROY
:
4885 if (!statep
->isInt32(0)) {
4886 cursor
= (JSXMLArrayCursor
*) statep
->toPrivate();
4888 cx
->destroy(cursor
);
4897 xml_typeOf(JSContext
*cx
, JSObject
*obj
)
4903 xml_hasInstance(JSContext
*cx
, JSObject
*obj
, const Value
*, JSBool
*bp
)
4909 xml_trace(JSTracer
*trc
, JSObject
*obj
)
4911 JSXML
*xml
= (JSXML
*) obj
->getPrivate();
4913 JS_CALL_TRACER(trc
, xml
, JSTRACE_XML
, "private");
4917 xml_fix(JSContext
*cx
, JSObject
*obj
, bool *success
, AutoIdVector
*props
)
4919 JS_ASSERT(obj
->isExtensible());
4925 xml_clear(JSContext
*cx
, JSObject
*obj
)
4930 HasSimpleContent(JSXML
*xml
)
4937 switch (xml
->xml_class
) {
4938 case JSXML_CLASS_COMMENT
:
4939 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
4941 case JSXML_CLASS_LIST
:
4942 if (xml
->xml_kids
.length
== 0)
4944 if (xml
->xml_kids
.length
== 1) {
4945 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
4954 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
4955 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
4956 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
4966 * 11.2.2.1 Step 3(d) onward.
4969 js_GetXMLMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
4971 JS_ASSERT(JS_InstanceOf(cx
, obj
, Jsvalify(&js_XMLClass
), NULL
));
4973 if (JSID_IS_OBJECT(id
)) {
4976 if (!js_IsFunctionQName(cx
, JSID_TO_OBJECT(id
), &funid
))
4978 if (!JSID_IS_VOID(funid
))
4983 * As our callers have a bad habit of passing a pointer to an unrooted
4984 * local value as vp, we use a proper root here.
4986 AutoValueRooter
tvr(cx
);
4987 JSBool ok
= GetXMLFunction(cx
, obj
, id
, Jsvalify(tvr
.addr()));
4993 js_TestXMLEquality(JSContext
*cx
, const Value
&v1
, const Value
&v2
, JSBool
*bp
)
4998 JSString
*str
, *vstr
;
5003 if (v1
.isObject() && v1
.toObject().isXML()) {
5004 obj
= &v1
.toObject();
5008 obj
= &v2
.toObject();
5011 JS_ASSERT(JS_InstanceOf(cx
, obj
, Jsvalify(&js_XMLClass
), NULL
));
5013 xml
= (JSXML
*) obj
->getPrivate();
5015 if (!JSVAL_IS_PRIMITIVE(v
)) {
5016 vobj
= JSVAL_TO_OBJECT(v
);
5018 vxml
= (JSXML
*) vobj
->getPrivate();
5021 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5022 ok
= Equals(cx
, xml
, v
, bp
);
5024 if (vxml
->xml_class
== JSXML_CLASS_LIST
) {
5025 ok
= Equals(cx
, vxml
, OBJECT_TO_JSVAL(obj
), bp
);
5027 if (((xml
->xml_class
== JSXML_CLASS_TEXT
||
5028 xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5029 HasSimpleContent(vxml
)) ||
5030 ((vxml
->xml_class
== JSXML_CLASS_TEXT
||
5031 vxml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) &&
5032 HasSimpleContent(xml
))) {
5033 ok
= js_EnterLocalRootScope(cx
);
5035 ok
= (str
= js_ValueToString(cx
, ObjectValue(*obj
))) &&
5036 (vstr
= js_ValueToString(cx
, Valueify(v
)));
5038 ok
= EqualStrings(cx
, str
, vstr
, bp
);
5039 js_LeaveLocalRootScope(cx
);
5042 ok
= XMLEquals(cx
, xml
, vxml
, bp
);
5046 ok
= js_EnterLocalRootScope(cx
);
5048 if (HasSimpleContent(xml
)) {
5049 ok
= (str
= js_ValueToString(cx
, ObjectValue(*obj
))) &&
5050 (vstr
= js_ValueToString(cx
, Valueify(v
)));
5052 ok
= EqualStrings(cx
, str
, vstr
, bp
);
5053 } else if (JSVAL_IS_STRING(v
) || JSVAL_IS_NUMBER(v
)) {
5054 str
= js_ValueToString(cx
, ObjectValue(*obj
));
5057 } else if (JSVAL_IS_STRING(v
)) {
5058 ok
= EqualStrings(cx
, str
, JSVAL_TO_STRING(v
), bp
);
5060 ok
= JS_ValueToNumber(cx
, STRING_TO_JSVAL(str
), &d
);
5062 d2
= JSVAL_IS_INT(v
) ? JSVAL_TO_INT(v
)
5063 : JSVAL_TO_DOUBLE(v
);
5064 *bp
= JSDOUBLE_COMPARE(d
, ==, d2
, JS_FALSE
);
5070 js_LeaveLocalRootScope(cx
);
5077 js_ConcatenateXML(JSContext
*cx
, JSObject
*obj
, JSObject
*robj
, Value
*vp
)
5081 JSXML
*list
, *lxml
, *rxml
;
5083 JS_ASSERT(JS_InstanceOf(cx
, obj
, Jsvalify(&js_XMLClass
), NULL
));
5084 ok
= js_EnterLocalRootScope(cx
);
5088 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5094 list
= (JSXML
*) listobj
->getPrivate();
5095 lxml
= (JSXML
*) obj
->getPrivate();
5096 ok
= Append(cx
, list
, lxml
);
5100 JS_ASSERT(robj
->isXML());
5101 rxml
= (JSXML
*) robj
->getPrivate();
5102 ok
= Append(cx
, list
, rxml
);
5106 vp
->setObject(*listobj
);
5108 js_LeaveLocalRootScopeWithResult(cx
, *vp
);
5112 JS_FRIEND_DATA(Class
) js_XMLClass
= {
5114 JSCLASS_HAS_PRIVATE
| JSCLASS_MARK_IS_TRACE
|
5115 JSCLASS_HAS_CACHED_PROTO(JSProto_XML
),
5116 PropertyStub
, /* addProperty */
5117 PropertyStub
, /* delProperty */
5118 PropertyStub
, /* getProperty */
5119 PropertyStub
, /* setProperty */
5124 NULL
, /* reserved0 */
5125 NULL
, /* checkAccess */
5127 NULL
, /* construct */
5128 NULL
, /* xdrObject */
5130 JS_CLASS_TRACE(xml_trace
),
5144 NULL
, /* thisObject */
5150 StartNonListXMLMethod(JSContext
*cx
, jsval
*vp
, JSObject
**objp
)
5156 JS_ASSERT(VALUE_IS_FUNCTION(cx
, *vp
));
5158 *objp
= JS_THIS_OBJECT(cx
, vp
);
5159 xml
= (JSXML
*) GetInstancePrivate(cx
, *objp
, &js_XMLClass
, Valueify(vp
+ 2));
5160 if (!xml
|| xml
->xml_class
!= JSXML_CLASS_LIST
)
5163 if (xml
->xml_kids
.length
== 1) {
5164 xml
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5166 *objp
= js_GetXMLObject(cx
, xml
);
5169 vp
[1] = OBJECT_TO_JSVAL(*objp
);
5174 fun
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
));
5175 JS_snprintf(numBuf
, sizeof numBuf
, "%u", xml
->xml_kids
.length
);
5176 JSAutoByteString funNameBytes
;
5177 if (const char *funName
= GetFunctionNameBytes(cx
, fun
, &funNameBytes
)) {
5178 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NON_LIST_XML_METHOD
,
5184 /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5185 #define XML_METHOD_PROLOG \
5186 JSObject *obj = JS_THIS_OBJECT(cx, vp); \
5187 JSXML *xml = (JSXML *)GetInstancePrivate(cx, obj, &js_XMLClass, Valueify(vp+2)); \
5191 #define NON_LIST_XML_METHOD_PROLOG \
5193 JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5196 JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5199 xml_addNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
5203 NON_LIST_XML_METHOD_PROLOG
;
5204 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
5206 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5210 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
5212 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
5214 ns
= JSVAL_TO_OBJECT(*vp
);
5215 if (!AddInScopeNamespace(cx
, xml
, ns
))
5217 ns
->setNamespaceDeclared(JSVAL_TRUE
);
5220 *vp
= OBJECT_TO_JSVAL(obj
);
5225 xml_appendChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
5231 NON_LIST_XML_METHOD_PROLOG
;
5232 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5237 if (!js_GetAnyName(cx
, &name
))
5240 if (!GetProperty(cx
, obj
, name
, &v
))
5243 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5244 vobj
= JSVAL_TO_OBJECT(v
);
5245 JS_ASSERT(vobj
->isXML());
5246 vxml
= (JSXML
*) vobj
->getPrivate();
5247 JS_ASSERT(vxml
->xml_class
== JSXML_CLASS_LIST
);
5249 if (!IndexToId(cx
, vxml
->xml_kids
.length
, &name
))
5251 *vp
= (argc
!= 0) ? vp
[2] : JSVAL_VOID
;
5253 if (!PutProperty(cx
, JSVAL_TO_OBJECT(v
), name
, vp
))
5256 *vp
= OBJECT_TO_JSVAL(obj
);
5260 /* XML and XMLList */
5262 xml_attribute(JSContext
*cx
, uintN argc
, jsval
*vp
)
5267 js_ReportMissingArg(cx
, Valueify(*vp
), 0);
5271 qn
= ToAttributeName(cx
, vp
[2]);
5274 vp
[2] = OBJECT_TO_JSVAL(qn
); /* local root */
5276 jsid id
= OBJECT_TO_JSID(qn
);
5277 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), id
, vp
);
5280 /* XML and XMLList */
5282 xml_attributes(JSContext
*cx
, uintN argc
, jsval
*vp
)
5284 jsval name
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
);
5285 JSObject
*qn
= ToAttributeName(cx
, name
);
5289 AutoObjectRooter
tvr(cx
, qn
);
5290 jsid id
= OBJECT_TO_JSID(qn
);
5291 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), id
, vp
);
5295 xml_list_helper(JSContext
*cx
, JSXML
*xml
, jsval
*rval
)
5300 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
5304 *rval
= OBJECT_TO_JSVAL(listobj
);
5305 list
= (JSXML
*) listobj
->getPrivate();
5306 list
->xml_target
= xml
;
5311 ValueToId(JSContext
*cx
, jsval v
, AutoIdRooter
*idr
)
5313 if (JSVAL_IS_INT(v
)) {
5314 jsint i
= JSVAL_TO_INT(v
);
5315 if (INT_FITS_IN_JSID(i
))
5316 *idr
->addr() = INT_TO_JSID(i
);
5317 else if (!js_ValueToStringId(cx
, Valueify(v
), idr
->addr()))
5319 } else if (JSVAL_IS_STRING(v
)) {
5320 JSAtom
*atom
= js_AtomizeString(cx
, JSVAL_TO_STRING(v
), 0);
5323 *idr
->addr() = ATOM_TO_JSID(atom
);
5324 } else if (!JSVAL_IS_PRIMITIVE(v
)) {
5325 *idr
->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v
));
5327 ReportBadXMLName(cx
, Valueify(v
));
5334 xml_child_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval name
,
5342 /* ECMA-357 13.4.4.6 */
5343 JS_ASSERT(xml
->xml_class
!= JSXML_CLASS_LIST
);
5345 if (!js_IdValIsIndex(cx
, name
, &index
, &isIndex
))
5349 if (index
>= JSXML_LENGTH(xml
)) {
5352 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, index
, JSXML
);
5356 kidobj
= js_GetXMLObject(cx
, kid
);
5359 *rval
= OBJECT_TO_JSVAL(kidobj
);
5365 AutoIdRooter
idr(cx
);
5366 if (!ValueToId(cx
, name
, &idr
))
5369 return GetProperty(cx
, obj
, idr
.id(), rval
);
5372 /* XML and XMLList */
5374 xml_child(JSContext
*cx
, uintN argc
, jsval
*vp
)
5381 jsval name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5382 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5383 /* ECMA-357 13.5.4.4 */
5384 list
= xml_list_helper(cx
, xml
, vp
);
5388 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
5389 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
5390 kidobj
= js_GetXMLObject(cx
, kid
);
5393 if (!xml_child_helper(cx
, kidobj
, kid
, name
, &v
))
5395 if (JSVAL_IS_VOID(v
)) {
5396 /* The property didn't exist in this kid. */
5400 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
5401 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
5402 if ((!JSXML_HAS_KIDS(vxml
) || vxml
->xml_kids
.length
!= 0) &&
5403 !Append(cx
, list
, vxml
)) {
5410 /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5411 if (!xml_child_helper(cx
, obj
, xml
, name
, vp
))
5413 if (JSVAL_IS_VOID(*vp
) && !xml_list_helper(cx
, xml
, vp
))
5419 xml_childIndex(JSContext
*cx
, uintN argc
, jsval
*vp
)
5424 NON_LIST_XML_METHOD_PROLOG
;
5425 parent
= xml
->parent
;
5426 if (!parent
|| xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
) {
5427 *vp
= DOUBLE_TO_JSVAL(js_NaN
);
5430 for (i
= 0, n
= JSXML_LENGTH(parent
); i
< n
; i
++) {
5431 if (XMLARRAY_MEMBER(&parent
->xml_kids
, i
, JSXML
) == xml
)
5435 if (i
<= JSVAL_INT_MAX
)
5436 *vp
= INT_TO_JSVAL(i
);
5438 *vp
= DOUBLE_TO_JSVAL(i
);
5442 /* XML and XMLList */
5444 xml_children(JSContext
*cx
, uintN argc
, jsval
*vp
)
5446 jsid name
= ATOM_TO_JSID(cx
->runtime
->atomState
.starAtom
);
5447 return GetProperty(cx
, JS_THIS_OBJECT(cx
, vp
), name
, vp
);
5450 /* XML and XMLList */
5452 xml_comments_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
5454 JSXML
*list
, *kid
, *vxml
;
5460 list
= xml_list_helper(cx
, xml
, vp
);
5466 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5467 /* 13.5.4.6 Step 2. */
5468 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5469 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5470 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5471 ok
= js_EnterLocalRootScope(cx
);
5474 kidobj
= js_GetXMLObject(cx
, kid
);
5476 ok
= xml_comments_helper(cx
, kidobj
, kid
, &v
);
5481 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
5484 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
5485 if (JSXML_LENGTH(vxml
) != 0) {
5486 ok
= Append(cx
, list
, vxml
);
5493 /* 13.4.4.9 Step 2. */
5494 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5495 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5496 if (kid
&& kid
->xml_class
== JSXML_CLASS_COMMENT
) {
5497 ok
= Append(cx
, list
, kid
);
5508 xml_comments(JSContext
*cx
, uintN argc
, jsval
*vp
)
5511 return xml_comments_helper(cx
, obj
, xml
, vp
);
5514 /* XML and XMLList */
5516 xml_contains(JSContext
*cx
, uintN argc
, jsval
*vp
)
5523 value
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5524 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5526 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
5527 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
5528 kidobj
= js_GetXMLObject(cx
, kid
);
5529 if (!kidobj
|| !js_TestXMLEquality(cx
, ObjectValue(*kidobj
), Valueify(value
), &eq
))
5535 if (!js_TestXMLEquality(cx
, ObjectValue(*obj
), Valueify(value
), &eq
))
5538 *vp
= BOOLEAN_TO_JSVAL(eq
);
5542 /* XML and XMLList */
5544 xml_copy(JSContext
*cx
, uintN argc
, jsval
*vp
)
5549 copy
= DeepCopy(cx
, xml
, NULL
, 0);
5552 *vp
= OBJECT_TO_JSVAL(copy
->object
);
5556 /* XML and XMLList */
5558 xml_descendants(JSContext
*cx
, uintN argc
, jsval
*vp
)
5564 name
= argc
== 0 ? ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
) : vp
[2];
5565 list
= Descendants(cx
, xml
, name
);
5568 *vp
= OBJECT_TO_JSVAL(list
->object
);
5572 /* XML and XMLList */
5574 xml_elements_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
5575 JSObject
*nameqn
, jsval
*vp
)
5583 list
= xml_list_helper(cx
, xml
, vp
);
5587 list
->xml_targetprop
= nameqn
;
5590 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
5592 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
5593 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
5594 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5595 ok
= js_EnterLocalRootScope(cx
);
5598 kidobj
= js_GetXMLObject(cx
, kid
);
5600 ok
= xml_elements_helper(cx
, kidobj
, kid
, nameqn
, &v
);
5605 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
5608 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
5609 if (JSXML_LENGTH(vxml
) != 0) {
5610 ok
= Append(cx
, list
, vxml
);
5617 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
5618 JSXML
*kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5619 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
&&
5620 MatchElemName(nameqn
, kid
)) {
5621 ok
= Append(cx
, list
, kid
);
5632 xml_elements(JSContext
*cx
, uintN argc
, jsval
*vp
)
5640 name
= (argc
== 0) ? ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
) : vp
[2];
5641 nameqn
= ToXMLName(cx
, name
, &funid
);
5644 vp
[2] = OBJECT_TO_JSVAL(nameqn
);
5646 if (!JSID_IS_VOID(funid
))
5647 return xml_list_helper(cx
, xml
, vp
) != NULL
;
5649 return xml_elements_helper(cx
, obj
, xml
, nameqn
, vp
);
5652 /* XML and XMLList */
5654 xml_hasOwnProperty(JSContext
*cx
, uintN argc
, jsval
*vp
)
5660 obj
= JS_THIS_OBJECT(cx
, vp
);
5661 if (!InstanceOf(cx
, obj
, &js_XMLClass
, Valueify(vp
+ 2)))
5664 name
= argc
!= 0 ? vp
[2] : JSVAL_VOID
;
5665 if (!HasProperty(cx
, obj
, name
, &found
))
5671 return js_HasOwnPropertyHelper(cx
, js_LookupProperty
, argc
, Valueify(vp
));
5674 /* XML and XMLList */
5676 xml_hasComplexContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
5684 switch (xml
->xml_class
) {
5685 case JSXML_CLASS_ATTRIBUTE
:
5686 case JSXML_CLASS_COMMENT
:
5687 case JSXML_CLASS_PROCESSING_INSTRUCTION
:
5688 case JSXML_CLASS_TEXT
:
5691 case JSXML_CLASS_LIST
:
5692 if (xml
->xml_kids
.length
== 0) {
5694 } else if (xml
->xml_kids
.length
== 1) {
5695 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
5697 kidobj
= js_GetXMLObject(cx
, kid
);
5701 xml
= (JSXML
*) obj
->getPrivate();
5708 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
5709 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
5710 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
5720 /* XML and XMLList */
5722 xml_hasSimpleContent(JSContext
*cx
, uintN argc
, jsval
*vp
)
5725 *vp
= BOOLEAN_TO_JSVAL(HasSimpleContent(xml
));
5730 FindInScopeNamespaces(JSContext
*cx
, JSXML
*xml
, JSXMLArray
*nsarray
)
5732 uint32 length
, i
, j
, n
;
5734 JSLinearString
*prefix
, *prefix2
;
5736 length
= nsarray
->length
;
5738 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
5740 for (i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
5741 ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
5745 prefix
= ns
->getNamePrefix();
5746 for (j
= 0; j
< length
; j
++) {
5747 ns2
= XMLARRAY_MEMBER(nsarray
, j
, JSObject
);
5749 prefix2
= ns2
->getNamePrefix();
5750 if ((prefix2
&& prefix
)
5751 ? EqualStrings(prefix2
, prefix
)
5752 : EqualStrings(ns2
->getNameURI(), ns
->getNameURI())) {
5759 if (!XMLARRAY_APPEND(cx
, nsarray
, ns
))
5764 } while ((xml
= xml
->parent
) != NULL
);
5765 JS_ASSERT(length
== nsarray
->length
);
5771 * Populate a new JS array with elements of array and place the result into
5772 * rval. rval must point to a rooted location.
5775 NamespacesToJSArray(JSContext
*cx
, JSXMLArray
*array
, jsval
*rval
)
5777 JSObject
*arrayobj
= NewDenseEmptyArray(cx
);
5780 *rval
= OBJECT_TO_JSVAL(arrayobj
);
5782 AutoValueRooter
tvr(cx
);
5783 for (uint32 i
= 0, n
= array
->length
; i
< n
; i
++) {
5784 JSObject
*ns
= XMLARRAY_MEMBER(array
, i
, JSObject
);
5787 tvr
.set(ObjectValue(*ns
));
5788 if (!arrayobj
->setProperty(cx
, INT_TO_JSID(i
), tvr
.addr(), false))
5795 xml_inScopeNamespaces(JSContext
*cx
, uintN argc
, jsval
*vp
)
5797 NON_LIST_XML_METHOD_PROLOG
;
5799 AutoNamespaceArray
namespaces(cx
);
5800 return FindInScopeNamespaces(cx
, xml
, &namespaces
.array
) &&
5801 NamespacesToJSArray(cx
, &namespaces
.array
, vp
);
5805 xml_insertChildAfter(JSContext
*cx
, uintN argc
, jsval
*vp
)
5811 NON_LIST_XML_METHOD_PROLOG
;
5812 *vp
= OBJECT_TO_JSVAL(obj
);
5813 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
5817 if (JSVAL_IS_NULL(arg
)) {
5821 if (!VALUE_IS_XML(arg
))
5823 kid
= (JSXML
*) JSVAL_TO_OBJECT(arg
)->getPrivate();
5824 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
5825 if (i
== XML_NOT_FOUND
)
5830 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5833 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
5837 xml_insertChildBefore(JSContext
*cx
, uintN argc
, jsval
*vp
)
5843 NON_LIST_XML_METHOD_PROLOG
;
5844 *vp
= OBJECT_TO_JSVAL(obj
);
5845 if (!JSXML_HAS_KIDS(xml
) || argc
== 0)
5849 if (JSVAL_IS_NULL(arg
)) {
5851 i
= xml
->xml_kids
.length
;
5853 if (!VALUE_IS_XML(arg
))
5855 kid
= (JSXML
*) JSVAL_TO_OBJECT(arg
)->getPrivate();
5856 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_kids
, kid
, NULL
);
5857 if (i
== XML_NOT_FOUND
)
5861 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
5864 return Insert(cx
, xml
, i
, argc
>= 2 ? vp
[3] : JSVAL_VOID
);
5867 /* XML and XMLList */
5869 xml_length(JSContext
*cx
, uintN argc
, jsval
*vp
)
5872 if (xml
->xml_class
!= JSXML_CLASS_LIST
) {
5875 uint32 l
= xml
->xml_kids
.length
;
5876 if (l
<= JSVAL_INT_MAX
)
5877 *vp
= INT_TO_JSVAL(l
);
5879 *vp
= DOUBLE_TO_JSVAL(l
);
5885 xml_localName(JSContext
*cx
, uintN argc
, jsval
*vp
)
5887 NON_LIST_XML_METHOD_PROLOG
;
5888 *vp
= xml
->name
? xml
->name
->getQNameLocalNameVal() : JSVAL_NULL
;
5893 xml_name(JSContext
*cx
, uintN argc
, jsval
*vp
)
5895 NON_LIST_XML_METHOD_PROLOG
;
5896 *vp
= OBJECT_TO_JSVAL(xml
->name
);
5901 xml_namespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
5903 JSLinearString
*prefix
, *nsprefix
;
5907 NON_LIST_XML_METHOD_PROLOG
;
5908 if (argc
== 0 && !JSXML_HAS_NAME(xml
)) {
5916 JSString
*str
= js_ValueToString(cx
, Valueify(vp
[2]));
5919 prefix
= str
->ensureLinear(cx
);
5922 vp
[2] = STRING_TO_JSVAL(prefix
); /* local root */
5925 AutoNamespaceArray
inScopeNSes(cx
);
5926 if (!FindInScopeNamespaces(cx
, xml
, &inScopeNSes
.array
))
5930 ns
= GetNamespace(cx
, xml
->name
, &inScopeNSes
.array
);
5935 for (i
= 0, length
= inScopeNSes
.array
.length
; i
< length
; i
++) {
5936 ns
= XMLARRAY_MEMBER(&inScopeNSes
.array
, i
, JSObject
);
5938 nsprefix
= ns
->getNamePrefix();
5939 if (nsprefix
&& EqualStrings(nsprefix
, prefix
))
5946 *vp
= (!ns
) ? JSVAL_VOID
: OBJECT_TO_JSVAL(ns
);
5951 xml_namespaceDeclarations(JSContext
*cx
, uintN argc
, jsval
*vp
)
5953 NON_LIST_XML_METHOD_PROLOG
;
5954 if (JSXML_HAS_VALUE(xml
))
5957 AutoNamespaceArray
ancestors(cx
);
5958 AutoNamespaceArray
declared(cx
);
5961 while ((yml
= yml
->parent
) != NULL
) {
5962 JS_ASSERT(yml
->xml_class
== JSXML_CLASS_ELEMENT
);
5963 for (uint32 i
= 0, n
= yml
->xml_namespaces
.length
; i
< n
; i
++) {
5964 JSObject
*ns
= XMLARRAY_MEMBER(&yml
->xml_namespaces
, i
, JSObject
);
5965 if (ns
&& !XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
5966 if (!XMLARRAY_APPEND(cx
, &ancestors
.array
, ns
))
5972 for (uint32 i
= 0, n
= xml
->xml_namespaces
.length
; i
< n
; i
++) {
5973 JSObject
*ns
= XMLARRAY_MEMBER(&xml
->xml_namespaces
, i
, JSObject
);
5976 if (!IsDeclared(ns
))
5978 if (!XMLARRAY_HAS_MEMBER(&ancestors
.array
, ns
, namespace_match
)) {
5979 if (!XMLARRAY_APPEND(cx
, &declared
.array
, ns
))
5984 return NamespacesToJSArray(cx
, &declared
.array
, vp
);
5987 static const char js_attribute_str
[] = "attribute";
5988 static const char js_text_str
[] = "text";
5990 /* Exported to jsgc.c #ifdef DEBUG. */
5991 const char *js_xml_class_str
[] = {
5995 "processing-instruction",
6001 xml_nodeKind(JSContext
*cx
, uintN argc
, jsval
*vp
)
6005 NON_LIST_XML_METHOD_PROLOG
;
6006 str
= JS_InternString(cx
, js_xml_class_str
[xml
->xml_class
]);
6009 *vp
= STRING_TO_JSVAL(str
);
6014 NormalizingDelete(JSContext
*cx
, JSXML
*xml
, uint32 index
)
6016 if (xml
->xml_class
== JSXML_CLASS_LIST
)
6017 DeleteListElement(cx
, xml
, index
);
6019 DeleteByIndex(cx
, xml
, index
);
6022 /* XML and XMLList */
6024 xml_normalize_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
)
6031 if (!JSXML_HAS_KIDS(xml
))
6034 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6038 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6039 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6042 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6043 kidobj
= js_GetXMLObject(cx
, kid
);
6044 if (!kidobj
|| !xml_normalize_helper(cx
, kidobj
, kid
))
6046 } else if (kid
->xml_class
== JSXML_CLASS_TEXT
) {
6048 (kid2
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
+ 1, JSXML
)) &&
6049 kid2
->xml_class
== JSXML_CLASS_TEXT
) {
6050 str
= js_ConcatStrings(cx
, kid
->xml_value
, kid2
->xml_value
);
6053 NormalizingDelete(cx
, xml
, i
+ 1);
6054 n
= xml
->xml_kids
.length
;
6055 kid
->xml_value
= str
;
6057 if (kid
->xml_value
->empty()) {
6058 NormalizingDelete(cx
, xml
, i
);
6059 n
= xml
->xml_kids
.length
;
6069 xml_normalize(JSContext
*cx
, uintN argc
, jsval
*vp
)
6072 *vp
= OBJECT_TO_JSVAL(obj
);
6073 return xml_normalize_helper(cx
, obj
, xml
);
6076 /* XML and XMLList */
6078 xml_parent(JSContext
*cx
, uintN argc
, jsval
*vp
)
6080 JSXML
*parent
, *kid
;
6082 JSObject
*parentobj
;
6085 parent
= xml
->parent
;
6086 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6088 n
= xml
->xml_kids
.length
;
6092 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, 0, JSXML
);
6095 parent
= kid
->parent
;
6096 for (i
= 1; i
< n
; i
++) {
6097 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6098 if (kid
&& kid
->parent
!= parent
)
6108 parentobj
= js_GetXMLObject(cx
, parent
);
6111 *vp
= OBJECT_TO_JSVAL(parentobj
);
6115 /* XML and XMLList */
6117 xml_processingInstructions_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
,
6118 JSObject
*nameqn
, jsval
*vp
)
6126 list
= xml_list_helper(cx
, xml
, vp
);
6130 list
->xml_targetprop
= nameqn
;
6133 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6134 /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6135 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
6136 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
6137 if (kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6138 ok
= js_EnterLocalRootScope(cx
);
6141 kidobj
= js_GetXMLObject(cx
, kid
);
6143 ok
= xml_processingInstructions_helper(cx
, kidobj
, kid
,
6149 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
6152 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
6153 if (JSXML_LENGTH(vxml
) != 0) {
6154 ok
= Append(cx
, list
, vxml
);
6161 /* 13.4.4.28 Step 4. */
6162 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6163 JSXML
*kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6164 if (kid
&& kid
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
) {
6165 JSLinearString
*localName
= nameqn
->getQNameLocalName();
6166 if (IS_STAR(localName
) ||
6167 EqualStrings(localName
, kid
->name
->getQNameLocalName())) {
6168 ok
= Append(cx
, list
, kid
);
6180 xml_processingInstructions(JSContext
*cx
, uintN argc
, jsval
*vp
)
6188 name
= (argc
== 0) ? ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
) : vp
[2];
6189 nameqn
= ToXMLName(cx
, name
, &funid
);
6192 vp
[2] = OBJECT_TO_JSVAL(nameqn
);
6194 if (!JSID_IS_VOID(funid
))
6195 return xml_list_helper(cx
, xml
, vp
) != NULL
;
6197 return xml_processingInstructions_helper(cx
, obj
, xml
, nameqn
, vp
);
6201 xml_prependChild(JSContext
*cx
, uintN argc
, jsval
*vp
)
6203 NON_LIST_XML_METHOD_PROLOG
;
6204 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6207 *vp
= OBJECT_TO_JSVAL(obj
);
6208 return Insert(cx
, xml
, 0, argc
!= 0 ? vp
[2] : JSVAL_VOID
);
6211 /* XML and XMLList */
6213 xml_propertyIsEnumerable(JSContext
*cx
, uintN argc
, jsval
*vp
)
6221 if (!js_IdValIsIndex(cx
, vp
[2], &index
, &isIndex
))
6225 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6227 *vp
= BOOLEAN_TO_JSVAL(index
< xml
->xml_kids
.length
);
6230 *vp
= BOOLEAN_TO_JSVAL(index
== 0);
6238 namespace_full_match(const void *a
, const void *b
)
6240 const JSObject
*nsa
= (const JSObject
*) a
;
6241 const JSObject
*nsb
= (const JSObject
*) b
;
6242 JSLinearString
*prefixa
= nsa
->getNamePrefix();
6243 JSLinearString
*prefixb
;
6246 prefixb
= nsb
->getNamePrefix();
6247 if (prefixb
&& !EqualStrings(prefixa
, prefixb
))
6250 return EqualStrings(nsa
->getNameURI(), nsb
->getNameURI());
6254 xml_removeNamespace_helper(JSContext
*cx
, JSXML
*xml
, JSObject
*ns
)
6256 JSObject
*thisns
, *attrns
;
6260 thisns
= GetNamespace(cx
, xml
->name
, &xml
->xml_namespaces
);
6265 for (i
= 0, n
= xml
->xml_attrs
.length
; i
< n
; i
++) {
6266 attr
= XMLARRAY_MEMBER(&xml
->xml_attrs
, i
, JSXML
);
6269 attrns
= GetNamespace(cx
, attr
->name
, &xml
->xml_namespaces
);
6275 i
= XMLARRAY_FIND_MEMBER(&xml
->xml_namespaces
, ns
, namespace_full_match
);
6276 if (i
!= XML_NOT_FOUND
)
6277 XMLArrayDelete(cx
, &xml
->xml_namespaces
, i
, JS_TRUE
);
6279 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6280 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6281 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6282 if (!xml_removeNamespace_helper(cx
, kid
, ns
))
6290 xml_removeNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6294 NON_LIST_XML_METHOD_PROLOG
;
6295 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6297 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6301 if (!NamespaceHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
6303 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6304 ns
= JSVAL_TO_OBJECT(*vp
);
6306 /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6307 if (!xml_removeNamespace_helper(cx
, xml
, ns
))
6310 *vp
= OBJECT_TO_JSVAL(obj
);
6315 xml_replace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6322 NON_LIST_XML_METHOD_PROLOG
;
6323 if (xml
->xml_class
!= JSXML_CLASS_ELEMENT
)
6327 value
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6330 vxml
= VALUE_IS_XML(value
)
6331 ? (JSXML
*) JSVAL_TO_OBJECT(value
)->getPrivate()
6334 if (!JS_ConvertValue(cx
, value
, JSTYPE_STRING
, &vp
[3]))
6338 vxml
= DeepCopy(cx
, vxml
, NULL
, 0);
6341 value
= vp
[3] = OBJECT_TO_JSVAL(vxml
->object
);
6345 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6353 if (!js_IdValIsIndex(cx
, vp
[2], &index
, &haveIndex
))
6359 * Call function QName per spec, not ToXMLName, to avoid attribute
6362 if (!QNameHelper(cx
, NULL
, argc
== 0 ? -1 : 1, vp
+ 2, vp
))
6364 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp
));
6365 nameqn
= JSVAL_TO_OBJECT(*vp
);
6367 i
= xml
->xml_kids
.length
;
6368 index
= XML_NOT_FOUND
;
6371 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6372 if (kid
&& MatchElemName(nameqn
, kid
)) {
6373 if (i
!= XML_NOT_FOUND
)
6374 DeleteByIndex(cx
, xml
, i
);
6379 if (index
== XML_NOT_FOUND
)
6383 if (!Replace(cx
, xml
, index
, value
))
6387 *vp
= OBJECT_TO_JSVAL(obj
);
6392 xml_setChildren(JSContext
*cx
, uintN argc
, jsval
*vp
)
6396 if (!StartNonListXMLMethod(cx
, vp
, &obj
))
6399 *vp
= argc
!= 0 ? vp
[2] : JSVAL_VOID
; /* local root */
6400 if (!PutProperty(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.starAtom
), vp
))
6403 *vp
= OBJECT_TO_JSVAL(obj
);
6408 xml_setLocalName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6412 JSLinearString
*namestr
;
6414 NON_LIST_XML_METHOD_PROLOG
;
6415 if (!JSXML_HAS_NAME(xml
))
6419 namestr
= ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6422 if (!JSVAL_IS_PRIMITIVE(name
) &&
6423 JSVAL_TO_OBJECT(name
)->getClass() == &js_QNameClass
) {
6424 nameqn
= JSVAL_TO_OBJECT(name
);
6425 namestr
= nameqn
->getQNameLocalName();
6427 if (!JS_ConvertValue(cx
, name
, JSTYPE_STRING
, &vp
[2]))
6430 namestr
= JSVAL_TO_STRING(name
)->ensureLinear(cx
);
6436 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6440 xml
->name
->setQNameLocalName(namestr
);
6445 xml_setName(JSContext
*cx
, uintN argc
, jsval
*vp
)
6450 JSXMLArray
*nsarray
;
6454 NON_LIST_XML_METHOD_PROLOG
;
6455 if (!JSXML_HAS_NAME(xml
))
6459 name
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
]);
6462 if (!JSVAL_IS_PRIMITIVE(name
) &&
6463 JSVAL_TO_OBJECT(name
)->getClass() == &js_QNameClass
&&
6464 !(nameqn
= JSVAL_TO_OBJECT(name
))->getNameURI()) {
6465 name
= vp
[2] = nameqn
->getQNameLocalNameVal();
6469 nameqn
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 1, Valueify(&name
));
6473 /* ECMA-357 13.4.4.35 Step 4. */
6474 if (xml
->xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
)
6475 nameqn
->setNameURI(cx
->runtime
->emptyString
);
6477 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6483 * Erratum: nothing in 13.4.4.35 talks about making the name match the
6484 * in-scope namespaces, either by finding an in-scope namespace with a
6485 * matching uri and setting the new name's prefix to that namespace's
6486 * prefix, or by extending the in-scope namespaces for xml (which are in
6487 * xml->parent if xml is an attribute or a PI).
6489 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
6492 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
6494 nsowner
= xml
->parent
;
6497 if (nameqn
->getNamePrefix()) {
6499 * The name being set has a prefix, which originally came from some
6500 * namespace object (which may be the null namespace, where both the
6501 * prefix and uri are the empty string). We must go through a full
6502 * GetNamespace in case that namespace is in-scope in nsowner.
6504 * If we find such an in-scope namespace, we return true right away,
6505 * in this block. Otherwise, we fall through to the final return of
6506 * AddInScopeNamespace(cx, nsowner, ns).
6508 ns
= GetNamespace(cx
, nameqn
, &nsowner
->xml_namespaces
);
6512 /* XXXbe have to test membership to see whether GetNamespace added */
6513 if (XMLARRAY_HAS_MEMBER(&nsowner
->xml_namespaces
, ns
, NULL
))
6517 * At this point, we know prefix of nameqn is null, so its uri can't
6518 * be the empty string (the null namespace always uses the empty string
6519 * for both prefix and uri).
6521 * This means we must inline GetNamespace and specialize it to match
6522 * uri only, never prefix. If we find a namespace with nameqn's uri
6523 * already in nsowner->xml_namespaces, then all that we need do is set
6524 * prefix of nameqn to that namespace's prefix.
6526 * If no such namespace exists, we can create one without going through
6527 * the constructor, because we know uri of nameqn is non-empty (so
6528 * prefix does not need to be converted from null to empty by QName).
6530 JS_ASSERT(!nameqn
->getNameURI()->empty());
6532 nsarray
= &nsowner
->xml_namespaces
;
6533 for (i
= 0, n
= nsarray
->length
; i
< n
; i
++) {
6534 ns
= XMLARRAY_MEMBER(nsarray
, i
, JSObject
);
6535 if (ns
&& EqualStrings(ns
->getNameURI(), nameqn
->getNameURI())) {
6536 nameqn
->setNamePrefix(ns
->getNamePrefix());
6541 ns
= NewXMLNamespace(cx
, NULL
, nameqn
->getNameURI(), JS_TRUE
);
6546 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
6553 xml_setNamespace(JSContext
*cx
, uintN argc
, jsval
*vp
)
6560 NON_LIST_XML_METHOD_PROLOG
;
6561 if (!JSXML_HAS_NAME(xml
))
6564 xml
= CHECK_COPY_ON_WRITE(cx
, xml
, obj
);
6568 ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, obj
,
6569 argc
== 0 ? 0 : 1, Valueify(vp
+ 2));
6572 vp
[0] = OBJECT_TO_JSVAL(ns
);
6573 ns
->setNamespaceDeclared(JSVAL_TRUE
);
6575 qnargv
[0] = OBJECT_TO_JSVAL(ns
);
6576 qnargv
[1] = OBJECT_TO_JSVAL(xml
->name
);
6577 qn
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 2, Valueify(qnargv
));
6584 * Erratum: the spec fails to update the governing in-scope namespaces.
6585 * See the erratum noted in xml_setName, above.
6587 if (xml
->xml_class
== JSXML_CLASS_ELEMENT
) {
6590 if (!xml
->parent
|| xml
->parent
->xml_class
!= JSXML_CLASS_ELEMENT
)
6592 nsowner
= xml
->parent
;
6594 if (!AddInScopeNamespace(cx
, nsowner
, ns
))
6600 /* XML and XMLList */
6602 xml_text_helper(JSContext
*cx
, JSObject
*obj
, JSXML
*xml
, jsval
*vp
)
6604 JSXML
*list
, *kid
, *vxml
;
6610 list
= xml_list_helper(cx
, xml
, vp
);
6614 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6616 for (i
= 0, n
= xml
->xml_kids
.length
; i
< n
; i
++) {
6617 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6618 if (kid
&& kid
->xml_class
== JSXML_CLASS_ELEMENT
) {
6619 ok
= js_EnterLocalRootScope(cx
);
6622 kidobj
= js_GetXMLObject(cx
, kid
);
6624 ok
= xml_text_helper(cx
, kidobj
, kid
, &v
);
6629 js_LeaveLocalRootScopeWithResult(cx
, Valueify(v
));
6632 vxml
= (JSXML
*) JSVAL_TO_OBJECT(v
)->getPrivate();
6633 if (JSXML_LENGTH(vxml
) != 0 && !Append(cx
, list
, vxml
))
6638 for (i
= 0, n
= JSXML_LENGTH(xml
); i
< n
; i
++) {
6639 kid
= XMLARRAY_MEMBER(&xml
->xml_kids
, i
, JSXML
);
6640 if (kid
&& kid
->xml_class
== JSXML_CLASS_TEXT
) {
6641 if (!Append(cx
, list
, kid
))
6650 xml_text(JSContext
*cx
, uintN argc
, jsval
*vp
)
6653 return xml_text_helper(cx
, obj
, xml
, vp
);
6656 /* XML and XMLList */
6658 xml_toString_helper(JSContext
*cx
, JSXML
*xml
)
6660 JSString
*str
, *kidstr
;
6662 if (xml
->xml_class
== JSXML_CLASS_ATTRIBUTE
||
6663 xml
->xml_class
== JSXML_CLASS_TEXT
) {
6664 return xml
->xml_value
;
6667 if (!HasSimpleContent(xml
))
6668 return ToXMLString(cx
, OBJECT_TO_JSVAL(xml
->object
), 0);
6670 str
= cx
->runtime
->emptyString
;
6671 if (!js_EnterLocalRootScope(cx
))
6673 JSXMLArrayCursor
cursor(&xml
->xml_kids
);
6674 while (JSXML
*kid
= (JSXML
*) cursor
.getNext()) {
6675 if (kid
->xml_class
!= JSXML_CLASS_COMMENT
&&
6676 kid
->xml_class
!= JSXML_CLASS_PROCESSING_INSTRUCTION
) {
6677 kidstr
= xml_toString_helper(cx
, kid
);
6682 str
= js_ConcatStrings(cx
, str
, kidstr
);
6687 js_LeaveLocalRootScopeWithResult(cx
, str
);
6692 xml_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
6697 thisv
= JS_THIS(cx
, vp
);
6698 if (JSVAL_IS_NULL(thisv
))
6700 str
= ToXMLString(cx
, thisv
, TO_SOURCE_FLAG
);
6703 *vp
= STRING_TO_JSVAL(str
);
6708 xml_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
6713 str
= xml_toString_helper(cx
, xml
);
6716 *vp
= STRING_TO_JSVAL(str
);
6720 /* XML and XMLList */
6722 xml_toXMLString(JSContext
*cx
, uintN argc
, jsval
*vp
)
6727 thisv
= JS_THIS(cx
, vp
);
6728 if (JSVAL_IS_NULL(thisv
))
6730 str
= ToXMLString(cx
, thisv
, 0);
6733 *vp
= STRING_TO_JSVAL(str
);
6737 /* XML and XMLList */
6739 xml_valueOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
6741 *vp
= JS_THIS(cx
, vp
);
6742 return !JSVAL_IS_NULL(*vp
);
6745 static JSFunctionSpec xml_methods
[] = {
6746 JS_FN("addNamespace", xml_addNamespace
, 1,0),
6747 JS_FN("appendChild", xml_appendChild
, 1,0),
6748 JS_FN(js_attribute_str
, xml_attribute
, 1,0),
6749 JS_FN("attributes", xml_attributes
, 0,0),
6750 JS_FN("child", xml_child
, 1,0),
6751 JS_FN("childIndex", xml_childIndex
, 0,0),
6752 JS_FN("children", xml_children
, 0,0),
6753 JS_FN("comments", xml_comments
, 0,0),
6754 JS_FN("contains", xml_contains
, 1,0),
6755 JS_FN("copy", xml_copy
, 0,0),
6756 JS_FN("descendants", xml_descendants
, 1,0),
6757 JS_FN("elements", xml_elements
, 1,0),
6758 JS_FN("hasOwnProperty", xml_hasOwnProperty
, 1,0),
6759 JS_FN("hasComplexContent", xml_hasComplexContent
, 1,0),
6760 JS_FN("hasSimpleContent", xml_hasSimpleContent
, 1,0),
6761 JS_FN("inScopeNamespaces", xml_inScopeNamespaces
, 0,0),
6762 JS_FN("insertChildAfter", xml_insertChildAfter
, 2,0),
6763 JS_FN("insertChildBefore", xml_insertChildBefore
, 2,0),
6764 JS_FN(js_length_str
, xml_length
, 0,0),
6765 JS_FN(js_localName_str
, xml_localName
, 0,0),
6766 JS_FN(js_name_str
, xml_name
, 0,0),
6767 JS_FN(js_namespace_str
, xml_namespace
, 1,0),
6768 JS_FN("namespaceDeclarations", xml_namespaceDeclarations
, 0,0),
6769 JS_FN("nodeKind", xml_nodeKind
, 0,0),
6770 JS_FN("normalize", xml_normalize
, 0,0),
6771 JS_FN(js_xml_parent_str
, xml_parent
, 0,0),
6772 JS_FN("processingInstructions",xml_processingInstructions
,1,0),
6773 JS_FN("prependChild", xml_prependChild
, 1,0),
6774 JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable
, 1,0),
6775 JS_FN("removeNamespace", xml_removeNamespace
, 1,0),
6776 JS_FN("replace", xml_replace
, 2,0),
6777 JS_FN("setChildren", xml_setChildren
, 1,0),
6778 JS_FN("setLocalName", xml_setLocalName
, 1,0),
6779 JS_FN("setName", xml_setName
, 1,0),
6780 JS_FN("setNamespace", xml_setNamespace
, 1,0),
6781 JS_FN(js_text_str
, xml_text
, 0,0),
6782 JS_FN(js_toSource_str
, xml_toSource
, 0,0),
6783 JS_FN(js_toString_str
, xml_toString
, 0,0),
6784 JS_FN(js_toXMLString_str
, xml_toXMLString
, 0,0),
6785 JS_FN(js_valueOf_str
, xml_valueOf
, 0,0),
6790 CopyXMLSettings(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
6796 /* Note: PRETTY_INDENT is not a boolean setting. */
6797 for (i
= 0; xml_static_props
[i
].name
; i
++) {
6798 name
= xml_static_props
[i
].name
;
6799 if (!JS_GetProperty(cx
, from
, name
, &v
))
6801 if (name
== js_prettyIndent_str
) {
6802 if (!JSVAL_IS_NUMBER(v
))
6805 if (!JSVAL_IS_BOOLEAN(v
))
6808 if (!JS_SetProperty(cx
, to
, name
, &v
))
6816 SetDefaultXMLSettings(JSContext
*cx
, JSObject
*obj
)
6821 /* Note: PRETTY_INDENT is not a boolean setting. */
6822 for (i
= 0; xml_static_props
[i
].name
; i
++) {
6823 v
= (xml_static_props
[i
].name
!= js_prettyIndent_str
)
6824 ? JSVAL_TRUE
: INT_TO_JSVAL(2);
6825 if (!JS_SetProperty(cx
, obj
, xml_static_props
[i
].name
, &v
))
6832 xml_settings(JSContext
*cx
, uintN argc
, jsval
*vp
)
6837 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
6840 *vp
= OBJECT_TO_JSVAL(settings
);
6841 obj
= JS_THIS_OBJECT(cx
, vp
);
6842 return obj
&& CopyXMLSettings(cx
, obj
, settings
);
6846 xml_setSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
6848 JSObject
*obj
, *settings
;
6852 obj
= JS_THIS_OBJECT(cx
, vp
);
6855 v
= (argc
== 0) ? JSVAL_VOID
: vp
[2];
6856 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
)) {
6857 ok
= SetDefaultXMLSettings(cx
, obj
);
6859 if (JSVAL_IS_PRIMITIVE(v
))
6861 settings
= JSVAL_TO_OBJECT(v
);
6862 ok
= CopyXMLSettings(cx
, settings
, obj
);
6868 xml_defaultSettings(JSContext
*cx
, uintN argc
, jsval
*vp
)
6872 settings
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
6875 *vp
= OBJECT_TO_JSVAL(settings
);
6876 return SetDefaultXMLSettings(cx
, settings
);
6879 static JSFunctionSpec xml_static_methods
[] = {
6880 JS_FN("settings", xml_settings
, 0,0),
6881 JS_FN("setSettings", xml_setSettings
, 1,0),
6882 JS_FN("defaultSettings", xml_defaultSettings
, 0,0),
6887 XML(JSContext
*cx
, uintN argc
, Value
*vp
)
6890 JSObject
*xobj
, *vobj
;
6893 jsval v
= argc
? Jsvalify(vp
[2]) : JSVAL_VOID
;
6895 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
6896 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
6898 xobj
= ToXML(cx
, v
);
6901 xml
= (JSXML
*) xobj
->getPrivate();
6903 if (IsConstructing(vp
) && !JSVAL_IS_PRIMITIVE(v
)) {
6904 vobj
= JSVAL_TO_OBJECT(v
);
6905 clasp
= vobj
->getClass();
6906 if (clasp
== &js_XMLClass
||
6907 (clasp
->flags
& JSCLASS_DOCUMENT_OBSERVER
)) {
6908 copy
= DeepCopy(cx
, xml
, NULL
, 0);
6911 vp
->setObject(*copy
->object
);
6916 vp
->setObject(*xobj
);
6921 XMLList(JSContext
*cx
, uintN argc
, jsval
*vp
)
6923 JSObject
*vobj
, *listobj
;
6926 jsval v
= argc
? vp
[2] : JSVAL_VOID
;
6928 if (JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
))
6929 v
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
6931 if (IsConstructing(Valueify(vp
)) && !JSVAL_IS_PRIMITIVE(v
)) {
6932 vobj
= JSVAL_TO_OBJECT(v
);
6933 if (vobj
->isXML()) {
6934 xml
= (JSXML
*) vobj
->getPrivate();
6935 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
6936 listobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
6939 *vp
= OBJECT_TO_JSVAL(listobj
);
6941 list
= (JSXML
*) listobj
->getPrivate();
6942 if (!Append(cx
, list
, xml
))
6949 /* Toggle on XML support since the script has explicitly requested it. */
6950 listobj
= ToXMLList(cx
, v
);
6954 *vp
= OBJECT_TO_JSVAL(listobj
);
6959 JSCList xml_leaks
= JS_INIT_STATIC_CLIST(&xml_leaks
);
6964 js_NewXML(JSContext
*cx
, JSXMLClass xml_class
)
6966 JSXML
*xml
= js_NewGCXML(cx
);
6971 xml
->domnode
= NULL
;
6974 xml
->xml_class
= xml_class
;
6976 if (JSXML_CLASS_HAS_VALUE(xml_class
)) {
6977 xml
->xml_value
= cx
->runtime
->emptyString
;
6979 xml
->xml_kids
.init();
6980 if (xml_class
== JSXML_CLASS_LIST
) {
6981 xml
->xml_target
= NULL
;
6982 xml
->xml_targetprop
= NULL
;
6984 xml
->xml_namespaces
.init();
6985 xml
->xml_attrs
.init();
6990 JS_APPEND_LINK(&xml
->links
, &xml_leaks
);
6991 xml
->serial
= xml_serial
++;
6993 METER(xml_stats
.xml
);
6998 js_TraceXML(JSTracer
*trc
, JSXML
*xml
)
7001 MarkObject(trc
, *xml
->object
, "object");
7003 MarkObject(trc
, *xml
->name
, "name");
7005 JS_CALL_TRACER(trc
, xml
->parent
, JSTRACE_XML
, "xml_parent");
7007 if (JSXML_HAS_VALUE(xml
)) {
7009 MarkString(trc
, xml
->xml_value
, "value");
7013 xml_trace_vector(trc
,
7014 (JSXML
**) xml
->xml_kids
.vector
,
7015 xml
->xml_kids
.length
);
7016 XMLArrayCursorTrace(trc
, xml
->xml_kids
.cursors
);
7017 if (IS_GC_MARKING_TRACER(trc
))
7018 xml
->xml_kids
.trim();
7020 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7021 if (xml
->xml_target
)
7022 JS_CALL_TRACER(trc
, xml
->xml_target
, JSTRACE_XML
, "target");
7023 if (xml
->xml_targetprop
)
7024 MarkObject(trc
, *xml
->xml_targetprop
, "targetprop");
7026 MarkObjectRange(trc
, xml
->xml_namespaces
.length
,
7027 (JSObject
**) xml
->xml_namespaces
.vector
,
7029 XMLArrayCursorTrace(trc
, xml
->xml_namespaces
.cursors
);
7030 if (IS_GC_MARKING_TRACER(trc
))
7031 xml
->xml_namespaces
.trim();
7033 xml_trace_vector(trc
,
7034 (JSXML
**) xml
->xml_attrs
.vector
,
7035 xml
->xml_attrs
.length
);
7036 XMLArrayCursorTrace(trc
, xml
->xml_attrs
.cursors
);
7037 if (IS_GC_MARKING_TRACER(trc
))
7038 xml
->xml_attrs
.trim();
7043 js_NewXMLObject(JSContext
*cx
, JSXMLClass xml_class
)
7045 JSXML
*xml
= js_NewXML(cx
, xml_class
);
7049 AutoXMLRooter
root(cx
, xml
);
7050 return js_GetXMLObject(cx
, xml
);
7054 NewXMLObject(JSContext
*cx
, JSXML
*xml
)
7058 obj
= NewNonFunction
<WithProto::Class
>(cx
, &js_XMLClass
, NULL
, NULL
);
7061 obj
->setPrivate(xml
);
7062 METER(xml_stats
.xmlobj
);
7067 js_GetXMLObject(JSContext
*cx
, JSXML
*xml
)
7073 JS_ASSERT(obj
->getPrivate() == xml
);
7077 obj
= NewXMLObject(cx
, xml
);
7085 js_InitNamespaceClass(JSContext
*cx
, JSObject
*obj
)
7087 return js_InitClass(cx
, obj
, NULL
, &js_NamespaceClass
, Namespace
, 2,
7088 namespace_props
, namespace_methods
, NULL
, NULL
);
7092 js_InitQNameClass(JSContext
*cx
, JSObject
*obj
)
7094 return js_InitClass(cx
, obj
, NULL
, &js_QNameClass
, QName
, 2,
7095 qname_props
, qname_methods
, NULL
, NULL
);
7099 js_InitXMLClass(JSContext
*cx
, JSObject
*obj
)
7101 JSObject
*proto
, *pobj
;
7108 /* Define the isXMLName function. */
7109 if (!JS_DefineFunction(cx
, obj
, js_isXMLName_str
, xml_isXMLName
, 1, 0))
7112 /* Define the XML class constructor and prototype. */
7113 proto
= js_InitClass(cx
, obj
, NULL
, &js_XMLClass
, XML
, 1,
7115 xml_static_props
, xml_static_methods
);
7119 xml
= js_NewXML(cx
, JSXML_CLASS_TEXT
);
7122 proto
->setPrivate(xml
);
7123 xml
->object
= proto
;
7124 METER(xml_stats
.xmlobj
);
7127 * Prepare to set default settings on the XML constructor we just made.
7128 * NB: We can't use JS_GetConstructor, because it calls
7129 * JSObject::getProperty, which is xml_getProperty, which creates a new
7130 * XMLList every time! We must instead call js_LookupProperty directly.
7132 if (!js_LookupProperty(cx
, proto
,
7133 ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
7138 shape
= (Shape
*) prop
;
7139 cval
= Jsvalify(pobj
->nativeGetSlot(shape
->slot
));
7140 JS_ASSERT(VALUE_IS_FUNCTION(cx
, cval
));
7142 /* Set default settings. */
7146 if (!xml_setSettings(cx
, 1, vp
))
7149 /* Define the XMLList function and give it the same prototype as XML. */
7150 fun
= JS_DefineFunction(cx
, obj
, js_XMLList_str
, XMLList
, 1, JSFUN_CONSTRUCTOR
);
7153 if (!js_SetClassPrototype(cx
, FUN_OBJECT(fun
), proto
,
7154 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
7161 js_InitXMLClasses(JSContext
*cx
, JSObject
*obj
)
7163 if (!js_InitNamespaceClass(cx
, obj
))
7165 if (!js_InitQNameClass(cx
, obj
))
7167 return js_InitXMLClass(cx
, obj
);
7171 js_GetFunctionNamespace(JSContext
*cx
, Value
*vp
)
7173 JSObject
*global
= cx
->hasfp() ? cx
->fp()->scopeChain().getGlobal() : cx
->globalObject
;
7175 *vp
= global
->getReservedSlot(JSRESERVED_GLOBAL_FUNCTION_NS
);
7176 if (vp
->isUndefined()) {
7177 JSRuntime
*rt
= cx
->runtime
;
7178 JSLinearString
*prefix
= rt
->atomState
.typeAtoms
[JSTYPE_FUNCTION
];
7179 JSLinearString
*uri
= rt
->atomState
.functionNamespaceURIAtom
;
7180 JSObject
*obj
= NewXMLNamespace(cx
, prefix
, uri
, JS_FALSE
);
7185 * Avoid entraining any in-scope Object.prototype. The loss of
7186 * Namespace.prototype is not detectable, as there is no way to
7187 * refer to this instance in scripts. When used to qualify method
7188 * names, its prefix and uri references are copied to the QName.
7189 * The parent remains set and links back to global.
7193 vp
->setObject(*obj
);
7194 if (!js_SetReservedSlot(cx
, global
, JSRESERVED_GLOBAL_FUNCTION_NS
, *vp
))
7202 * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7203 * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7204 * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
7205 * requirement that fp->varobj lie directly on fp->scopeChain, although
7206 * it should be reachable using the prototype chain from a scope object (cf.
7207 * JSOPTION_VAROBJFIX in jsapi.h).
7209 * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7210 * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7211 * its v argument as the uri of a new Namespace, with "" as the prefix. See
7212 * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7213 * the default XML namespace will be set to ("", n.uri). So the uri string
7214 * is really the only usefully stored value of the default namespace.
7217 js_GetDefaultXMLNamespace(JSContext
*cx
, jsval
*vp
)
7219 JSObject
*ns
, *obj
, *tmp
;
7222 JSObject
*scopeChain
= GetScopeChain(cx
);
7225 for (tmp
= scopeChain
; tmp
; tmp
= tmp
->getParent()) {
7226 Class
*clasp
= tmp
->getClass();
7227 if (clasp
== &js_BlockClass
|| clasp
== &js_WithClass
)
7229 if (!tmp
->getProperty(cx
, JS_DEFAULT_XML_NAMESPACE_ID
, Valueify(&v
)))
7231 if (!JSVAL_IS_PRIMITIVE(v
)) {
7238 ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, obj
, 0, NULL
);
7241 v
= OBJECT_TO_JSVAL(ns
);
7242 if (!obj
->defineProperty(cx
, JS_DEFAULT_XML_NAMESPACE_ID
, Valueify(v
),
7243 PropertyStub
, PropertyStub
, JSPROP_PERMANENT
)) {
7251 js_SetDefaultXMLNamespace(JSContext
*cx
, const Value
&v
)
7254 argv
[0].setString(cx
->runtime
->emptyString
);
7256 JSObject
*ns
= js_ConstructObject(cx
, &js_NamespaceClass
, NULL
, NULL
, 2, argv
);
7260 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
7261 JSObject
&varobj
= fp
->varobj(cx
);
7262 if (!varobj
.defineProperty(cx
, JS_DEFAULT_XML_NAMESPACE_ID
, ObjectValue(*ns
),
7263 PropertyStub
, PropertyStub
, JSPROP_PERMANENT
)) {
7270 js_ToAttributeName(JSContext
*cx
, Value
*vp
)
7274 qn
= ToAttributeName(cx
, Jsvalify(*vp
));
7282 js_EscapeAttributeValue(JSContext
*cx
, JSString
*str
, JSBool quote
)
7284 StringBuffer
sb(cx
);
7285 return EscapeAttributeValue(cx
, sb
, str
, quote
);
7289 js_AddAttributePart(JSContext
*cx
, JSBool isName
, JSString
*str
, JSString
*str2
)
7291 size_t len
= str
->length();
7292 const jschar
*chars
= str
->getChars(cx
);
7296 size_t len2
= str2
->length();
7297 const jschar
*chars2
= str2
->getChars(cx
);
7301 size_t newlen
= (isName
) ? len
+ 1 + len2
: len
+ 2 + len2
+ 1;
7302 jschar
*newchars
= (jschar
*) cx
->malloc((newlen
+1) * sizeof(jschar
));
7306 js_strncpy(newchars
, chars
, len
);
7310 js_strncpy(newchars
, chars2
, len2
);
7315 js_strncpy(newchars
, chars2
, len2
);
7320 return js_NewString(cx
, newchars
- newlen
, newlen
);
7324 js_EscapeElementValue(JSContext
*cx
, JSString
*str
)
7326 StringBuffer
sb(cx
);
7327 return EscapeElementValue(cx
, sb
, str
, 0);
7331 js_ValueToXMLString(JSContext
*cx
, const Value
&v
)
7333 return ToXMLString(cx
, Jsvalify(v
), 0);
7337 js_GetAnyName(JSContext
*cx
, jsid
*idp
)
7339 JSObject
*global
= cx
->hasfp() ? cx
->fp()->scopeChain().getGlobal() : cx
->globalObject
;
7340 Value v
= global
->getReservedSlot(JSProto_AnyName
);
7341 if (v
.isUndefined()) {
7342 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_AnyNameClass
, NULL
, global
);
7346 JS_ASSERT(!obj
->getProto());
7348 JSRuntime
*rt
= cx
->runtime
;
7349 InitXMLQName(obj
, rt
->emptyString
, rt
->emptyString
,
7350 ATOM_TO_STRING(rt
->atomState
.starAtom
));
7351 METER(xml_stats
.qname
);
7354 if (!js_SetReservedSlot(cx
, global
, JSProto_AnyName
, v
))
7357 *idp
= OBJECT_TO_JSID(&v
.toObject());
7362 js_FindXMLProperty(JSContext
*cx
, const Value
&nameval
, JSObject
**objp
, jsid
*idp
)
7368 JSObject
*obj
, *target
, *proto
, *pobj
;
7373 JS_ASSERT(nameval
.isObject());
7374 nameobj
= &nameval
.toObject();
7375 if (nameobj
->getClass() == &js_AnyNameClass
) {
7376 v
= ATOM_TO_JSVAL(cx
->runtime
->atomState
.starAtom
);
7377 nameobj
= js_ConstructObject(cx
, &js_QNameClass
, NULL
, NULL
, 1,
7382 JS_ASSERT(nameobj
->getClass() == &js_AttributeNameClass
||
7383 nameobj
->getClass() == &js_QNameClass
);
7387 if (!IsFunctionQName(cx
, qn
, &funid
))
7390 obj
= &js_GetTopStackFrame(cx
)->scopeChain();
7392 /* Skip any With object that can wrap XML. */
7394 while (target
->getClass() == &js_WithClass
) {
7395 proto
= target
->getProto();
7401 if (target
->isXML()) {
7402 if (JSID_IS_VOID(funid
)) {
7403 xml
= (JSXML
*) target
->getPrivate();
7404 found
= HasNamedProperty(xml
, qn
);
7406 if (!HasFunctionProperty(cx
, target
, funid
, &found
))
7410 *idp
= OBJECT_TO_JSID(nameobj
);
7414 } else if (!JSID_IS_VOID(funid
)) {
7415 if (!target
->lookupProperty(cx
, funid
, &pobj
, &prop
))
7423 } while ((obj
= obj
->getParent()) != NULL
);
7425 JSAutoByteString printable
;
7426 JSString
*str
= ConvertQNameToString(cx
, nameobj
);
7427 if (str
&& js_ValueToPrintable(cx
, StringValue(str
), &printable
)) {
7428 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
7429 JSMSG_UNDEFINED_XML_NAME
, printable
.ptr());
7435 GetXMLFunction(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
7437 JS_ASSERT(obj
->isXML());
7440 * See comments before xml_lookupProperty about the need for the proto
7443 JSObject
*target
= obj
;
7444 AutoObjectRooter
tvr(cx
);
7446 if (!js_GetProperty(cx
, target
, id
, Valueify(vp
)))
7448 if (VALUE_IS_FUNCTION(cx
, *vp
))
7450 target
= target
->getProto();
7451 if (target
== NULL
|| !target
->isNative())
7453 tvr
.setObject(target
);
7456 JSXML
*xml
= (JSXML
*) obj
->getPrivate();
7457 if (!HasSimpleContent(xml
))
7460 /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
7461 if (!js_GetClassPrototype(cx
, NULL
, JSProto_String
, tvr
.addr()))
7464 JS_ASSERT(tvr
.object());
7465 return tvr
.object()->getProperty(cx
, id
, Valueify(vp
));
7469 GetPrivate(JSContext
*cx
, JSObject
*obj
, const char *method
)
7473 xml
= (JSXML
*) GetInstancePrivate(cx
, obj
, &js_XMLClass
, NULL
);
7475 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
7476 JSMSG_INCOMPATIBLE_METHOD
,
7477 js_XML_str
, method
, obj
->getClass()->name
);
7483 js_GetXMLDescendants(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
7487 xml
= GetPrivate(cx
, obj
, "descendants internal method");
7491 list
= Descendants(cx
, xml
, id
);
7494 *vp
= OBJECT_TO_JSVAL(list
->object
);
7499 js_DeleteXMLListElements(JSContext
*cx
, JSObject
*listobj
)
7504 list
= (JSXML
*) listobj
->getPrivate();
7505 for (n
= list
->xml_kids
.length
; n
!= 0; --n
)
7506 DeleteListElement(cx
, list
, 0);
7516 JSXMLArrayCursor cursor
;
7518 JSXMLFilter(JSXML
*list
, JSXMLArray
*array
)
7519 : list(list
), result(NULL
), kid(NULL
), cursor(array
) {}
7525 xmlfilter_trace(JSTracer
*trc
, JSObject
*obj
)
7527 JSXMLFilter
*filter
= (JSXMLFilter
*) obj
->getPrivate();
7531 JS_ASSERT(filter
->list
);
7532 JS_CALL_TRACER(trc
, filter
->list
, JSTRACE_XML
, "list");
7534 JS_CALL_TRACER(trc
, filter
->result
, JSTRACE_XML
, "result");
7536 JS_CALL_TRACER(trc
, filter
->kid
, JSTRACE_XML
, "kid");
7539 * We do not need to trace the cursor as that would be done when
7540 * tracing the filter->list.
7545 xmlfilter_finalize(JSContext
*cx
, JSObject
*obj
)
7547 JSXMLFilter
*filter
= (JSXMLFilter
*) obj
->getPrivate();
7551 cx
->destroy(filter
);
7554 Class js_XMLFilterClass
= {
7556 JSCLASS_HAS_PRIVATE
| JSCLASS_IS_ANONYMOUS
| JSCLASS_MARK_IS_TRACE
,
7557 PropertyStub
, /* addProperty */
7558 PropertyStub
, /* delProperty */
7559 PropertyStub
, /* getProperty */
7560 PropertyStub
, /* setProperty */
7565 NULL
, /* reserved0 */
7566 NULL
, /* checkAccess */
7568 NULL
, /* construct */
7569 NULL
, /* xdrObject */
7570 NULL
, /* hasInstance */
7571 JS_CLASS_TRACE(xmlfilter_trace
)
7575 js_StepXMLListFilter(JSContext
*cx
, JSBool initialized
)
7578 JSObject
*obj
, *filterobj
, *resobj
, *kidobj
;
7580 JSXMLFilter
*filter
;
7583 sp
= Jsvalify(cx
->regs
->sp
);
7586 * We haven't iterated yet, so initialize the filter based on the
7587 * value stored in sp[-2].
7589 if (!VALUE_IS_XML(sp
[-2])) {
7590 js_ReportValueError(cx
, JSMSG_NON_XML_FILTER
, -2, Valueify(sp
[-2]), NULL
);
7593 obj
= JSVAL_TO_OBJECT(sp
[-2]);
7594 xml
= (JSXML
*) obj
->getPrivate();
7596 if (xml
->xml_class
== JSXML_CLASS_LIST
) {
7599 obj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
7604 * Root just-created obj. sp[-2] cannot be used yet for rooting
7605 * as it may be the only root holding xml.
7607 sp
[-1] = OBJECT_TO_JSVAL(obj
);
7608 list
= (JSXML
*) obj
->getPrivate();
7609 if (!Append(cx
, list
, xml
))
7613 filterobj
= NewNonFunction
<WithProto::Given
>(cx
, &js_XMLFilterClass
, NULL
, NULL
);
7618 * Init all filter fields before setPrivate exposes it to
7619 * xmlfilter_trace or xmlfilter_finalize.
7621 filter
= cx
->create
<JSXMLFilter
>(list
, &list
->xml_kids
);
7624 filterobj
->setPrivate(filter
);
7626 /* Store filterobj to use in the later iterations. */
7627 sp
[-2] = OBJECT_TO_JSVAL(filterobj
);
7629 resobj
= js_NewXMLObject(cx
, JSXML_CLASS_LIST
);
7633 /* This also roots resobj. */
7634 filter
->result
= (JSXML
*) resobj
->getPrivate();
7636 /* We have iterated at least once. */
7637 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp
[-2]));
7638 JS_ASSERT(JSVAL_TO_OBJECT(sp
[-2])->getClass() == &js_XMLFilterClass
);
7639 filter
= (JSXMLFilter
*) JSVAL_TO_OBJECT(sp
[-2])->getPrivate();
7640 JS_ASSERT(filter
->kid
);
7642 /* Check if the filter expression wants to append the element. */
7643 if (js_ValueToBoolean(Valueify(sp
[-1])) &&
7644 !Append(cx
, filter
->result
, filter
->kid
)) {
7649 /* Do the iteration. */
7650 filter
->kid
= (JSXML
*) filter
->cursor
.getNext();
7653 * Do not defer finishing the cursor until the next GC cycle to avoid
7654 * accumulation of dead cursors associated with filter->list.
7656 filter
->cursor
.disconnect();
7657 JS_ASSERT(filter
->result
->object
);
7658 sp
[-2] = OBJECT_TO_JSVAL(filter
->result
->object
);
7661 kidobj
= js_GetXMLObject(cx
, filter
->kid
);
7666 /* Null as kidobj at sp[-1] signals filter termination. */
7667 sp
[-1] = OBJECT_TO_JSVAL(kidobj
);
7672 js_ValueToXMLObject(JSContext
*cx
, const Value
&v
)
7674 return ToXML(cx
, Jsvalify(v
));
7678 js_ValueToXMLListObject(JSContext
*cx
, const Value
&v
)
7680 return ToXMLList(cx
, Jsvalify(v
));
7684 js_NewXMLSpecialObject(JSContext
*cx
, JSXMLClass xml_class
, JSString
*name
,
7692 if (!GetXMLSettingFlags(cx
, &flags
))
7695 if ((xml_class
== JSXML_CLASS_COMMENT
&&
7696 (flags
& XSF_IGNORE_COMMENTS
)) ||
7697 (xml_class
== JSXML_CLASS_PROCESSING_INSTRUCTION
&&
7698 (flags
& XSF_IGNORE_PROCESSING_INSTRUCTIONS
))) {
7699 return js_NewXMLObject(cx
, JSXML_CLASS_TEXT
);
7702 obj
= js_NewXMLObject(cx
, xml_class
);
7705 xml
= (JSXML
*) obj
->getPrivate();
7707 JSLinearString
*linearName
= name
->ensureLinear(cx
);
7710 qn
= NewXMLQName(cx
, cx
->runtime
->emptyString
, NULL
, linearName
);
7715 xml
->xml_value
= value
;
7720 js_MakeXMLCDATAString(JSContext
*cx
, JSString
*str
)
7722 StringBuffer
sb(cx
);
7723 return MakeXMLCDATAString(cx
, sb
, str
);
7727 js_MakeXMLCommentString(JSContext
*cx
, JSString
*str
)
7729 StringBuffer
sb(cx
);
7730 return MakeXMLCommentString(cx
, sb
, str
);
7734 js_MakeXMLPIString(JSContext
*cx
, JSString
*name
, JSString
*str
)
7736 StringBuffer
sb(cx
);
7737 return MakeXMLPIString(cx
, sb
, name
, str
);
7740 #endif /* JS_HAS_XML_SUPPORT */