Back to index

lightning-sunbird  0.9+nobinonly
jsxml.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=4 sw=4 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is SpiderMonkey E4X code, released August, 2004.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "jsstddef.h"
00041 #include "jsconfig.h"
00042 
00043 #if JS_HAS_XML_SUPPORT
00044 
00045 #include <math.h>
00046 #include <stdlib.h>
00047 #include <string.h>
00048 #include "jstypes.h"
00049 #include "jsbit.h"
00050 #include "jsprf.h"
00051 #include "jsutil.h"
00052 #include "jsapi.h"
00053 #include "jsarray.h"
00054 #include "jsatom.h"
00055 #include "jsbool.h"
00056 #include "jscntxt.h"
00057 #include "jsfun.h"
00058 #include "jsgc.h"
00059 #include "jsinterp.h"
00060 #include "jslock.h"
00061 #include "jsnum.h"
00062 #include "jsobj.h"
00063 #include "jsopcode.h"
00064 #include "jsparse.h"
00065 #include "jsscan.h"
00066 #include "jsscope.h"
00067 #include "jsscript.h"
00068 #include "jsstr.h"
00069 #include "jsxml.h"
00070 
00071 #ifdef DEBUG
00072 #include <string.h>     /* for #ifdef DEBUG memset calls */
00073 #endif
00074 
00075 /*
00076  * NOTES
00077  * - in the js shell, you must use the -x command line option, or call
00078  *   options('xml') before compiling anything that uses XML literals
00079  *
00080  * TODO
00081  * - XXXbe patrol
00082  * - Fuse objects and their JSXML* private data into single GC-things
00083  * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
00084  * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
00085  * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
00086  * - JS_TypeOfValue sure could use a cleaner interface to "types"
00087  */
00088 
00089 #ifdef DEBUG_brendan
00090 #define METERING        1
00091 #endif
00092 
00093 #ifdef METERING
00094 static struct {
00095     jsrefcount  qname;
00096     jsrefcount  qnameobj;
00097     jsrefcount  liveqname;
00098     jsrefcount  liveqnameobj;
00099     jsrefcount  namespace;
00100     jsrefcount  namespaceobj;
00101     jsrefcount  livenamespace;
00102     jsrefcount  livenamespaceobj;
00103     jsrefcount  xml;
00104     jsrefcount  xmlobj;
00105     jsrefcount  livexml;
00106     jsrefcount  livexmlobj;
00107 } xml_stats;
00108 
00109 #define METER(x)        JS_ATOMIC_INCREMENT(&(x))
00110 #define UNMETER(x)      JS_ATOMIC_DECREMENT(&(x))
00111 #else
00112 #define METER(x)        /* nothing */
00113 #define UNMETER(x)      /* nothing */
00114 #endif
00115 
00116 /*
00117  * Random utilities and global functions.
00118  */
00119 const char js_isXMLName_str[]     = "isXMLName";
00120 const char js_XMLList_str[]       = "XMLList";
00121 const char js_localName_str[]     = "localName";
00122 const char js_xml_parent_str[]    = "parent";
00123 const char js_prefix_str[]        = "prefix";
00124 const char js_toXMLString_str[]   = "toXMLString";
00125 const char js_uri_str[]           = "uri";
00126 
00127 const char js_amp_entity_str[]    = "&amp;";
00128 const char js_gt_entity_str[]     = "&gt;";
00129 const char js_lt_entity_str[]     = "&lt;";
00130 const char js_quot_entity_str[]   = "&quot;";
00131 
00132 #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
00133 #define IS_STAR(str)  (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
00134 
00135 static JSBool
00136 xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00137               jsval *rval)
00138 {
00139     *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
00140     return JS_TRUE;
00141 }
00142 
00143 /*
00144  * Namespace class and library functions.
00145  */
00146 enum namespace_tinyid {
00147     NAMESPACE_PREFIX = -1,
00148     NAMESPACE_URI = -2
00149 };
00150 
00151 static JSBool
00152 namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00153 {
00154     JSXMLNamespace *ns;
00155 
00156     if (!JSVAL_IS_INT(id))
00157         return JS_TRUE;
00158 
00159     ns = (JSXMLNamespace *)
00160          JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL);
00161     if (!ns)
00162         return JS_TRUE;
00163 
00164     switch (JSVAL_TO_INT(id)) {
00165       case NAMESPACE_PREFIX:
00166         *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID;
00167         break;
00168       case NAMESPACE_URI:
00169         *vp = STRING_TO_JSVAL(ns->uri);
00170         break;
00171     }
00172     return JS_TRUE;
00173 }
00174 
00175 static void
00176 namespace_finalize(JSContext *cx, JSObject *obj)
00177 {
00178     JSXMLNamespace *ns;
00179     JSRuntime *rt;
00180 
00181     ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
00182     if (!ns)
00183         return;
00184     JS_ASSERT(ns->object == obj);
00185     ns->object = NULL;
00186     UNMETER(xml_stats.livenamespaceobj);
00187 
00188     rt = cx->runtime;
00189     if (rt->functionNamespaceObject == obj)
00190         rt->functionNamespaceObject = NULL;
00191 }
00192 
00193 static void
00194 namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len)
00195 {
00196     uint32 i;
00197     JSXMLNamespace *ns;
00198 
00199     for (i = 0; i < len; i++) {
00200         ns = vec[i];
00201         {
00202 #ifdef GC_MARK_DEBUG
00203             char buf[100];
00204 
00205             JS_snprintf(buf, sizeof buf, "%s=%s",
00206                         ns->prefix ? JS_GetStringBytes(ns->prefix) : "",
00207                         JS_GetStringBytes(ns->uri));
00208 #endif
00209             GC_MARK(cx, ns, buf);
00210         }
00211     }
00212 }
00213 
00214 static uint32
00215 namespace_mark(JSContext *cx, JSObject *obj, void *arg)
00216 {
00217     JSXMLNamespace *ns;
00218 
00219     ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
00220     GC_MARK(cx, ns, "private");
00221     return 0;
00222 }
00223 
00224 static JSBool
00225 namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
00226 {
00227     JSXMLNamespace *ns, *ns2;
00228     JSObject *obj2;
00229 
00230     ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj);
00231     JS_ASSERT(JSVAL_IS_OBJECT(v));
00232     obj2 = JSVAL_TO_OBJECT(v);
00233     if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) {
00234         *bp = JS_FALSE;
00235     } else {
00236         ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2);
00237         *bp = js_EqualStrings(ns->uri, ns2->uri);
00238     }
00239     return JS_TRUE;
00240 }
00241 
00242 JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
00243   { "Namespace",
00244     JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
00245     JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
00246     JS_PropertyStub,   JS_PropertyStub,   namespace_getProperty, NULL,
00247     JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    namespace_finalize,
00248     NULL,              NULL,              NULL,              NULL,
00249     NULL,              NULL,              namespace_mark,    NULL },
00250     namespace_equality,NULL,              NULL,              NULL,
00251     NULL,              NULL,              NULL,              NULL
00252 };
00253 
00254 #define NAMESPACE_ATTRS                                                       \
00255     (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
00256 
00257 static JSPropertySpec namespace_props[] = {
00258     {js_prefix_str,    NAMESPACE_PREFIX,  NAMESPACE_ATTRS,   0, 0},
00259     {js_uri_str,       NAMESPACE_URI,     NAMESPACE_ATTRS,   0, 0},
00260     {0,0,0,0,0}
00261 };
00262 
00263 static JSBool
00264 namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00265                    jsval *rval)
00266 {
00267     JSXMLNamespace *ns;
00268 
00269     ns = (JSXMLNamespace *)
00270          JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv);
00271     if (!ns)
00272         return JS_FALSE;
00273 
00274     *rval = STRING_TO_JSVAL(ns->uri);
00275     return JS_TRUE;
00276 }
00277 
00278 static JSFunctionSpec namespace_methods[] = {
00279     {js_toString_str,  namespace_toString,        0,0,0},
00280     {0,0,0,0,0}
00281 };
00282 
00283 JSXMLNamespace *
00284 js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri,
00285                    JSBool declared)
00286 {
00287     JSXMLNamespace *ns;
00288 
00289     ns = (JSXMLNamespace *)
00290          js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace));
00291     if (!ns)
00292         return NULL;
00293     ns->object = NULL;
00294     ns->prefix = prefix;
00295     ns->uri = uri;
00296     ns->declared = declared;
00297     METER(xml_stats.namespace);
00298     METER(xml_stats.livenamespace);
00299     return ns;
00300 }
00301 
00302 void
00303 js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
00304 {
00305     GC_MARK(cx, ns->object, "object");
00306     GC_MARK(cx, ns->prefix, "prefix");
00307     GC_MARK(cx, ns->uri, "uri");
00308 }
00309 
00310 void
00311 js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns)
00312 {
00313     UNMETER(xml_stats.livenamespace);
00314 }
00315 
00316 JSObject *
00317 js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri,
00318                          JSBool declared)
00319 {
00320     JSXMLNamespace *ns;
00321     JSTempValueRooter tvr;
00322     JSObject *obj;
00323 
00324     ns = js_NewXMLNamespace(cx, prefix, uri, declared);
00325     if (!ns)
00326         return NULL;
00327 
00328     JS_PUSH_TEMP_ROOT_NAMESPACE(cx, ns, &tvr);
00329     obj = js_GetXMLNamespaceObject(cx, ns);
00330     JS_POP_TEMP_ROOT(cx, &tvr);
00331     return obj;
00332 }
00333 
00334 JSObject *
00335 js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns)
00336 {
00337     JSObject *obj;
00338 
00339     obj = ns->object;
00340     if (obj) {
00341         JS_ASSERT(JS_GetPrivate(cx, obj) == ns);
00342         return obj;
00343     }
00344     obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
00345     if (!obj || !JS_SetPrivate(cx, obj, ns)) {
00346         cx->weakRoots.newborn[GCX_OBJECT] = NULL;
00347         return NULL;
00348     }
00349     ns->object = obj;
00350     METER(xml_stats.namespaceobj);
00351     METER(xml_stats.livenamespaceobj);
00352     return obj;
00353 }
00354 
00355 /*
00356  * QName class and library functions.
00357  */
00358 enum qname_tinyid {
00359     QNAME_URI = -1,
00360     QNAME_LOCALNAME = -2
00361 };
00362 
00363 static JSBool
00364 qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00365 {
00366     JSXMLQName *qn;
00367 
00368     if (!JSVAL_IS_INT(id))
00369         return JS_TRUE;
00370 
00371     qn = (JSXMLQName *)
00372          JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL);
00373     if (!qn)
00374         return JS_TRUE;
00375 
00376     switch (JSVAL_TO_INT(id)) {
00377       case QNAME_URI:
00378         *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL;
00379         break;
00380       case QNAME_LOCALNAME:
00381         *vp = STRING_TO_JSVAL(qn->localName);
00382         break;
00383     }
00384     return JS_TRUE;
00385 }
00386 
00387 static void
00388 qname_finalize(JSContext *cx, JSObject *obj)
00389 {
00390     JSXMLQName *qn;
00391 
00392     qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
00393     if (!qn)
00394         return;
00395     JS_ASSERT(qn->object == obj);
00396     qn->object = NULL;
00397     UNMETER(xml_stats.liveqnameobj);
00398 }
00399 
00400 static void
00401 anyname_finalize(JSContext* cx, JSObject* obj)
00402 {
00403     JSRuntime *rt;
00404 
00405     /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
00406     rt = cx->runtime;
00407     if (rt->anynameObject == obj)
00408         rt->anynameObject = NULL;
00409 
00410     qname_finalize(cx, obj);
00411 }
00412 
00413 static uint32
00414 qname_mark(JSContext *cx, JSObject *obj, void *arg)
00415 {
00416     JSXMLQName *qn;
00417 
00418     qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
00419     GC_MARK(cx, qn, "private");
00420     return 0;
00421 }
00422 
00423 static JSBool
00424 qname_identity(JSXMLQName *qna, JSXMLQName *qnb)
00425 {
00426     if (!qna->uri ^ !qnb->uri)
00427         return JS_FALSE;
00428     if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri))
00429         return JS_FALSE;
00430     return js_EqualStrings(qna->localName, qnb->localName);
00431 }
00432 
00433 static JSBool
00434 qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
00435 {
00436     JSXMLQName *qn, *qn2;
00437     JSObject *obj2;
00438 
00439     qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
00440     JS_ASSERT(JSVAL_IS_OBJECT(v));
00441     obj2 = JSVAL_TO_OBJECT(v);
00442     if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) {
00443         *bp = JS_FALSE;
00444     } else {
00445         qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2);
00446         *bp = qname_identity(qn, qn2);
00447     }
00448     return JS_TRUE;
00449 }
00450 
00451 JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
00452   { "QName",
00453     JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
00454     JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
00455     JS_PropertyStub,   JS_PropertyStub,   qname_getProperty, NULL,
00456     JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    qname_finalize,
00457     NULL,              NULL,              NULL,              NULL,
00458     NULL,              NULL,              qname_mark,        NULL },
00459     qname_equality,    NULL,              NULL,              NULL,
00460     NULL,              NULL,              NULL,              NULL
00461 };
00462 
00463 /*
00464  * Classes for the ECMA-357-internal types AttributeName and AnyName, which
00465  * are like QName, except that they have no property getters.  They share the
00466  * qname_toString method, and therefore are exposed as constructable objects
00467  * in this implementation.
00468  */
00469 JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
00470     js_AttributeName_str,
00471     JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE |
00472     JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
00473     JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
00474     JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    qname_finalize,
00475     NULL,              NULL,              NULL,              NULL,
00476     NULL,              NULL,              qname_mark,        NULL
00477 };
00478 
00479 JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
00480     js_AnyName_str,
00481     JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE |
00482     JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
00483     JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
00484     JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    anyname_finalize,
00485     NULL,              NULL,              NULL,              NULL,
00486     NULL,              NULL,              qname_mark,        NULL
00487 };
00488 
00489 #define QNAME_ATTRS                                                           \
00490     (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
00491 
00492 static JSPropertySpec qname_props[] = {
00493     {js_uri_str,       QNAME_URI,         QNAME_ATTRS,       0, 0},
00494     {js_localName_str, QNAME_LOCALNAME,   QNAME_ATTRS,       0, 0},
00495     {0,0,0,0,0}
00496 };
00497 
00498 static JSBool
00499 qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00500                jsval *rval)
00501 {
00502     JSClass *clasp;
00503     JSXMLQName *qn;
00504     JSString *str, *qualstr;
00505     size_t length;
00506     jschar *chars;
00507 
00508     clasp = OBJ_GET_CLASS(cx, obj);
00509     if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) {
00510         qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
00511     } else {
00512         qn = (JSXMLQName *)
00513              JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv);
00514         if (!qn)
00515             return JS_FALSE;
00516     }
00517 
00518     if (!qn->uri) {
00519         /* No uri means wildcard qualifier. */
00520         str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
00521     } else if (IS_EMPTY(qn->uri)) {
00522         /* Empty string for uri means localName is in no namespace. */
00523         str = cx->runtime->emptyString;
00524     } else {
00525         qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
00526         str = js_ConcatStrings(cx, qn->uri, qualstr);
00527         if (!str)
00528             return JS_FALSE;
00529     }
00530     str = js_ConcatStrings(cx, str, qn->localName);
00531     if (!str)
00532         return JS_FALSE;
00533 
00534     if (str && clasp == &js_AttributeNameClass) {
00535         length = JSSTRING_LENGTH(str);
00536         chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
00537         if (!chars)
00538             return JS_FALSE;
00539         *chars = '@';
00540         js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
00541         chars[++length] = 0;
00542         str = js_NewString(cx, chars, length, 0);
00543         if (!str) {
00544             JS_free(cx, chars);
00545             return JS_FALSE;
00546         }
00547     }
00548 
00549     *rval = STRING_TO_JSVAL(str);
00550     return JS_TRUE;
00551 }
00552 
00553 static JSFunctionSpec qname_methods[] = {
00554     {js_toString_str,  qname_toString,    0,0,0},
00555     {0,0,0,0,0}
00556 };
00557 
00558 JSXMLQName *
00559 js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix,
00560                JSString *localName)
00561 {
00562     JSXMLQName *qn;
00563 
00564     qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName));
00565     if (!qn)
00566         return NULL;
00567     qn->object = NULL;
00568     qn->uri = uri;
00569     qn->prefix = prefix;
00570     qn->localName = localName;
00571     METER(xml_stats.qname);
00572     METER(xml_stats.liveqname);
00573     return qn;
00574 }
00575 
00576 void
00577 js_MarkXMLQName(JSContext *cx, JSXMLQName *qn)
00578 {
00579     GC_MARK(cx, qn->object, "object");
00580     GC_MARK(cx, qn->uri, "uri");
00581     GC_MARK(cx, qn->prefix, "prefix");
00582     GC_MARK(cx, qn->localName, "localName");
00583 }
00584 
00585 void
00586 js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn)
00587 {
00588     UNMETER(xml_stats.liveqname);
00589 }
00590 
00591 JSObject *
00592 js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix,
00593                      JSString *localName)
00594 {
00595     JSXMLQName *qn;
00596     JSTempValueRooter tvr;
00597     JSObject *obj;
00598 
00599     qn = js_NewXMLQName(cx, uri, prefix, localName);
00600     if (!qn)
00601         return NULL;
00602     JS_PUSH_TEMP_ROOT_QNAME(cx, qn, &tvr);
00603     obj = js_GetXMLQNameObject(cx, qn);
00604     JS_POP_TEMP_ROOT(cx, &tvr);
00605     return obj;
00606 }
00607 
00608 JSObject *
00609 js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn)
00610 {
00611     JSObject *obj;
00612 
00613     obj = qn->object;
00614     if (obj) {
00615         JS_ASSERT(JS_GetPrivate(cx, obj) == qn);
00616         return obj;
00617     }
00618     obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL);
00619     if (!obj || !JS_SetPrivate(cx, obj, qn)) {
00620         cx->weakRoots.newborn[GCX_OBJECT] = NULL;
00621         return NULL;
00622     }
00623     qn->object = obj;
00624     METER(xml_stats.qnameobj);
00625     METER(xml_stats.liveqnameobj);
00626     return obj;
00627 }
00628 
00629 JSObject *
00630 js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn)
00631 {
00632     JSObject *obj;
00633 
00634     obj = qn->object;
00635     if (obj) {
00636         if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass)
00637             return obj;
00638         qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
00639         if (!qn)
00640             return NULL;
00641     }
00642 
00643     obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL);
00644     if (!obj || !JS_SetPrivate(cx, obj, qn)) {
00645         cx->weakRoots.newborn[GCX_OBJECT] = NULL;
00646         return NULL;
00647     }
00648 
00649     qn->object = obj;
00650     METER(xml_stats.qnameobj);
00651     METER(xml_stats.liveqnameobj);
00652     return obj;
00653 }
00654 
00655 JSObject *
00656 js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
00657 {
00658     jsval argv[2];
00659 
00660     /*
00661      * ECMA-357 11.1.2,
00662      * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
00663      * production, step 2.
00664      */
00665     if (!JSVAL_IS_PRIMITIVE(nsval) &&
00666         OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
00667         nsval = JSVAL_NULL;
00668     }
00669 
00670     argv[0] = nsval;
00671     argv[1] = lnval;
00672     return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
00673 }
00674 
00675 static JSBool
00676 IsXMLName(const jschar *cp, size_t n)
00677 {
00678     JSBool rv;
00679     jschar c;
00680 
00681     rv = JS_FALSE;
00682     if (n != 0 && JS_ISXMLNSSTART(*cp)) {
00683         while (--n != 0) {
00684             c = *++cp;
00685             if (!JS_ISXMLNS(c))
00686                 return rv;
00687         }
00688         rv = JS_TRUE;
00689     }
00690     return rv;
00691 }
00692 
00693 JSBool
00694 js_IsXMLName(JSContext *cx, jsval v)
00695 {
00696     JSClass *clasp;
00697     JSXMLQName *qn;
00698     JSString *name;
00699     JSErrorReporter older;
00700 
00701     /*
00702      * Inline specialization of the QName constructor called with v passed as
00703      * the only argument, to compute the localName for the constructed qname,
00704      * without actually allocating the object or computing its uri and prefix.
00705      * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
00706      */
00707     if (!JSVAL_IS_PRIMITIVE(v) &&
00708         (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)),
00709          clasp == &js_QNameClass.base ||
00710          clasp == &js_AttributeNameClass ||
00711          clasp == &js_AnyNameClass)) {
00712         qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
00713         name = qn->localName;
00714     } else {
00715         older = JS_SetErrorReporter(cx, NULL);
00716         name = js_ValueToString(cx, v);
00717         JS_SetErrorReporter(cx, older);
00718         if (!name) {
00719             JS_ClearPendingException(cx);
00720             return JS_FALSE;
00721         }
00722     }
00723 
00724     return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
00725 }
00726 
00727 static JSBool
00728 NamespaceHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00729                 jsval *rval)
00730 {
00731     jsval urival, prefixval;
00732     JSObject *uriobj;
00733     JSBool isNamespace, isQName;
00734     JSClass *clasp;
00735     JSString *empty, *prefix;
00736     JSXMLNamespace *ns, *ns2;
00737     JSXMLQName *qn;
00738 
00739     urival = argv[argc > 1];
00740     isNamespace = isQName = JS_FALSE;
00741     if (!JSVAL_IS_PRIMITIVE(urival)) {
00742         uriobj = JSVAL_TO_OBJECT(urival);
00743         clasp = OBJ_GET_CLASS(cx, uriobj);
00744         isNamespace = (clasp == &js_NamespaceClass.base);
00745         isQName = (clasp == &js_QNameClass.base);
00746     }
00747 #ifdef __GNUC__         /* suppress bogus gcc warnings */
00748     else uriobj = NULL;
00749 #endif
00750 
00751     if (!obj) {
00752         /* Namespace called as function. */
00753         if (argc == 1 && isNamespace) {
00754             /* Namespace called with one Namespace argument is identity. */
00755             *rval = urival;
00756             return JS_TRUE;
00757         }
00758 
00759         /* Create and return a new QName object exactly as if constructed. */
00760         obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
00761         if (!obj)
00762             return JS_FALSE;
00763         *rval = OBJECT_TO_JSVAL(obj);
00764     }
00765     METER(xml_stats.namespaceobj);
00766     METER(xml_stats.livenamespaceobj);
00767 
00768     /*
00769      * Create and connect private data to rooted obj early, so we don't have
00770      * to worry about rooting string newborns hanging off of the private data
00771      * further below.
00772      */
00773     empty = cx->runtime->emptyString;
00774     ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE);
00775     if (!ns)
00776         return JS_FALSE;
00777     if (!JS_SetPrivate(cx, obj, ns))
00778         return JS_FALSE;
00779     ns->object = obj;
00780 
00781     if (argc == 1) {
00782         if (isNamespace) {
00783             ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj);
00784             ns->uri = ns2->uri;
00785             ns->prefix = ns2->prefix;
00786         } else if (isQName &&
00787                    (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
00788             ns->uri = qn->uri;
00789             ns->prefix = qn->prefix;
00790         } else {
00791             ns->uri = js_ValueToString(cx, urival);
00792             if (!ns->uri)
00793                 return JS_FALSE;
00794 
00795             /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
00796             if (!IS_EMPTY(ns->uri))
00797                 ns->prefix = NULL;
00798         }
00799     } else if (argc == 2) {
00800         if (isQName &&
00801             (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) {
00802             ns->uri = qn->uri;
00803         } else {
00804             ns->uri = js_ValueToString(cx, urival);
00805             if (!ns->uri)
00806                 return JS_FALSE;
00807         }
00808 
00809         prefixval = argv[0];
00810         if (IS_EMPTY(ns->uri)) {
00811             if (!JSVAL_IS_VOID(prefixval)) {
00812                 prefix = js_ValueToString(cx, prefixval);
00813                 if (!prefix)
00814                     return JS_FALSE;
00815                 if (!IS_EMPTY(prefix)) {
00816                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00817                                          JSMSG_BAD_XML_NAMESPACE,
00818                                          js_ValueToPrintableString(cx,
00819                                              STRING_TO_JSVAL(prefix)));
00820                     return JS_FALSE;
00821                 }
00822             }
00823         } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
00824             /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */
00825             ns->prefix = NULL;
00826         } else {
00827             prefix = js_ValueToString(cx, prefixval);
00828             if (!prefix)
00829                 return JS_FALSE;
00830             ns->prefix = prefix;
00831         }
00832     }
00833 
00834     return JS_TRUE;
00835 }
00836 
00837 static JSBool
00838 Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00839 {
00840     return NamespaceHelper(cx,
00841                            (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
00842                            argc, argv, rval);
00843 }
00844 
00845 static JSBool
00846 QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, uintN argc,
00847             jsval *argv, jsval *rval)
00848 {
00849     jsval nameval, nsval;
00850     JSBool isQName, isNamespace;
00851     JSXMLQName *qn;
00852     JSString *uri, *prefix, *name;
00853     JSObject *nsobj;
00854     JSXMLNamespace *ns;
00855 
00856     JS_ASSERT(clasp == &js_QNameClass.base ||
00857               clasp == &js_AttributeNameClass);
00858     nameval = argv[argc > 1];
00859     isQName =
00860         !JSVAL_IS_PRIMITIVE(nameval) &&
00861         OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
00862 
00863     if (!obj) {
00864         /* QName called as function. */
00865         if (argc == 1 && isQName) {
00866             /* QName called with one QName argument is identity. */
00867             *rval = nameval;
00868             return JS_TRUE;
00869         }
00870 
00871         /*
00872          * Create and return a new QName or AttributeName object exactly as if
00873          * constructed.
00874          */
00875         obj = js_NewObject(cx, clasp, NULL, NULL);
00876         if (!obj)
00877             return JS_FALSE;
00878         *rval = OBJECT_TO_JSVAL(obj);
00879     }
00880     METER(xml_stats.qnameobj);
00881     METER(xml_stats.liveqnameobj);
00882 
00883     if (isQName) {
00884         /* If namespace is not specified and name is a QName, clone it. */
00885         qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval));
00886         if (argc == 1) {
00887             uri = qn->uri;
00888             prefix = qn->prefix;
00889             name = qn->localName;
00890             goto out;
00891         }
00892 
00893         /* Namespace and qname were passed -- use the qname's localName. */
00894         nameval = STRING_TO_JSVAL(qn->localName);
00895     }
00896 
00897     if (argc == 0) {
00898         name = cx->runtime->emptyString;
00899     } else {
00900         name = js_ValueToString(cx, nameval);
00901         if (!name)
00902             return JS_FALSE;
00903 
00904         /* Use argv[1] as a local root for name, even if it was not passed. */
00905         argv[1] = STRING_TO_JSVAL(name);
00906     }
00907 
00908     nsval = argv[0];
00909     if (argc == 1 || JSVAL_IS_VOID(nsval)) {
00910         if (IS_STAR(name)) {
00911             nsval = JSVAL_NULL;
00912         } else {
00913             if (!js_GetDefaultXMLNamespace(cx, &nsval))
00914                 return JS_FALSE;
00915         }
00916     }
00917 
00918     if (JSVAL_IS_NULL(nsval)) {
00919         /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
00920         uri = prefix = NULL;
00921     } else {
00922         /*
00923          * Inline specialization of the Namespace constructor called with
00924          * nsval passed as the only argument, to compute the uri and prefix
00925          * for the constructed namespace, without actually allocating the
00926          * object or computing other members.  See ECMA-357 13.3.2 6(a) and
00927          * 13.2.2.
00928          */
00929         isNamespace = isQName = JS_FALSE;
00930         if (!JSVAL_IS_PRIMITIVE(nsval)) {
00931             nsobj = JSVAL_TO_OBJECT(nsval);
00932             clasp = OBJ_GET_CLASS(cx, nsobj);
00933             isNamespace = (clasp == &js_NamespaceClass.base);
00934             isQName = (clasp == &js_QNameClass.base);
00935         }
00936 #ifdef __GNUC__         /* suppress bogus gcc warnings */
00937         else nsobj = NULL;
00938 #endif
00939 
00940         if (isNamespace) {
00941             ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
00942             uri = ns->uri;
00943             prefix = ns->prefix;
00944         } else if (isQName &&
00945                    (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) {
00946             uri = qn->uri;
00947             prefix = qn->prefix;
00948         } else {
00949             uri = js_ValueToString(cx, nsval);
00950             if (!uri)
00951                 return JS_FALSE;
00952             argv[0] = STRING_TO_JSVAL(uri);     /* local root */
00953 
00954             /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
00955             prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
00956         }
00957     }
00958 
00959 out:
00960     qn = js_NewXMLQName(cx, uri, prefix, name);
00961     if (!qn)
00962         return JS_FALSE;
00963     if (!JS_SetPrivate(cx, obj, qn))
00964         return JS_FALSE;
00965     qn->object = obj;
00966     return JS_TRUE;
00967 }
00968 
00969 static JSBool
00970 QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00971 {
00972     return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
00973                        &js_QNameClass.base, argc, argv, rval);
00974 }
00975 
00976 static JSBool
00977 AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00978               jsval *rval)
00979 {
00980     return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
00981                        &js_AttributeNameClass, argc, argv, rval);
00982 }
00983 
00984 /*
00985  * XMLArray library functions.
00986  */
00987 static JSBool
00988 namespace_identity(const void *a, const void *b)
00989 {
00990     const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
00991     const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
00992 
00993     if (nsa->prefix && nsb->prefix) {
00994         if (!js_EqualStrings(nsa->prefix, nsb->prefix))
00995             return JS_FALSE;
00996     } else {
00997         if (nsa->prefix || nsb->prefix)
00998             return JS_FALSE;
00999     }
01000     return js_EqualStrings(nsa->uri, nsb->uri);
01001 }
01002 
01003 static JSBool
01004 attr_identity(const void *a, const void *b)
01005 {
01006     const JSXML *xmla = (const JSXML *) a;
01007     const JSXML *xmlb = (const JSXML *) b;
01008 
01009     return qname_identity(xmla->name, xmlb->name);
01010 }
01011 
01012 static void
01013 XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
01014 {
01015     JSXMLArrayCursor *next;
01016 
01017     cursor->array = array;
01018     cursor->index = 0;
01019     next = cursor->next = array->cursors;
01020     if (next)
01021         next->prevp = &cursor->next;
01022     cursor->prevp = &array->cursors;
01023     array->cursors = cursor;
01024     cursor->root = NULL;
01025 }
01026 
01027 static void
01028 XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
01029 {
01030     JSXMLArrayCursor *next;
01031 
01032     if (!cursor->array)
01033         return;
01034     next = cursor->next;
01035     if (next)
01036         next->prevp = cursor->prevp;
01037     *cursor->prevp = next;
01038     cursor->array = NULL;
01039 }
01040 
01041 static void *
01042 XMLArrayCursorNext(JSXMLArrayCursor *cursor)
01043 {
01044     JSXMLArray *array;
01045 
01046     array = cursor->array;
01047     if (!array || cursor->index >= array->length)
01048         return NULL;
01049     return cursor->root = array->vector[cursor->index++];
01050 }
01051 
01052 static void *
01053 XMLArrayCursorItem(JSXMLArrayCursor *cursor)
01054 {
01055     JSXMLArray *array;
01056 
01057     array = cursor->array;
01058     if (!array || cursor->index >= array->length)
01059         return NULL;
01060     return cursor->root = array->vector[cursor->index];
01061 }
01062 
01063 static void
01064 XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor)
01065 {
01066     while (cursor) {
01067         GC_MARK(cx, cursor->root, "cursor->root");
01068         cursor = cursor->next;
01069     }
01070 }
01071 
01072 /* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */
01073 static JSBool
01074 XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
01075 {
01076     void **vector;
01077 
01078     if (capacity == 0) {
01079         /* We could let realloc(p, 0) free this, but purify gets confused. */
01080         if (array->vector)
01081             free(array->vector);
01082         vector = NULL;
01083     } else {
01084         if ((size_t)capacity > ~(size_t)0 / sizeof(void *) ||
01085             !(vector = (void **)
01086                        realloc(array->vector, capacity * sizeof(void *)))) {
01087             if (cx)
01088                 JS_ReportOutOfMemory(cx);
01089             return JS_FALSE;
01090         }
01091     }
01092     array->capacity = JSXML_PRESET_CAPACITY | capacity;
01093     array->vector = vector;
01094     return JS_TRUE;
01095 }
01096 
01097 static void
01098 XMLArrayTrim(JSXMLArray *array)
01099 {
01100     if (array->capacity & JSXML_PRESET_CAPACITY)
01101         return;
01102     if (array->length < array->capacity)
01103         XMLArraySetCapacity(NULL, array, array->length);
01104 }
01105 
01106 static JSBool
01107 XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
01108 {
01109     array->length = array->capacity = 0;
01110     array->vector = NULL;
01111     array->cursors = NULL;
01112     return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
01113 }
01114 
01115 static void
01116 XMLArrayFinish(JSContext *cx, JSXMLArray *array)
01117 {
01118     JSXMLArrayCursor *cursor;
01119 
01120     JS_free(cx, array->vector);
01121 
01122     while ((cursor = array->cursors) != NULL)
01123         XMLArrayCursorFinish(cursor);
01124 
01125 #ifdef DEBUG
01126     memset(array, 0xd5, sizeof *array);
01127 #endif
01128 }
01129 
01130 #define XML_NOT_FOUND   ((uint32) -1)
01131 
01132 static uint32
01133 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
01134 {
01135     void **vector;
01136     uint32 i, n;
01137 
01138     /* The identity op must not reallocate array->vector. */
01139     vector = array->vector;
01140     if (identity) {
01141         for (i = 0, n = array->length; i < n; i++) {
01142             if (identity(vector[i], elt))
01143                 return i;
01144         }
01145     } else {
01146         for (i = 0, n = array->length; i < n; i++) {
01147             if (vector[i] == elt)
01148                 return i;
01149         }
01150     }
01151     return XML_NOT_FOUND;
01152 }
01153 
01154 /*
01155  * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
01156  * that, grow by LINEAR_INCREMENT.  Both must be powers of two, and threshold
01157  * should be greater than increment.
01158  */
01159 #define LINEAR_THRESHOLD        256
01160 #define LINEAR_INCREMENT        32
01161 
01162 static JSBool
01163 XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
01164 {
01165     uint32 capacity, i;
01166     int log2;
01167     void **vector;
01168 
01169     if (index >= array->length) {
01170         if (index >= JSXML_CAPACITY(array)) {
01171             /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
01172             capacity = index + 1;
01173             if (index >= LINEAR_THRESHOLD) {
01174                 capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
01175             } else {
01176                 JS_CEILING_LOG2(log2, capacity);
01177                 capacity = JS_BIT(log2);
01178             }
01179             if ((size_t)capacity > ~(size_t)0 / sizeof(void *) ||
01180                 !(vector = (void **)
01181                            realloc(array->vector, capacity * sizeof(void *)))) {
01182                 JS_ReportOutOfMemory(cx);
01183                 return JS_FALSE;
01184             }
01185             array->capacity = capacity;
01186             array->vector = vector;
01187             for (i = array->length; i < index; i++)
01188                 vector[i] = NULL;
01189         }
01190         array->length = index + 1;
01191     }
01192 
01193     array->vector[index] = elt;
01194     return JS_TRUE;
01195 }
01196 
01197 static JSBool
01198 XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
01199 {
01200     uint32 j;
01201     JSXMLArrayCursor *cursor;
01202 
01203     j = array->length;
01204     JS_ASSERT(i <= j);
01205     if (!XMLArraySetCapacity(cx, array, j + n))
01206         return JS_FALSE;
01207 
01208     array->length = j + n;
01209     JS_ASSERT(n != (uint32)-1);
01210     while (j != i) {
01211         --j;
01212         array->vector[j + n] = array->vector[j];
01213     }
01214 
01215     for (cursor = array->cursors; cursor; cursor = cursor->next) {
01216         if (cursor->index > i)
01217             cursor->index += n;
01218     }
01219     return JS_TRUE;
01220 }
01221 
01222 static void *
01223 XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
01224 {
01225     uint32 length;
01226     void **vector, *elt;
01227     JSXMLArrayCursor *cursor;
01228 
01229     length = array->length;
01230     if (index >= length)
01231         return NULL;
01232 
01233     vector = array->vector;
01234     elt = vector[index];
01235     if (compress) {
01236         while (++index < length)
01237             vector[index-1] = vector[index];
01238         array->length = length - 1;
01239         array->capacity = JSXML_CAPACITY(array);
01240     } else {
01241         vector[index] = NULL;
01242     }
01243 
01244     for (cursor = array->cursors; cursor; cursor = cursor->next) {
01245         if (cursor->index > index)
01246             --cursor->index;
01247     }
01248     return elt;
01249 }
01250 
01251 static void
01252 XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
01253 {
01254     void **vector;
01255 
01256     JS_ASSERT(!array->cursors);
01257     if (length >= array->length)
01258         return;
01259 
01260     if (length == 0) {
01261         if (array->vector)
01262             free(array->vector);
01263         vector = NULL;
01264     } else {
01265         vector = realloc(array->vector, length * sizeof(void *));
01266         if (!vector)
01267             return;
01268     }
01269 
01270     if (array->length > length)
01271         array->length = length;
01272     array->capacity = length;
01273     array->vector = vector;
01274 }
01275 
01276 #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
01277 #define XMLARRAY_HAS_MEMBER(a,e,f)  (XMLArrayFindMember(a, (void *)(e), f) != \
01278                                      XML_NOT_FOUND)
01279 #define XMLARRAY_MEMBER(a,i,t)      (((i) < (a)->length)                      \
01280                                      ? (t *) (a)->vector[i]                   \
01281                                      : NULL)
01282 #define XMLARRAY_SET_MEMBER(a,i,e)  JS_BEGIN_MACRO                            \
01283                                         if ((a)->length <= (i))               \
01284                                             (a)->length = (i) + 1;            \
01285                                         ((a)->vector[i] = (void *)(e));       \
01286                                     JS_END_MACRO
01287 #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
01288 #define XMLARRAY_INSERT(x,a,i,n)    XMLArrayInsert(x, a, i, n)
01289 #define XMLARRAY_APPEND(x,a,e)      XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
01290 #define XMLARRAY_DELETE(x,a,i,c,t)  ((t *) XMLArrayDelete(x, a, i, c))
01291 #define XMLARRAY_TRUNCATE(x,a,n)    XMLArrayTruncate(x, a, n)
01292 
01293 /*
01294  * Define XML setting property strings and constants early, so everyone can
01295  * use the same names and their magic numbers (tinyids, flags).
01296  */
01297 static const char js_ignoreComments_str[]   = "ignoreComments";
01298 static const char js_ignoreProcessingInstructions_str[]
01299                                             = "ignoreProcessingInstructions";
01300 static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
01301 static const char js_prettyPrinting_str[]   = "prettyPrinting";
01302 static const char js_prettyIndent_str[]     = "prettyIndent";
01303 
01304 /*
01305  * NB: These XML static property tinyids must
01306  * (a) not collide with the generic negative tinyids at the top of jsfun.c;
01307  * (b) index their corresponding xml_static_props array elements.
01308  * Don't change 'em!
01309  */
01310 enum xml_static_tinyid {
01311     XML_IGNORE_COMMENTS,
01312     XML_IGNORE_PROCESSING_INSTRUCTIONS,
01313     XML_IGNORE_WHITESPACE,
01314     XML_PRETTY_PRINTING,
01315     XML_PRETTY_INDENT
01316 };
01317 
01318 static JSBool
01319 xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01320 {
01321     return JS_TRUE;
01322 }
01323 
01324 static JSBool
01325 xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01326 {
01327     JSBool b;
01328     uint8 flag;
01329 
01330     JS_ASSERT(JSVAL_IS_INT(id));
01331     if (!js_ValueToBoolean(cx, *vp, &b))
01332         return JS_FALSE;
01333 
01334     flag = JS_BIT(JSVAL_TO_INT(id));
01335     if (b)
01336         cx->xmlSettingFlags |= flag;
01337     else
01338         cx->xmlSettingFlags &= ~flag;
01339     return JS_TRUE;
01340 }
01341 
01342 static JSPropertySpec xml_static_props[] = {
01343     {js_ignoreComments_str,     XML_IGNORE_COMMENTS,   JSPROP_PERMANENT,
01344                                 xml_setting_getter, xml_setting_setter},
01345     {js_ignoreProcessingInstructions_str,
01346                    XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
01347                                 xml_setting_getter, xml_setting_setter},
01348     {js_ignoreWhitespace_str,   XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
01349                                 xml_setting_getter, xml_setting_setter},
01350     {js_prettyPrinting_str,     XML_PRETTY_PRINTING,   JSPROP_PERMANENT,
01351                                 xml_setting_getter, xml_setting_setter},
01352     {js_prettyIndent_str,       XML_PRETTY_INDENT,     JSPROP_PERMANENT,
01353                                 xml_setting_getter, NULL},
01354     {0,0,0,0,0}
01355 };
01356 
01357 /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
01358 #define XSF_IGNORE_COMMENTS     JS_BIT(XML_IGNORE_COMMENTS)
01359 #define XSF_IGNORE_PROCESSING_INSTRUCTIONS                                    \
01360                                 JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
01361 #define XSF_IGNORE_WHITESPACE   JS_BIT(XML_IGNORE_WHITESPACE)
01362 #define XSF_PRETTY_PRINTING     JS_BIT(XML_PRETTY_PRINTING)
01363 #define XSF_CACHE_VALID         JS_BIT(XML_PRETTY_INDENT)
01364 
01365 /*
01366  * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
01367  * This flag means a couple of things:
01368  *
01369  * - The top JSXML created for a parse tree must have an object owning it.
01370  *
01371  * - That the default namespace normally inherited from the temporary
01372  *   <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
01373  *   string must, in the case of a precompiled XML object tree, inherit via
01374  *   ad-hoc code in ParseNodeToXML.
01375  *
01376  * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
01377  */
01378 #define XSF_PRECOMPILED_ROOT    (XSF_CACHE_VALID << 1)
01379 
01380 /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
01381 #define IS_XML(str)                                                           \
01382     (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
01383 
01384 #define IS_XMLNS(str)                                                         \
01385     (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
01386 
01387 #define IS_XML_CHARS(chars)                                                   \
01388     (JS_TOLOWER((chars)[0]) == 'x' &&                                         \
01389      JS_TOLOWER((chars)[1]) == 'm' &&                                         \
01390      JS_TOLOWER((chars)[2]) == 'l')
01391 
01392 #define HAS_NS_AFTER_XML(chars)                                               \
01393     (JS_TOLOWER((chars)[3]) == 'n' &&                                         \
01394      JS_TOLOWER((chars)[4]) == 's')
01395 
01396 #define IS_XMLNS_CHARS(chars)                                                 \
01397     (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
01398 
01399 #define STARTS_WITH_XML(chars,length)                                         \
01400     (length >= 3 && IS_XML_CHARS(chars))
01401 
01402 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
01403 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
01404 
01405 static JSXMLQName *
01406 ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
01407                  JSBool isAttributeName)
01408 {
01409     JSString *str, *uri, *prefix, *localName;
01410     size_t length, offset;
01411     const jschar *start, *limit, *colon;
01412     uint32 n;
01413     JSXMLNamespace *ns;
01414 
01415     JS_ASSERT(pn->pn_arity == PN_NULLARY);
01416     str = ATOM_TO_STRING(pn->pn_atom);
01417     length = JSSTRING_LENGTH(str);
01418     start = JSSTRING_CHARS(str);
01419     JS_ASSERT(length != 0 && *start != '@');
01420     JS_ASSERT(length != 1 || *start != '*');
01421 
01422     uri = cx->runtime->emptyString;
01423     limit = start + length;
01424     colon = js_strchr_limit(start, ':', limit);
01425     if (colon) {
01426         offset = PTRDIFF(colon, start, jschar);
01427         prefix = js_NewDependentString(cx, str, 0, offset, 0);
01428         if (!prefix)
01429             return NULL;
01430 
01431         if (STARTS_WITH_XML(start, offset)) {
01432             if (offset == 3) {
01433                 uri = JS_InternString(cx, xml_namespace_str);
01434                 if (!uri)
01435                     return NULL;
01436             } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
01437                 uri = JS_InternString(cx, xmlns_namespace_str);
01438                 if (!uri)
01439                     return NULL;
01440             } else {
01441                 uri = NULL;
01442             }
01443         } else {
01444             uri = NULL;
01445             n = inScopeNSes->length;
01446             while (n != 0) {
01447                 --n;
01448                 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
01449                 if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) {
01450                     uri = ns->uri;
01451                     break;
01452                 }
01453             }
01454         }
01455 
01456         if (!uri) {
01457             js_ReportCompileErrorNumber(cx, pn,
01458                                         JSREPORT_PN | JSREPORT_ERROR,
01459                                         JSMSG_BAD_XML_NAMESPACE,
01460                                         js_ValueToPrintableString(cx,
01461                                             STRING_TO_JSVAL(prefix)));
01462             return NULL;
01463         }
01464 
01465         localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0);
01466         if (!localName)
01467             return NULL;
01468     } else {
01469         if (isAttributeName) {
01470             /*
01471              * An unprefixed attribute is not in any namespace, so set prefix
01472              * as well as uri to the empty string.
01473              */
01474             prefix = uri;
01475         } else {
01476             /*
01477              * Loop from back to front looking for the closest declared default
01478              * namespace.
01479              */
01480             n = inScopeNSes->length;
01481             while (n != 0) {
01482                 --n;
01483                 ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace);
01484                 if (!ns->prefix || IS_EMPTY(ns->prefix)) {
01485                     uri = ns->uri;
01486                     break;
01487                 }
01488             }
01489             prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
01490         }
01491         localName = str;
01492     }
01493 
01494     return js_NewXMLQName(cx, uri, prefix, localName);
01495 }
01496 
01497 static JSString *
01498 ChompXMLWhitespace(JSContext *cx, JSString *str)
01499 {
01500     size_t length, newlength, offset;
01501     const jschar *cp, *start, *end;
01502     jschar c;
01503 
01504     length = JSSTRING_LENGTH(str);
01505     for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
01506         c = *cp;
01507         if (!JS_ISXMLSPACE(c))
01508             break;
01509     }
01510     while (end > cp) {
01511         c = end[-1];
01512         if (!JS_ISXMLSPACE(c))
01513             break;
01514         --end;
01515     }
01516     newlength = PTRDIFF(end, cp, jschar);
01517     if (newlength == length)
01518         return str;
01519     offset = PTRDIFF(cp, start, jschar);
01520     return js_NewDependentString(cx, str, offset, newlength, 0);
01521 }
01522 
01523 static JSXML *
01524 ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
01525                uintN flags)
01526 {
01527     JSXML *xml, *kid, *attr, *attrj;
01528     JSString *str;
01529     uint32 length, n, i, j;
01530     JSParseNode *pn2, *pn3, *head, **pnp;
01531     JSXMLNamespace *ns;
01532     JSXMLQName *qn, *attrjqn;
01533     JSXMLClass xml_class;
01534     int stackDummy;
01535 
01536     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
01537         js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
01538                                     JSMSG_OVER_RECURSED);
01539         return NULL;
01540     }
01541 
01542 #define PN2X_SKIP_CHILD ((JSXML *) 1)
01543 
01544     /*
01545      * Cases return early to avoid common code that gets an outermost xml's
01546      * object, which protects GC-things owned by xml and its descendants from
01547      * garbage collection.
01548      */
01549     xml = NULL;
01550     if (!js_EnterLocalRootScope(cx))
01551         return NULL;
01552     switch (pn->pn_type) {
01553       case TOK_XMLELEM:
01554         length = inScopeNSes->length;
01555         pn2 = pn->pn_head;
01556         xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
01557         if (!xml)
01558             goto fail;
01559 
01560         flags &= ~XSF_PRECOMPILED_ROOT;
01561         n = pn->pn_count;
01562         JS_ASSERT(n >= 2);
01563         n -= 2;
01564         if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
01565             goto fail;
01566 
01567         i = 0;
01568         while ((pn2 = pn2->pn_next) != NULL) {
01569             if (!pn2->pn_next) {
01570                 /* Don't append the end tag! */
01571                 JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
01572                 break;
01573             }
01574 
01575             if ((flags & XSF_IGNORE_WHITESPACE) &&
01576                 n > 1 && pn2->pn_type == TOK_XMLSPACE) {
01577                 --n;
01578                 continue;
01579             }
01580 
01581             kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
01582             if (kid == PN2X_SKIP_CHILD) {
01583                 --n;
01584                 continue;
01585             }
01586 
01587             if (!kid)
01588                 goto fail;
01589 
01590             /* Store kid in xml right away, to protect it from GC. */
01591             XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
01592             kid->parent = xml;
01593             ++i;
01594 
01595             /* XXX where is this documented in an XML spec, or in E4X? */
01596             if ((flags & XSF_IGNORE_WHITESPACE) &&
01597                 n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
01598                 str = ChompXMLWhitespace(cx, kid->xml_value);
01599                 if (!str)
01600                     goto fail;
01601                 kid->xml_value = str;
01602             }
01603         }
01604 
01605         JS_ASSERT(i == n);
01606         if (n < pn->pn_count - 2)
01607             XMLArrayTrim(&xml->xml_kids);
01608         XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
01609         break;
01610 
01611       case TOK_XMLLIST:
01612         xml = js_NewXML(cx, JSXML_CLASS_LIST);
01613         if (!xml)
01614             goto fail;
01615 
01616         n = pn->pn_count;
01617         if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
01618             goto fail;
01619 
01620         i = 0;
01621         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
01622             /*
01623              * Always ignore insignificant whitespace in lists -- we shouldn't
01624              * condition this on an XML.ignoreWhitespace setting when the list
01625              * constructor is XMLList (note XML/XMLList unification hazard).
01626              */
01627             if (pn2->pn_type == TOK_XMLSPACE) {
01628                 --n;
01629                 continue;
01630             }
01631 
01632             kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags);
01633             if (kid == PN2X_SKIP_CHILD) {
01634                 --n;
01635                 continue;
01636             }
01637 
01638             if (!kid)
01639                 goto fail;
01640 
01641             XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
01642             ++i;
01643         }
01644 
01645         if (n < pn->pn_count)
01646             XMLArrayTrim(&xml->xml_kids);
01647         break;
01648 
01649       case TOK_XMLSTAGO:
01650       case TOK_XMLPTAGC:
01651         length = inScopeNSes->length;
01652         pn2 = pn->pn_head;
01653         JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
01654         if (pn2->pn_arity == PN_LIST)
01655             goto syntax;
01656 
01657         xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
01658         if (!xml)
01659             goto fail;
01660 
01661         /* First pass: check syntax and process namespace declarations. */
01662         JS_ASSERT(pn->pn_count >= 1);
01663         n = pn->pn_count - 1;
01664         pnp = &pn2->pn_next;
01665         head = *pnp;
01666         while ((pn2 = *pnp) != NULL) {
01667             size_t length;
01668             const jschar *chars;
01669 
01670             if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
01671                 goto syntax;
01672 
01673             /* Enforce "Well-formedness constraint: Unique Att Spec". */
01674             for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
01675                 if (pn3->pn_atom == pn2->pn_atom) {
01676                     js_ReportCompileErrorNumber(cx, pn2,
01677                                                 JSREPORT_PN | JSREPORT_ERROR,
01678                                                 JSMSG_DUPLICATE_XML_ATTR,
01679                                                 js_ValueToPrintableString(cx,
01680                                                     ATOM_KEY(pn2->pn_atom)));
01681                     goto fail;
01682                 }
01683             }
01684 
01685             str = ATOM_TO_STRING(pn2->pn_atom);
01686             pn2 = pn2->pn_next;
01687             JS_ASSERT(pn2);
01688             if (pn2->pn_type != TOK_XMLATTR)
01689                 goto syntax;
01690 
01691             length = JSSTRING_LENGTH(str);
01692             chars = JSSTRING_CHARS(str);
01693             if (length >= 5 &&
01694                 IS_XMLNS_CHARS(chars) &&
01695                 (length == 5 || chars[5] == ':')) {
01696                 JSString *uri, *prefix;
01697 
01698                 uri = ATOM_TO_STRING(pn2->pn_atom);
01699                 if (length == 5) {
01700                     /* 10.3.2.1. Step 6(h)(i)(1)(a). */
01701                     prefix = cx->runtime->emptyString;
01702                 } else {
01703                     prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0);
01704                     if (!prefix)
01705                         goto fail;
01706                 }
01707 
01708                 /*
01709                  * Once the new ns is appended to xml->xml_namespaces, it is
01710                  * protected from GC by the object that owns xml -- which is
01711                  * either xml->object if outermost, or the object owning xml's
01712                  * oldest ancestor if !outermost.
01713                  */
01714                 ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE);
01715                 if (!ns)
01716                     goto fail;
01717 
01718                 /*
01719                  * Don't add a namespace that's already in scope.  If someone
01720                  * extracts a child property from its parent via [[Get]], then
01721                  * we enforce the invariant, noted many times in ECMA-357, that
01722                  * the child's namespaces form a possibly-improper superset of
01723                  * its ancestors' namespaces.
01724                  */
01725                 if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
01726                     if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
01727                         !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
01728                         goto fail;
01729                     }
01730                 }
01731 
01732                 JS_ASSERT(n >= 2);
01733                 n -= 2;
01734                 *pnp = pn2->pn_next;
01735                 /* XXXbe recycle pn2 */
01736                 continue;
01737             }
01738 
01739             pnp = &pn2->pn_next;
01740         }
01741 
01742         /*
01743          * If called from js_ParseNodeToXMLObject, emulate the effect of the
01744          * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
01745          * the String Type" (ECMA-357 10.3.1).
01746          */
01747         if (flags & XSF_PRECOMPILED_ROOT) {
01748             JS_ASSERT(length >= 1);
01749             ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace);
01750             JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
01751                                            namespace_identity));
01752             ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE);
01753             if (!ns)
01754                 goto fail;
01755             if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
01756                 goto fail;
01757         }
01758         XMLArrayTrim(&xml->xml_namespaces);
01759 
01760         /* Second pass: process tag name and attributes, using namespaces. */
01761         pn2 = pn->pn_head;
01762         qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE);
01763         if (!qn)
01764             goto fail;
01765         xml->name = qn;
01766 
01767         JS_ASSERT((n & 1) == 0);
01768         n >>= 1;
01769         if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
01770             goto fail;
01771 
01772         for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
01773             qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE);
01774             if (!qn) {
01775                 xml->xml_attrs.length = i;
01776                 goto fail;
01777             }
01778 
01779             /*
01780              * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
01781              * this time checking local name and namespace URI.
01782              */
01783             for (j = 0; j < i; j++) {
01784                 attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
01785                 attrjqn = attrj->name;
01786                 if (js_EqualStrings(attrjqn->uri, qn->uri) &&
01787                     js_EqualStrings(attrjqn->localName, qn->localName)) {
01788                     js_ReportCompileErrorNumber(cx, pn2,
01789                                                 JSREPORT_PN | JSREPORT_ERROR,
01790                                                 JSMSG_DUPLICATE_XML_ATTR,
01791                                                 js_ValueToPrintableString(cx,
01792                                                     ATOM_KEY(pn2->pn_atom)));
01793                     goto fail;
01794                 }
01795             }
01796 
01797             pn2 = pn2->pn_next;
01798             JS_ASSERT(pn2);
01799             JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
01800 
01801             attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
01802             if (!attr)
01803                 goto fail;
01804 
01805             XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
01806             attr->parent = xml;
01807             attr->name = qn;
01808             attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
01809         }
01810 
01811         /* Point tag closes its own namespace scope. */
01812         if (pn->pn_type == TOK_XMLPTAGC)
01813             XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
01814         break;
01815 
01816       case TOK_XMLSPACE:
01817       case TOK_XMLTEXT:
01818       case TOK_XMLCDATA:
01819       case TOK_XMLCOMMENT:
01820       case TOK_XMLPI:
01821         str = ATOM_TO_STRING(pn->pn_atom);
01822         qn = NULL;
01823         if (pn->pn_type == TOK_XMLCOMMENT) {
01824             if (flags & XSF_IGNORE_COMMENTS)
01825                 goto skip_child;
01826             xml_class = JSXML_CLASS_COMMENT;
01827         } else if (pn->pn_type == TOK_XMLPI) {
01828             if (IS_XML(str)) {
01829                 js_ReportCompileErrorNumber(cx, pn,
01830                                             JSREPORT_PN | JSREPORT_ERROR,
01831                                             JSMSG_RESERVED_ID,
01832                                             js_ValueToPrintableString(cx,
01833                                                 STRING_TO_JSVAL(str)));
01834                 goto fail;
01835             }
01836 
01837             if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
01838                 goto skip_child;
01839 
01840             qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE);
01841             if (!qn)
01842                 goto fail;
01843 
01844             str = pn->pn_atom2
01845                   ? ATOM_TO_STRING(pn->pn_atom2)
01846                   : cx->runtime->emptyString;
01847             xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
01848         } else {
01849             /* CDATA section content, or element text. */
01850             xml_class = JSXML_CLASS_TEXT;
01851         }
01852 
01853         xml = js_NewXML(cx, xml_class);
01854         if (!xml)
01855             goto fail;
01856         xml->name = qn;
01857         if (pn->pn_type == TOK_XMLSPACE)
01858             xml->xml_flags |= XMLF_WHITESPACE_TEXT;
01859         xml->xml_value = str;
01860         break;
01861 
01862       default:
01863         goto syntax;
01864     }
01865 
01866     js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
01867     if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
01868         return NULL;
01869     return xml;
01870 
01871 skip_child:
01872     js_LeaveLocalRootScope(cx);
01873     return PN2X_SKIP_CHILD;
01874 
01875 #undef PN2X_SKIP_CHILD
01876 
01877 syntax:
01878     js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
01879                                 JSMSG_BAD_XML_MARKUP);
01880 fail:
01881     js_LeaveLocalRootScope(cx);
01882     return NULL;
01883 }
01884 
01885 /*
01886  * XML helper, object-ops, and library functions.  We start with the helpers,
01887  * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
01888  */
01889 static JSBool
01890 GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
01891 {
01892     jsval v;
01893 
01894     if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
01895         return JS_FALSE;
01896     if (!VALUE_IS_FUNCTION(cx, v)) {
01897         *vp = JSVAL_VOID;
01898         return JS_TRUE;
01899     }
01900     return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
01901 }
01902 
01903 static JSBool
01904 FillSettingsCache(JSContext *cx)
01905 {
01906     int i;
01907     const char *name;
01908     jsval v;
01909     JSBool isSet;
01910 
01911     /* Note: XML_PRETTY_INDENT is not a boolean setting. */
01912     for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
01913         name = xml_static_props[i].name;
01914         if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet))
01915             return JS_FALSE;
01916         if (isSet)
01917             cx->xmlSettingFlags |= JS_BIT(i);
01918         else
01919             cx->xmlSettingFlags &= ~JS_BIT(i);
01920     }
01921 
01922     cx->xmlSettingFlags |= XSF_CACHE_VALID;
01923     return JS_TRUE;
01924 }
01925 
01926 static JSBool
01927 GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
01928 {
01929     int i;
01930 
01931     if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
01932         return JS_FALSE;
01933 
01934     for (i = 0; xml_static_props[i].name; i++) {
01935         if (!strcmp(xml_static_props[i].name, name)) {
01936             *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
01937             return JS_TRUE;
01938         }
01939     }
01940     *bp = JS_FALSE;
01941     return JS_TRUE;
01942 }
01943 
01944 static JSBool
01945 GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
01946 {
01947     jsval v;
01948 
01949     return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip);
01950 }
01951 
01952 static JSBool
01953 GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
01954 {
01955     JSBool flag;
01956 
01957     /* Just get the first flag to validate the setting flags cache. */
01958     if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
01959         return JS_FALSE;
01960     *flagsp = cx->xmlSettingFlags;
01961     return JS_TRUE;
01962 }
01963 
01964 static JSXML *
01965 ParseXMLSource(JSContext *cx, JSString *src)
01966 {
01967     jsval nsval;
01968     JSXMLNamespace *ns;
01969     size_t urilen, srclen, length, offset, dstlen;
01970     jschar *chars;
01971     const jschar *srcp, *endp;
01972     void *mark;
01973     JSTokenStream *ts;
01974     uintN lineno;
01975     JSStackFrame *fp;
01976     JSOp op;
01977     JSParseNode *pn;
01978     JSXML *xml;
01979     JSXMLArray nsarray;
01980     uintN flags;
01981 
01982     static const char prefix[] = "<parent xmlns='";
01983     static const char middle[] = "'>";
01984     static const char suffix[] = "</parent>";
01985 
01986 #define constrlen(constr)   (sizeof(constr) - 1)
01987 
01988     if (!js_GetDefaultXMLNamespace(cx, &nsval))
01989         return NULL;
01990     ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
01991 
01992     urilen = JSSTRING_LENGTH(ns->uri);
01993     srclen = JSSTRING_LENGTH(src);
01994     length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
01995              constrlen(suffix);
01996 
01997     chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
01998     if (!chars)
01999         return NULL;
02000 
02001     dstlen = length;
02002     js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
02003     offset = dstlen;
02004     js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen);
02005     offset += urilen;
02006     dstlen = length - offset + 1;
02007     js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
02008                              &dstlen);
02009     offset += dstlen;
02010     srcp = JSSTRING_CHARS(src);
02011     js_strncpy(chars + offset, srcp, srclen);
02012     offset += srclen;
02013     dstlen = length - offset + 1;
02014     js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
02015                              &dstlen);
02016     chars [offset + dstlen] = 0;
02017 
02018     mark = JS_ARENA_MARK(&cx->tempPool);
02019     ts = js_NewBufferTokenStream(cx, chars, length);
02020     if (!ts)
02021         return NULL;
02022     for (fp = cx->fp; fp && !fp->pc; fp = fp->down)
02023         continue;
02024     if (fp) {
02025         op = (JSOp) *fp->pc;
02026         if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
02027             ts->filename = fp->script->filename;
02028             lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
02029             for (endp = srcp + srclen; srcp < endp; srcp++)
02030                 if (*srcp == '\n')
02031                     --lineno;
02032             ts->lineno = lineno;
02033         }
02034     }
02035 
02036     JS_KEEP_ATOMS(cx->runtime);
02037     pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE);
02038     xml = NULL;
02039     if (pn && XMLArrayInit(cx, &nsarray, 1)) {
02040         if (GetXMLSettingFlags(cx, &flags))
02041             xml = ParseNodeToXML(cx, pn, &nsarray, flags);
02042 
02043         XMLArrayFinish(cx, &nsarray);
02044     }
02045     JS_UNKEEP_ATOMS(cx->runtime);
02046 
02047     JS_ARENA_RELEASE(&cx->tempPool, mark);
02048     JS_free(cx, chars);
02049     return xml;
02050 
02051 #undef constrlen
02052 }
02053 
02054 /*
02055  * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
02056  *
02057  * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
02058  * the constraint:
02059  *
02060  *     for all x belonging to XML:
02061  *         x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
02062  *
02063  * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
02064  * (in new sub-step 6(a), renumbering the others to (b) and (c)).
02065  *
02066  * Same goes for 10.4.1 Step 7(a).
02067  *
02068  * In order for XML.prototype.namespaceDeclarations() to work correctly, the
02069  * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
02070  * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
02071  * undeclared namespaces associated with x not belonging to ancestorNS.
02072  */
02073 static JSXML *
02074 OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
02075 {
02076     JSXMLNamespace *ns;
02077 
02078     ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace);
02079     xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
02080     if (!ns || !xml)
02081         return xml;
02082     if (xml->xml_class == JSXML_CLASS_ELEMENT) {
02083         if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
02084             return NULL;
02085         ns->declared = JS_FALSE;
02086     }
02087     xml->parent = NULL;
02088     return xml;
02089 }
02090 
02091 static JSObject *
02092 ToXML(JSContext *cx, jsval v)
02093 {
02094     JSObject *obj;
02095     JSXML *xml;
02096     JSClass *clasp;
02097     JSString *str;
02098     uint32 length;
02099 
02100     if (JSVAL_IS_PRIMITIVE(v)) {
02101         if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
02102             goto bad;
02103     } else {
02104         obj = JSVAL_TO_OBJECT(v);
02105         if (OBJECT_IS_XML(cx, obj)) {
02106             xml = (JSXML *) JS_GetPrivate(cx, obj);
02107             if (xml->xml_class == JSXML_CLASS_LIST) {
02108                 if (xml->xml_kids.length != 1)
02109                     goto bad;
02110                 xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
02111                 if (xml) {
02112                     JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
02113                     return js_GetXMLObject(cx, xml);
02114                 }
02115             }
02116             return obj;
02117         }
02118 
02119         clasp = OBJ_GET_CLASS(cx, obj);
02120         if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
02121             JS_ASSERT(0);
02122         }
02123 
02124         if (clasp != &js_StringClass &&
02125             clasp != &js_NumberClass &&
02126             clasp != &js_BooleanClass) {
02127             goto bad;
02128         }
02129     }
02130 
02131     str = js_ValueToString(cx, v);
02132     if (!str)
02133         return NULL;
02134     if (IS_EMPTY(str)) {
02135         length = 0;
02136 #ifdef __GNUC__         /* suppress bogus gcc warnings */
02137         xml = NULL;
02138 #endif
02139     } else {
02140         xml = ParseXMLSource(cx, str);
02141         if (!xml)
02142             return NULL;
02143         length = JSXML_LENGTH(xml);
02144     }
02145 
02146     if (length == 0) {
02147         obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
02148         if (!obj)
02149             return NULL;
02150     } else if (length == 1) {
02151         xml = OrphanXMLChild(cx, xml, 0);
02152         if (!xml)
02153             return NULL;
02154         obj = js_GetXMLObject(cx, xml);
02155         if (!obj)
02156             return NULL;
02157     } else {
02158         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
02159         return NULL;
02160     }
02161     return obj;
02162 
02163 bad:
02164     str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
02165     if (str) {
02166         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
02167                              JSMSG_BAD_XML_CONVERSION,
02168                              JS_GetStringBytes(str));
02169     }
02170     return NULL;
02171 }
02172 
02173 static JSBool
02174 Append(JSContext *cx, JSXML *list, JSXML *kid);
02175 
02176 static JSObject *
02177 ToXMLList(JSContext *cx, jsval v)
02178 {
02179     JSObject *obj, *listobj;
02180     JSXML *xml, *list, *kid;
02181     JSClass *clasp;
02182     JSString *str;
02183     uint32 i, length;
02184 
02185     if (JSVAL_IS_PRIMITIVE(v)) {
02186         if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
02187             goto bad;
02188     } else {
02189         obj = JSVAL_TO_OBJECT(v);
02190         if (OBJECT_IS_XML(cx, obj)) {
02191             xml = (JSXML *) JS_GetPrivate(cx, obj);
02192             if (xml->xml_class != JSXML_CLASS_LIST) {
02193                 listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
02194                 if (!listobj)
02195                     return NULL;
02196                 list = (JSXML *) JS_GetPrivate(cx, listobj);
02197                 if (!Append(cx, list, xml))
02198                     return NULL;
02199                 return listobj;
02200             }
02201             return obj;
02202         }
02203 
02204         clasp = OBJ_GET_CLASS(cx, obj);
02205         if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
02206             JS_ASSERT(0);
02207         }
02208 
02209         if (clasp != &js_StringClass &&
02210             clasp != &js_NumberClass &&
02211             clasp != &js_BooleanClass) {
02212             goto bad;
02213         }
02214     }
02215 
02216     str = js_ValueToString(cx, v);
02217     if (!str)
02218         return NULL;
02219     if (IS_EMPTY(str)) {
02220         xml = NULL;
02221         length = 0;
02222     } else {
02223         if (!js_EnterLocalRootScope(cx))
02224             return NULL;
02225         xml = ParseXMLSource(cx, str);
02226         if (!xml) {
02227             js_LeaveLocalRootScope(cx);
02228             return NULL;
02229         }
02230         length = JSXML_LENGTH(xml);
02231     }
02232 
02233     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
02234     if (listobj) {
02235         list = (JSXML *) JS_GetPrivate(cx, listobj);
02236         for (i = 0; i < length; i++) {
02237             kid = OrphanXMLChild(cx, xml, i);
02238             if (!kid || !Append(cx, list, kid)) {
02239                 listobj = NULL;
02240                 break;
02241             }
02242         }
02243     }
02244 
02245     if (xml)
02246         js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
02247     return listobj;
02248 
02249 bad:
02250     str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
02251     if (str) {
02252         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
02253                              JSMSG_BAD_XMLLIST_CONVERSION,
02254                              JS_GetStringBytes(str));
02255     }
02256     return NULL;
02257 }
02258 
02259 /*
02260  * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
02261  * and their library-public js_* counterparts.  The guts of MakeXMLCDataString,
02262  * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
02263  * MakeXMLSpecialString subroutine.
02264  *
02265  * These functions take ownership of sb->base, if sb is non-null, in all cases
02266  * of success or failure.
02267  */
02268 static JSString *
02269 MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
02270                      JSString *str, JSString *str2,
02271                      const jschar *prefix, size_t prefixlength,
02272                      const jschar *suffix, size_t suffixlength)
02273 {
02274     JSStringBuffer localSB;
02275     size_t length, length2, newlength;
02276     jschar *bp, *base;
02277 
02278     if (!sb) {
02279         sb = &localSB;
02280         js_InitStringBuffer(sb);
02281     }
02282 
02283     length = JSSTRING_LENGTH(str);
02284     length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
02285     newlength = STRING_BUFFER_OFFSET(sb) +
02286                 prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
02287                 suffixlength;
02288     bp = base = (jschar *)
02289                 JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
02290     if (!bp) {
02291         js_FinishStringBuffer(sb);
02292         return NULL;
02293     }
02294 
02295     bp += STRING_BUFFER_OFFSET(sb);
02296     js_strncpy(bp, prefix, prefixlength);
02297     bp += prefixlength;
02298     js_strncpy(bp, JSSTRING_CHARS(str), length);
02299     bp += length;
02300     if (length2 != 0) {
02301         *bp++ = (jschar) ' ';
02302         js_strncpy(bp, JSSTRING_CHARS(str2), length2);
02303         bp += length2;
02304     }
02305     js_strncpy(bp, suffix, suffixlength);
02306     bp[suffixlength] = 0;
02307 
02308     str = js_NewString(cx, base, newlength, 0);
02309     if (!str)
02310         free(base);
02311     return str;
02312 }
02313 
02314 static JSString *
02315 MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
02316 {
02317     static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
02318                                                  'C', 'D', 'A', 'T', 'A',
02319                                                  '['};
02320     static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
02321 
02322     return MakeXMLSpecialString(cx, sb, str, NULL,
02323                                 cdata_prefix_ucNstr, 9,
02324                                 cdata_suffix_ucNstr, 3);
02325 }
02326 
02327 static JSString *
02328 MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
02329 {
02330     static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
02331     static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
02332 
02333     return MakeXMLSpecialString(cx, sb, str, NULL,
02334                                 comment_prefix_ucNstr, 4,
02335                                 comment_suffix_ucNstr, 3);
02336 }
02337 
02338 static JSString *
02339 MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
02340                 JSString *value)
02341 {
02342     static const jschar pi_prefix_ucNstr[] = {'<', '?'};
02343     static const jschar pi_suffix_ucNstr[] = {'?', '>'};
02344 
02345     return MakeXMLSpecialString(cx, sb, name, value,
02346                                 pi_prefix_ucNstr, 2,
02347                                 pi_suffix_ucNstr, 2);
02348 }
02349 
02350 /*
02351  * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
02352  * equals, a double quote, an attribute value, and a closing double quote.
02353  */
02354 static void
02355 AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
02356 {
02357     js_AppendCString(sb, "=\"");
02358     valstr = js_EscapeAttributeValue(cx, valstr);
02359     if (!valstr) {
02360         free(sb->base);
02361         sb->base = STRING_BUFFER_ERROR_BASE;
02362         return;
02363     }
02364     js_AppendJSString(sb, valstr);
02365     js_AppendChar(sb, '"');
02366 }
02367 
02368 /*
02369  * ECMA-357 10.2.1.1 EscapeElementValue helper method.
02370  *
02371  * This function takes ownership of sb->base, if sb is non-null, in all cases
02372  * of success or failure.
02373  */
02374 static JSString *
02375 EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
02376 {
02377     size_t length, newlength;
02378     const jschar *cp, *start, *end;
02379     jschar c;
02380 
02381     length = newlength = JSSTRING_LENGTH(str);
02382     for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
02383         c = *cp;
02384         if (c == '<' || c == '>')
02385             newlength += 3;
02386         else if (c == '&')
02387             newlength += 4;
02388 
02389         if (newlength < length) {
02390             JS_ReportOutOfMemory(cx);
02391             return NULL;
02392         }
02393     }
02394     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
02395         JSStringBuffer localSB;
02396         if (!sb) {
02397             sb = &localSB;
02398             js_InitStringBuffer(sb);
02399         }
02400         if (!sb->grow(sb, newlength)) {
02401             JS_ReportOutOfMemory(cx);
02402             return NULL;
02403         }
02404         for (cp = start; cp < end; cp++) {
02405             c = *cp;
02406             if (c == '<')
02407                 js_AppendCString(sb, js_lt_entity_str);
02408             else if (c == '>')
02409                 js_AppendCString(sb, js_gt_entity_str);
02410             else if (c == '&')
02411                 js_AppendCString(sb, js_amp_entity_str);
02412             else
02413                 js_AppendChar(sb, c);
02414         }
02415         JS_ASSERT(STRING_BUFFER_OK(sb));
02416         str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
02417         if (!str)
02418             js_FinishStringBuffer(sb);
02419     }
02420     return str;
02421 }
02422 
02423 /*
02424  * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
02425  * This function takes ownership of sb->base, if sb is non-null, in all cases.
02426  */
02427 static JSString *
02428 EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
02429 {
02430     size_t length, newlength;
02431     const jschar *cp, *start, *end;
02432     jschar c;
02433 
02434     length = newlength = JSSTRING_LENGTH(str);
02435     for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
02436         c = *cp;
02437         if (c == '"')
02438             newlength += 5;
02439         else if (c == '<')
02440             newlength += 3;
02441         else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
02442             newlength += 4;
02443 
02444         if (newlength < length) {
02445             JS_ReportOutOfMemory(cx);
02446             return NULL;
02447         }
02448     }
02449     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
02450         JSStringBuffer localSB;
02451         if (!sb) {
02452             sb = &localSB;
02453             js_InitStringBuffer(sb);
02454         }
02455         if (!sb->grow(sb, newlength)) {
02456             JS_ReportOutOfMemory(cx);
02457             return NULL;
02458         }
02459         for (cp = start; cp < end; cp++) {
02460             c = *cp;
02461             if (c == '"')
02462                 js_AppendCString(sb, js_quot_entity_str);
02463             else if (c == '<')
02464                 js_AppendCString(sb, js_lt_entity_str);
02465             else if (c == '&')
02466                 js_AppendCString(sb, js_amp_entity_str);
02467             else if (c == '\n')
02468                 js_AppendCString(sb, "&#xA;");
02469             else if (c == '\r')
02470                 js_AppendCString(sb, "&#xD;");
02471             else if (c == '\t')
02472                 js_AppendCString(sb, "&#x9;");
02473             else
02474                 js_AppendChar(sb, c);
02475         }
02476         JS_ASSERT(STRING_BUFFER_OK(sb));
02477         str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
02478         if (!str)
02479             js_FinishStringBuffer(sb);
02480     }
02481     return str;
02482 }
02483 
02484 /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
02485 static JSXMLNamespace *
02486 GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes)
02487 {
02488     JSXMLNamespace *match, *ns;
02489     uint32 i, n;
02490     jsval argv[2];
02491     JSObject *nsobj;
02492 
02493     JS_ASSERT(qn->uri);
02494     if (!qn->uri) {
02495         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
02496                              JSMSG_BAD_XML_NAMESPACE,
02497                              qn->prefix
02498                              ? js_ValueToPrintableString(cx,
02499                                    STRING_TO_JSVAL(qn->prefix))
02500                              : js_type_strs[JSTYPE_VOID]);
02501         return NULL;
02502     }
02503 
02504     /* Look for a matching namespace in inScopeNSes, if provided. */
02505     match = NULL;
02506     if (inScopeNSes) {
02507         for (i = 0, n = inScopeNSes->length; i < n; i++) {
02508             ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace);
02509             if (!ns)
02510                 continue;
02511 
02512             /*
02513              * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
02514              * If we preserve prefixes, we must match null qn->prefix against
02515              * an empty ns->prefix, in order to avoid generating redundant
02516              * prefixed and default namespaces for cases such as:
02517              *
02518              *   x = <t xmlns="http://foo.com"/>
02519              *   print(x.toXMLString());
02520              *
02521              * Per 10.3.2.1, the namespace attribute in t has an empty string
02522              * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
02523              *
02524              *   1. If the [local name] property of a is "xmlns"
02525              *      a. Map ns.prefix to the empty string
02526              *
02527              * But t's name has a null prefix in this implementation, meaning
02528              * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
02529              * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
02530              * saying how "no value" maps to an ECMA-357 value -- but it must
02531              * map to the *undefined* prefix value).
02532              *
02533              * Since "" != undefined (or null, in the current implementation)
02534              * the ECMA-357 spec will fail to match in [[GetNamespace]] called
02535              * on t with argument {} U {(prefix="", uri="http://foo.com")}.
02536              * This spec bug leads to ToXMLString results that duplicate the
02537              * declared namespace.
02538              */
02539             if (js_EqualStrings(ns->uri, qn->uri) &&
02540                 (ns->prefix == qn->prefix ||
02541                  ((ns->prefix && qn->prefix)
02542                   ? js_EqualStrings(ns->prefix, qn->prefix)
02543                   : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) {
02544                 match = ns;
02545                 break;
02546             }
02547         }
02548     }
02549 
02550     /* If we didn't match, make a new namespace from qn. */
02551     if (!match) {
02552         argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID;
02553         argv[1] = STRING_TO_JSVAL(qn->uri);
02554         nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
02555                                    2, argv);
02556         if (!nsobj)
02557             return NULL;
02558         match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
02559     }
02560     return match;
02561 }
02562 
02563 static JSString *
02564 GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
02565 {
02566     const jschar *cp, *start, *end;
02567     size_t length, newlength, offset;
02568     uint32 i, n, m, serial;
02569     jschar *bp, *dp;
02570     JSBool done;
02571     JSXMLNamespace *ns;
02572     JSString *prefix;
02573 
02574     JS_ASSERT(!IS_EMPTY(uri));
02575 
02576     /*
02577      * If there are no *declared* namespaces, skip all collision detection and
02578      * return a short prefix quickly; an example of such a situation:
02579      *
02580      *   var x = <f/>;
02581      *   var n = new Namespace("http://example.com/");
02582      *   x.@n::att = "val";
02583      *   x.toXMLString();
02584      *
02585      * This is necessary for various log10 uses below to be valid.
02586      */
02587     if (decls->length == 0)
02588         return JS_NewStringCopyZ(cx, "a");
02589 
02590     /*
02591      * Try peeling off the last filename suffix or pathname component till
02592      * we have a valid XML name.  This heuristic will prefer "xul" given
02593      * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
02594      * likely URI of the form ".../xbl2/2005".
02595      */
02596     start = JSSTRING_CHARS(uri);
02597     cp = end = start + JSSTRING_LENGTH(uri);
02598     while (--cp > start) {
02599         if (*cp == '.' || *cp == '/' || *cp == ':') {
02600             ++cp;
02601             length = PTRDIFF(end, cp, jschar);
02602             if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
02603                 break;
02604             end = --cp;
02605         }
02606     }
02607     length = PTRDIFF(end, cp, jschar);
02608 
02609     /*
02610      * If the namespace consisted only of non-XML names or names that begin
02611      * case-insensitively with "xml", arbitrarily create a prefix consisting
02612      * of 'a's of size length (allowing dp-calculating code to work with or
02613      * without this branch executing) plus the space for storing a hyphen and
02614      * the serial number (avoiding reallocation if a collision happens).
02615      */
02616     bp = (jschar *) cp;
02617     newlength = length;
02618     if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
02619         newlength = length + 2 + (size_t) log10(decls->length);
02620         bp = (jschar *)
02621              JS_malloc(cx, (newlength + 1) * sizeof(jschar));
02622         if (!bp)
02623             return NULL;
02624 
02625         bp[newlength] = 0;
02626         for (i = 0; i < newlength; i++)
02627              bp[i] = 'a';
02628     }
02629 
02630     /*
02631      * Now search through decls looking for a collision.  If we collide with
02632      * an existing prefix, start tacking on a hyphen and a serial number.
02633      */
02634     serial = 0;
02635     do {
02636         done = JS_TRUE;
02637         for (i = 0, n = decls->length; i < n; i++) {
02638             ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace);
02639             if (ns && ns->prefix &&
02640                 JSSTRING_LENGTH(ns->prefix) == newlength &&
02641                 !memcmp(JSSTRING_CHARS(ns->prefix), bp,
02642                         newlength * sizeof(jschar))) {
02643                 if (bp == cp) {
02644                     newlength = length + 2 + (size_t) log10(n);
02645                     bp = (jschar *)
02646                          JS_malloc(cx, (newlength + 1) * sizeof(jschar));
02647                     if (!bp)
02648                         return NULL;
02649                     js_strncpy(bp, cp, length);
02650                 }
02651 
02652                 ++serial;
02653                 JS_ASSERT(serial <= n);
02654                 dp = bp + length + 2 + (size_t) log10(serial);
02655                 *dp = 0;
02656                 for (m = serial; m != 0; m /= 10)
02657                     *--dp = (jschar)('0' + m % 10);
02658                 *--dp = '-';
02659                 JS_ASSERT(dp == bp + length);
02660 
02661                 done = JS_FALSE;
02662                 break;
02663             }
02664         }
02665     } while (!done);
02666 
02667     if (bp == cp) {
02668         offset = PTRDIFF(cp, start, jschar);
02669         prefix = js_NewDependentString(cx, uri, offset, length, 0);
02670     } else {
02671         prefix = js_NewString(cx, bp, newlength, 0);
02672         if (!prefix)
02673             JS_free(cx, bp);
02674     }
02675     return prefix;
02676 }
02677 
02678 static JSBool
02679 namespace_match(const void *a, const void *b)
02680 {
02681     const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
02682     const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
02683 
02684     if (nsb->prefix)
02685         return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix);
02686     return js_EqualStrings(nsa->uri, nsb->uri);
02687 }
02688 
02689 /* ECMA-357 10.2.1 and 10.2.2 */
02690 static JSString *
02691 XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
02692                uintN indentLevel)
02693 {
02694     JSBool pretty, indentKids;
02695     JSStringBuffer sb;
02696     JSString *str, *prefix, *kidstr;
02697     JSXMLArrayCursor cursor;
02698     uint32 i, n;
02699     JSXMLArray empty, decls, ancdecls;
02700     JSXMLNamespace *ns, *ns2;
02701     uintN nextIndentLevel;
02702     JSXML *attr, *kid;
02703 
02704     if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
02705         return NULL;
02706 
02707     js_InitStringBuffer(&sb);
02708     if (pretty)
02709         js_RepeatChar(&sb, ' ', indentLevel);
02710     str = NULL;
02711 
02712     switch (xml->xml_class) {
02713       case JSXML_CLASS_TEXT:
02714         /* Step 4. */
02715         if (pretty) {
02716             str = ChompXMLWhitespace(cx, xml->xml_value);
02717             if (!str)
02718                 return NULL;
02719         } else {
02720             str = xml->xml_value;
02721         }
02722         return EscapeElementValue(cx, &sb, str);
02723 
02724       case JSXML_CLASS_ATTRIBUTE:
02725         /* Step 5. */
02726         return EscapeAttributeValue(cx, &sb, xml->xml_value);
02727 
02728       case JSXML_CLASS_COMMENT:
02729         /* Step 6. */
02730         return MakeXMLCommentString(cx, &sb, xml->xml_value);
02731 
02732       case JSXML_CLASS_PROCESSING_INSTRUCTION:
02733         /* Step 7. */
02734         return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value);
02735 
02736       case JSXML_CLASS_LIST:
02737         /* ECMA-357 10.2.2. */
02738         XMLArrayCursorInit(&cursor, &xml->xml_kids);
02739         i = 0;
02740         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
02741             if (pretty && i != 0)
02742                 js_AppendChar(&sb, '\n');
02743 
02744             kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel);
02745             if (!kidstr)
02746                 break;
02747 
02748             js_AppendJSString(&sb, kidstr);
02749             ++i;
02750         }
02751         XMLArrayCursorFinish(&cursor);
02752         if (kid)
02753             goto list_out;
02754 
02755         if (!sb.base) {
02756             if (!STRING_BUFFER_OK(&sb)) {
02757                 JS_ReportOutOfMemory(cx);
02758                 return NULL;
02759             }
02760             return cx->runtime->emptyString;
02761         }
02762 
02763         str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
02764       list_out:
02765         if (!str)
02766             js_FinishStringBuffer(&sb);
02767         return str;
02768 
02769       default:;
02770     }
02771 
02772     /* After this point, control must flow through label out: to exit. */
02773     if (!js_EnterLocalRootScope(cx))
02774         return NULL;
02775 
02776     /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
02777     if (!ancestorNSes) {
02778         XMLArrayInit(cx, &empty, 0);
02779         ancestorNSes = &empty;
02780     }
02781     XMLArrayInit(cx, &decls, 0);
02782     ancdecls.capacity = 0;
02783 
02784     /* Clone in-scope namespaces not in ancestorNSes into decls. */
02785     XMLArrayCursorInit(&cursor, &xml->xml_namespaces);
02786     while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
02787         if (!ns->declared)
02788             continue;
02789         if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
02790             /* NOTE: may want to exclude unused namespaces here. */
02791             ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE);
02792             if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2))
02793                 break;
02794         }
02795     }
02796     XMLArrayCursorFinish(&cursor);
02797     if (ns)
02798         goto out;
02799 
02800     /*
02801      * Union ancestorNSes and decls into ancdecls.  Note that ancdecls does
02802      * not own its member references.  In the spec, ancdecls has no name, but
02803      * is always written out as (AncestorNamespaces U namespaceDeclarations).
02804      */
02805     if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length))
02806         goto out;
02807     for (i = 0, n = ancestorNSes->length; i < n; i++) {
02808         ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace);
02809         if (!ns2)
02810             continue;
02811         JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity));
02812         if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
02813             goto out;
02814     }
02815     for (i = 0, n = decls.length; i < n; i++) {
02816         ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace);
02817         if (!ns2)
02818             continue;
02819         JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity));
02820         if (!XMLARRAY_APPEND(cx, &ancdecls, ns2))
02821             goto out;
02822     }
02823 
02824     /* Step 11, except we don't clone ns unless its prefix is undefined. */
02825     ns = GetNamespace(cx, xml->name, &ancdecls);
02826     if (!ns)
02827         goto out;
02828 
02829     /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
02830     if (!ns->prefix) {
02831         /*
02832          * Create a namespace prefix that isn't used by any member of decls.
02833          * Assign the new prefix to a copy of ns.  Flag this namespace as if
02834          * it were declared, for assertion-testing's sake later below.
02835          *
02836          * Erratum: if ns->prefix and xml->name are both null (*undefined* in
02837          * ECMA-357), we know that xml was named using the default namespace
02838          * (proof: see GetNamespace and the Namespace constructor called with
02839          * two arguments).  So we ought not generate a new prefix here, when
02840          * we can declare ns as the default namespace for xml.
02841          *
02842          * This helps descendants inherit the namespace instead of redundantly
02843          * redeclaring it with generated prefixes in each descendant.
02844          */
02845         if (!xml->name->prefix) {
02846             prefix = cx->runtime->emptyString;
02847         } else {
02848             prefix = GeneratePrefix(cx, ns->uri, &ancdecls);
02849             if (!prefix)
02850                 goto out;
02851         }
02852         ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE);
02853         if (!ns)
02854             goto out;
02855 
02856         /*
02857          * If the xml->name was unprefixed, we must remove any declared default
02858          * namespace from decls before appending ns.  How can you get a default
02859          * namespace in decls that doesn't match the one from name?  Apparently
02860          * by calling x.setNamespace(ns) where ns has no prefix.  The other way
02861          * to fix this is to update x's in-scope namespaces when setNamespace
02862          * is called, but that's not specified by ECMA-357.
02863          *
02864          * Likely Erratum here, depending on whether the lack of update to x's
02865          * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
02866          * erratum or not.  Note that changing setNamespace to update the list
02867          * of in-scope namespaces will change x.namespaceDeclarations().
02868          */
02869         if (IS_EMPTY(prefix)) {
02870             i = XMLArrayFindMember(&decls, ns, namespace_match);
02871             if (i != XML_NOT_FOUND)
02872                 XMLArrayDelete(cx, &decls, i, JS_TRUE);
02873         }
02874 
02875         /*
02876          * In the spec, ancdecls has no name, but is always written out as
02877          * (AncestorNamespaces U namespaceDeclarations).  Since we compute
02878          * that union in ancdecls, any time we append a namespace strong
02879          * ref to decls, we must also append a weak ref to ancdecls.  Order
02880          * matters here: code at label out: releases strong refs in decls.
02881          */
02882         if (!XMLARRAY_APPEND(cx, &ancdecls, ns) ||
02883             !XMLARRAY_APPEND(cx, &decls, ns)) {
02884             goto out;
02885         }
02886     }
02887 
02888     /* Format the element or point-tag into sb. */
02889     js_AppendChar(&sb, '<');
02890 
02891     if (ns->prefix && !IS_EMPTY(ns->prefix)) {
02892         js_AppendJSString(&sb, ns->prefix);
02893         js_AppendChar(&sb, ':');
02894     }
02895     js_AppendJSString(&sb, xml->name->localName);
02896 
02897     /*
02898      * Step 16 makes a union to avoid writing two loops in step 17, to share
02899      * common attribute value appending spec-code.  We prefer two loops for
02900      * faster code and less data overhead.
02901      */
02902 
02903     /* Step 17(b): append attributes. */
02904     XMLArrayCursorInit(&cursor, &xml->xml_attrs);
02905     while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
02906         js_AppendChar(&sb, ' ');
02907         ns2 = GetNamespace(cx, attr->name, &ancdecls);
02908         if (!ns2)
02909             break;
02910 
02911         /* 17(b)(ii): NULL means *undefined* here. */
02912         if (!ns2->prefix) {
02913             prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
02914             if (!prefix)
02915                 break;
02916 
02917             /* Again, we avoid copying ns2 until we know it's prefix-less. */
02918             ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE);
02919             if (!ns2)
02920                 break;
02921 
02922             /*
02923              * In the spec, ancdecls has no name, but is always written out as
02924              * (AncestorNamespaces U namespaceDeclarations).  Since we compute
02925              * that union in ancdecls, any time we append a namespace strong
02926              * ref to decls, we must also append a weak ref to ancdecls.  Order
02927              * matters here: code at label out: releases strong refs in decls.
02928              */
02929             if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) ||
02930                 !XMLARRAY_APPEND(cx, &decls, ns2)) {
02931                 break;
02932             }
02933         }
02934 
02935         /* 17(b)(iii). */
02936         if (!IS_EMPTY(ns2->prefix)) {
02937             js_AppendJSString(&sb, ns2->prefix);
02938             js_AppendChar(&sb, ':');
02939         }
02940 
02941         /* 17(b)(iv). */
02942         js_AppendJSString(&sb, attr->name->localName);
02943 
02944         /* 17(d-g). */
02945         AppendAttributeValue(cx, &sb, attr->xml_value);
02946     }
02947     XMLArrayCursorFinish(&cursor);
02948     if (attr)
02949         goto out;
02950 
02951     /* Step 17(c): append XML namespace declarations. */
02952     XMLArrayCursorInit(&cursor, &decls);
02953     while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) {
02954         JS_ASSERT(ns2->declared);
02955 
02956         js_AppendCString(&sb, " xmlns");
02957 
02958         /* 17(c)(ii): NULL means *undefined* here. */
02959         if (!ns2->prefix) {
02960             prefix = GeneratePrefix(cx, ns2->uri, &ancdecls);
02961             if (!prefix)
02962                 break;
02963             ns2->prefix = prefix;
02964         }
02965 
02966         /* 17(c)(iii). */
02967         if (!IS_EMPTY(ns2->prefix)) {
02968             js_AppendChar(&sb, ':');
02969             js_AppendJSString(&sb, ns2->prefix);
02970         }
02971 
02972         /* 17(d-g). */
02973         AppendAttributeValue(cx, &sb, ns2->uri);
02974     }
02975     XMLArrayCursorFinish(&cursor);
02976     if (ns2)
02977         goto out;
02978 
02979     /* Step 18: handle point tags. */
02980     n = xml->xml_kids.length;
02981     if (n == 0) {
02982         js_AppendCString(&sb, "/>");
02983     } else {
02984         /* Steps 19 through 25: handle element content, and open the end-tag. */
02985         js_AppendChar(&sb, '>');
02986         indentKids = n > 1 ||
02987                      (n == 1 &&
02988                       (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
02989                       kid->xml_class != JSXML_CLASS_TEXT);
02990 
02991         if (pretty && indentKids) {
02992             if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
02993                 goto out;
02994             nextIndentLevel = indentLevel + i;
02995         } else {
02996             nextIndentLevel = 0;
02997         }
02998 
02999         XMLArrayCursorInit(&cursor, &xml->xml_kids);
03000         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
03001             if (pretty && indentKids)
03002                 js_AppendChar(&sb, '\n');
03003 
03004             kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel);
03005             if (!kidstr)
03006                 break;
03007 
03008             js_AppendJSString(&sb, kidstr);
03009         }
03010         XMLArrayCursorFinish(&cursor);
03011         if (kid)
03012             goto out;
03013 
03014         if (pretty && indentKids) {
03015             js_AppendChar(&sb, '\n');
03016             js_RepeatChar(&sb, ' ', indentLevel);
03017         }
03018         js_AppendCString(&sb, "</");
03019 
03020         /* Step 26. */
03021         if (ns->prefix && !IS_EMPTY(ns->prefix)) {
03022             js_AppendJSString(&sb, ns->prefix);
03023             js_AppendChar(&sb, ':');
03024         }
03025 
03026         /* Step 27. */
03027         js_AppendJSString(&sb, xml->name->localName);
03028         js_AppendChar(&sb, '>');
03029     }
03030 
03031     if (!STRING_BUFFER_OK(&sb)) {
03032         JS_ReportOutOfMemory(cx);
03033         goto out;
03034     }
03035 
03036     str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
03037 out:
03038     js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
03039     if (!str && STRING_BUFFER_OK(&sb))
03040         js_FinishStringBuffer(&sb);
03041     XMLArrayFinish(cx, &decls);
03042     if (ancdecls.capacity != 0)
03043         XMLArrayFinish(cx, &ancdecls);
03044     return str;
03045 }
03046 
03047 /* ECMA-357 10.2 */
03048 static JSString *
03049 ToXMLString(JSContext *cx, jsval v)
03050 {
03051     JSObject *obj;
03052     JSString *str;
03053     JSXML *xml;
03054 
03055     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
03056         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
03057                              JSMSG_BAD_XML_CONVERSION,
03058                              js_type_strs[JSVAL_IS_NULL(v)
03059                                           ? JSTYPE_NULL
03060                                           : JSTYPE_VOID]);
03061         return NULL;
03062     }
03063 
03064     if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
03065         return js_ValueToString(cx, v);
03066 
03067     if (JSVAL_IS_STRING(v))
03068         return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v));
03069 
03070     obj = JSVAL_TO_OBJECT(v);
03071     if (!OBJECT_IS_XML(cx, obj)) {
03072         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
03073             return NULL;
03074         str = js_ValueToString(cx, v);
03075         if (!str)
03076             return NULL;
03077         return EscapeElementValue(cx, NULL, str);
03078     }
03079 
03080     /* Handle non-element cases in this switch, returning from each case. */
03081     xml = (JSXML *) JS_GetPrivate(cx, obj);
03082     return XMLToXMLString(cx, xml, NULL, 0);
03083 }
03084 
03085 static JSXMLQName *
03086 ToAttributeName(JSContext *cx, jsval v)
03087 {
03088     JSString *name, *uri, *prefix;
03089     JSObject *obj;
03090     JSClass *clasp;
03091     JSXMLQName *qn;
03092     JSTempValueRooter tvr;
03093 
03094     if (JSVAL_IS_STRING(v)) {
03095         name = JSVAL_TO_STRING(v);
03096         uri = prefix = cx->runtime->emptyString;
03097     } else {
03098         if (JSVAL_IS_PRIMITIVE(v)) {
03099             name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
03100             if (name) {
03101                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
03102                                      JSMSG_BAD_XML_ATTR_NAME,
03103                                      JS_GetStringBytes(name));
03104             }
03105             return NULL;
03106         }
03107 
03108         obj = JSVAL_TO_OBJECT(v);
03109         clasp = OBJ_GET_CLASS(cx, obj);
03110         if (clasp == &js_AttributeNameClass)
03111             return (JSXMLQName *) JS_GetPrivate(cx, obj);
03112 
03113         if (clasp == &js_QNameClass.base) {
03114             qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
03115             uri = qn->uri;
03116             prefix = qn->prefix;
03117             name = qn->localName;
03118         } else {
03119             if (clasp == &js_AnyNameClass) {
03120                 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
03121             } else {
03122                 name = js_ValueToString(cx, v);
03123                 if (!name)
03124                     return NULL;
03125             }
03126             uri = prefix = cx->runtime->emptyString;
03127         }
03128     }
03129 
03130     qn = js_NewXMLQName(cx, uri, prefix, name);
03131     if (!qn)
03132         return NULL;
03133 
03134     JS_PUSH_TEMP_ROOT_QNAME(cx, qn, &tvr);
03135     obj = js_GetAttributeNameObject(cx, qn);
03136     JS_POP_TEMP_ROOT(cx, &tvr);
03137     if (!obj)
03138         return NULL;
03139     return qn;
03140 }
03141 
03142 static JSXMLQName *
03143 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
03144 {
03145     JSString *name;
03146     JSObject *obj;
03147     JSClass *clasp;
03148     uint32 index;
03149     JSXMLQName *qn;
03150     JSAtom *atom;
03151 
03152     if (JSVAL_IS_STRING(v)) {
03153         name = JSVAL_TO_STRING(v);
03154     } else {
03155         if (JSVAL_IS_PRIMITIVE(v)) {
03156             name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL);
03157             if (name)
03158                 goto bad;
03159             return NULL;
03160         }
03161 
03162         obj = JSVAL_TO_OBJECT(v);
03163         clasp = OBJ_GET_CLASS(cx, obj);
03164         if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base)
03165             goto out;
03166         if (clasp == &js_AnyNameClass) {
03167             name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
03168             goto construct;
03169         }
03170         name = js_ValueToString(cx, v);
03171         if (!name)
03172             return NULL;
03173     }
03174 
03175     /*
03176      * ECMA-357 10.6.1 step 1 seems to be incorrect.  The spec says:
03177      *
03178      * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
03179      *
03180      * First, _P_ should be _s_, to refer to the given string.
03181      *
03182      * Second, why does ToXMLName applied to the string type throw TypeError
03183      * only for numeric literals without any leading or trailing whitespace?
03184      *
03185      * If the idea is to reject uint32 property names, then the check needs to
03186      * be stricter, to exclude hexadecimal and floating point literals.
03187      */
03188     if (js_IdIsIndex(STRING_TO_JSVAL(name), &index))
03189         goto bad;
03190 
03191     if (*JSSTRING_CHARS(name) == '@') {
03192         name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0);
03193         if (!name)
03194             return NULL;
03195         *funidp = 0;
03196         return ToAttributeName(cx, STRING_TO_JSVAL(name));
03197     }
03198 
03199 construct:
03200     v = STRING_TO_JSVAL(name);
03201     obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v);
03202     if (!obj)
03203         return NULL;
03204 
03205 out:
03206     qn = (JSXMLQName *) JS_GetPrivate(cx, obj);
03207     atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom;
03208     if (qn->uri && atom &&
03209         (qn->uri == ATOM_TO_STRING(atom) ||
03210          js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) {
03211         if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp))
03212             return NULL;
03213     } else {
03214         *funidp = 0;
03215     }
03216     return qn;
03217 
03218 bad:
03219     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
03220                          JSMSG_BAD_XML_NAME,
03221                          js_ValueToPrintableString(cx, STRING_TO_JSVAL(name)));
03222     return NULL;
03223 }
03224 
03225 /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
03226 static JSBool
03227 AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
03228 {
03229     JSXMLNamespace *match, *ns2;
03230     uint32 i, n, m;
03231 
03232     if (xml->xml_class != JSXML_CLASS_ELEMENT)
03233         return JS_TRUE;
03234 
03235     /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
03236     if (!ns->prefix) {
03237         match = NULL;
03238         for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
03239             ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
03240             if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) {
03241                 match = ns2;
03242                 break;
03243             }
03244         }
03245         if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
03246             return JS_FALSE;
03247     } else {
03248         if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri))
03249             return JS_TRUE;
03250         match = NULL;
03251 #ifdef __GNUC__         /* suppress bogus gcc warnings */
03252         m = XML_NOT_FOUND;
03253 #endif
03254         for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
03255             ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
03256             if (ns2 && ns2->prefix &&
03257                 js_EqualStrings(ns2->prefix, ns->prefix)) {
03258                 match = ns2;
03259                 m = i;
03260                 break;
03261             }
03262         }
03263         if (match && !js_EqualStrings(match->uri, ns->uri)) {
03264             ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
03265                                   JSXMLNamespace);
03266             JS_ASSERT(ns2 == match);
03267             match->prefix = NULL;
03268             if (!AddInScopeNamespace(cx, xml, match))
03269                 return JS_FALSE;
03270         }
03271         if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
03272             return JS_FALSE;
03273     }
03274 
03275     /* OPTION: enforce that descendants have superset namespaces. */
03276     return JS_TRUE;
03277 }
03278 
03279 /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
03280 static JSBool
03281 Append(JSContext *cx, JSXML *list, JSXML *xml)
03282 {
03283     uint32 i, j, k, n;
03284     JSXML *kid;
03285 
03286     JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
03287     i = list->xml_kids.length;
03288     n = 1;
03289     if (xml->xml_class == JSXML_CLASS_LIST) {
03290         list->xml_target = xml->xml_target;
03291         list->xml_targetprop = xml->xml_targetprop;
03292         n = JSXML_LENGTH(xml);
03293         k = i + n;
03294         if (!XMLArraySetCapacity(cx, &list->xml_kids, k))
03295             return JS_FALSE;
03296         for (j = 0; j < n; j++) {
03297             kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML);
03298             if (kid)
03299                 XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
03300         }
03301         return JS_TRUE;
03302     }
03303 
03304     list->xml_target = xml->parent;
03305     if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
03306         list->xml_targetprop = NULL;
03307     else
03308         list->xml_targetprop = xml->name;
03309     if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
03310         return JS_FALSE;
03311     return JS_TRUE;
03312 }
03313 
03314 /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
03315 static JSXML *
03316 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags);
03317 
03318 static JSXML *
03319 DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags)
03320 {
03321     JSXML *copy;
03322     JSBool ok;
03323 
03324     /* Our caller may not be protecting newborns with a local root scope. */
03325     if (!js_EnterLocalRootScope(cx))
03326         return NULL;
03327     copy = DeepCopyInLRS(cx, xml, flags);
03328     if (copy) {
03329         if (obj) {
03330             /* Caller provided the object for this copy, hook 'em up. */
03331             ok = JS_SetPrivate(cx, obj, copy);
03332             if (ok)
03333                 copy->object = obj;
03334         } else {
03335             ok = js_GetXMLObject(cx, copy) != NULL;
03336         }
03337         if (!ok)
03338             copy = NULL;
03339     }
03340     js_LeaveLocalRootScopeWithResult(cx, (jsval) copy);
03341     return copy;
03342 }
03343 
03344 /*
03345  * (i) We must be in a local root scope (InLRS).
03346  * (ii) parent must have a rooted object.
03347  * (iii) from's owning object must be locked if not thread-local.
03348  */
03349 static JSBool
03350 DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
03351                  uintN flags)
03352 {
03353     uint32 j, n;
03354     JSXMLArrayCursor cursor;
03355     JSBool ok;
03356     JSXML *kid, *kid2;
03357     JSString *str;
03358 
03359     JS_ASSERT(cx->localRootStack);
03360 
03361     n = from->length;
03362     if (!XMLArraySetCapacity(cx, to, n))
03363         return JS_FALSE;
03364 
03365     XMLArrayCursorInit(&cursor, from);
03366     j = 0;
03367     ok = JS_TRUE;
03368     while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
03369         if ((flags & XSF_IGNORE_COMMENTS) &&
03370             kid->xml_class == JSXML_CLASS_COMMENT) {
03371             continue;
03372         }
03373         if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
03374             kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
03375             continue;
03376         }
03377         if ((flags & XSF_IGNORE_WHITESPACE) &&
03378             (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
03379             continue;
03380         }
03381         kid2 = DeepCopyInLRS(cx, kid, flags);
03382         if (!kid2) {
03383             to->length = j;
03384             ok = JS_FALSE;
03385             break;
03386         }
03387 
03388         if ((flags & XSF_IGNORE_WHITESPACE) &&
03389             n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
03390             str = ChompXMLWhitespace(cx, kid2->xml_value);
03391             if (!str) {
03392                 to->length = j;
03393                 ok = JS_FALSE;
03394                 break;
03395             }
03396             kid2->xml_value = str;
03397         }
03398 
03399         XMLARRAY_SET_MEMBER(to, j, kid2);
03400         ++j;
03401         if (parent->xml_class != JSXML_CLASS_LIST)
03402             kid2->parent = parent;
03403     }
03404     XMLArrayCursorFinish(&cursor);
03405     if (!ok)
03406         return JS_FALSE;
03407 
03408     if (j < n)
03409         XMLArrayTrim(to);
03410     return JS_TRUE;
03411 }
03412 
03413 static JSXML *
03414 DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
03415 {
03416     JSXML *copy;
03417     JSXMLQName *qn;
03418     JSBool ok;
03419     uint32 i, n;
03420     JSXMLNamespace *ns, *ns2;
03421 
03422     /* Our caller must be protecting newborn objects. */
03423     JS_ASSERT(cx->localRootStack);
03424 
03425     copy = js_NewXML(cx, xml->xml_class);
03426     if (!copy)
03427         return NULL;
03428     qn = xml->name;
03429     if (qn) {
03430         qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName);
03431         if (!qn) {
03432             ok = JS_FALSE;
03433             goto out;
03434         }
03435     }
03436     copy->name = qn;
03437     copy->xml_flags = xml->xml_flags;
03438 
03439     if (JSXML_HAS_VALUE(xml)) {
03440         copy->xml_value = xml->xml_value;
03441         ok = JS_TRUE;
03442     } else {
03443         ok = DeepCopySetInLRS(cx, &xml->xml_kids, &copy->xml_kids, copy, flags);
03444         if (!ok)
03445             goto out;
03446 
03447         if (xml->xml_class == JSXML_CLASS_LIST) {
03448             copy->xml_target = xml->xml_target;
03449             copy->xml_targetprop = xml->xml_targetprop;
03450         } else {
03451             n = xml->xml_namespaces.length;
03452             ok = XMLArraySetCapacity(cx, &copy->xml_namespaces, n);
03453             if (!ok)
03454                 goto out;
03455             for (i = 0; i < n; i++) {
03456                 ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
03457                 if (!ns)
03458                     continue;
03459                 ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared);
03460                 if (!ns2) {
03461                     copy->xml_namespaces.length = i;
03462                     ok = JS_FALSE;
03463                     goto out;
03464                 }
03465                 XMLARRAY_SET_MEMBER(&copy->xml_namespaces, i, ns2);
03466             }
03467 
03468             ok = DeepCopySetInLRS(cx, &xml->xml_attrs, &copy->xml_attrs, copy,
03469                                   0);
03470             if (!ok)
03471                 goto out;
03472         }
03473     }
03474 
03475 out:
03476     if (!ok)
03477         return NULL;
03478     return copy;
03479 }
03480 
03481 static void
03482 ReportBadXMLName(JSContext *cx, jsval id)
03483 {
03484     JSString *name;
03485 
03486     name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL);
03487     if (name) {
03488         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
03489                              JSMSG_BAD_XML_NAME,
03490                              JS_GetStringBytes(name));
03491     }
03492 }
03493 
03494 /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
03495 static JSBool
03496 DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp)
03497 {
03498     uint32 index;
03499     JSXML *kid;
03500 
03501     if (!js_IdIsIndex(id, &index)) {
03502         ReportBadXMLName(cx, id);
03503         return JS_FALSE;
03504     }
03505 
03506     if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
03507         kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
03508         if (kid)
03509             kid->parent = NULL;
03510         XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
03511     }
03512 
03513     *vp = JSVAL_TRUE;
03514     return JS_TRUE;
03515 }
03516 
03517 typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml);
03518 
03519 static JSBool
03520 MatchAttrName(JSXMLQName *nameqn, JSXML *attr)
03521 {
03522     JSXMLQName *attrqn = attr->name;
03523 
03524     return (IS_STAR(nameqn->localName) ||
03525             js_EqualStrings(attrqn->localName, nameqn->localName)) &&
03526            (!nameqn->uri ||
03527             js_EqualStrings(attrqn->uri, nameqn->uri));
03528 }
03529 
03530 static JSBool
03531 MatchElemName(JSXMLQName *nameqn, JSXML *elem)
03532 {
03533     return (IS_STAR(nameqn->localName) ||
03534             (elem->xml_class == JSXML_CLASS_ELEMENT &&
03535              js_EqualStrings(elem->name->localName, nameqn->localName))) &&
03536            (!nameqn->uri ||
03537             (elem->xml_class == JSXML_CLASS_ELEMENT &&
03538              js_EqualStrings(elem->name->uri, nameqn->uri)));
03539 }
03540 
03541 /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
03542 static JSBool
03543 DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list)
03544 {
03545     uint32 i, n;
03546     JSXML *attr, *kid;
03547 
03548     if (xml->xml_class == JSXML_CLASS_ELEMENT &&
03549         OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) {
03550         for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
03551             attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
03552             if (attr && MatchAttrName(nameqn, attr)) {
03553                 if (!Append(cx, list, attr))
03554                     return JS_FALSE;
03555             }
03556         }
03557     }
03558 
03559     for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
03560         kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
03561         if (!kid)
03562             continue;
03563         if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass &&
03564             MatchElemName(nameqn, kid)) {
03565             if (!Append(cx, list, kid))
03566                 return JS_FALSE;
03567         }
03568         if (!DescendantsHelper(cx, kid, nameqn, list))
03569             return JS_FALSE;
03570     }
03571     return JS_TRUE;
03572 }
03573 
03574 static JSXML *
03575 Descendants(JSContext *cx, JSXML *xml, jsval id)
03576 {
03577     jsid funid;
03578     JSXMLQName *nameqn;
03579     JSObject *listobj;
03580     JSXML *list, *kid;
03581     uint32 i, n;
03582     JSBool ok;
03583 
03584     nameqn = ToXMLName(cx, id, &funid);
03585     if (!nameqn)
03586         return NULL;
03587 
03588     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
03589     if (!listobj)
03590         return NULL;
03591     list = (JSXML *) JS_GetPrivate(cx, listobj);
03592     if (funid)
03593         return list;
03594 
03595     /*
03596      * Protect nameqn's object and strings from GC by linking list to it
03597      * temporarily.  The cx->newborn[GCX_OBJECT] GC root protects listobj,
03598      * which protects list.  Any other object allocations occuring beneath
03599      * DescendantsHelper use local roots.
03600      */
03601     list->name = nameqn;
03602     if (!js_EnterLocalRootScope(cx))
03603         return NULL;
03604     if (xml->xml_class == JSXML_CLASS_LIST) {
03605         ok = JS_TRUE;
03606         for (i = 0, n = xml->xml_kids.length; i < n; i++) {
03607             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
03608             if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
03609                 ok = DescendantsHelper(cx, kid, nameqn, list);
03610                 if (!ok)
03611                     break;
03612             }
03613         }
03614     } else {
03615         ok = DescendantsHelper(cx, xml, nameqn, list);
03616     }
03617     js_LeaveLocalRootScopeWithResult(cx, (jsval) list);
03618     if (!ok)
03619         return NULL;
03620     list->name = NULL;
03621     return list;
03622 }
03623 
03624 static JSBool
03625 xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
03626 
03627 /* Recursive (JSXML *) parameterized version of Equals. */
03628 static JSBool
03629 XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
03630 {
03631     JSXMLQName *qn, *vqn;
03632     uint32 i, j, n;
03633     JSXMLArrayCursor cursor, vcursor;
03634     JSXML *kid, *vkid, *attr, *vattr;
03635     JSBool ok;
03636     JSObject *xobj, *vobj;
03637 
03638 retry:
03639     if (xml->xml_class != vxml->xml_class) {
03640         if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
03641             xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
03642             if (xml)
03643                 goto retry;
03644         }
03645         if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
03646             vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
03647             if (vxml)
03648                 goto retry;
03649         }
03650         *bp = JS_FALSE;
03651         return JS_TRUE;
03652     }
03653 
03654     qn = xml->name;
03655     vqn = vxml->name;
03656     if (qn) {
03657         *bp = vqn &&
03658               js_EqualStrings(qn->localName, vqn->localName) &&
03659               js_EqualStrings(qn->uri, vqn->uri);
03660     } else {
03661         *bp = vqn == NULL;
03662     }
03663     if (!*bp)
03664         return JS_TRUE;
03665 
03666     if (JSXML_HAS_VALUE(xml)) {
03667         *bp = js_EqualStrings(xml->xml_value, vxml->xml_value);
03668     } else if (xml->xml_kids.length != vxml->xml_kids.length) {
03669         *bp = JS_FALSE;
03670     } else {
03671         XMLArrayCursorInit(&cursor, &xml->xml_kids);
03672         XMLArrayCursorInit(&vcursor, &vxml->xml_kids);
03673         for (;;) {
03674             kid = (JSXML *) XMLArrayCursorNext(&cursor);
03675             vkid = (JSXML *) XMLArrayCursorNext(&vcursor);
03676             if (!kid || !vkid) {
03677                 *bp = !kid && !vkid;
03678                 ok = JS_TRUE;
03679                 break;
03680             }
03681             xobj = js_GetXMLObject(cx, kid);
03682             vobj = js_GetXMLObject(cx, vkid);
03683             ok = xobj && vobj &&
03684                  xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp);
03685             if (!ok || !*bp)
03686                 break;
03687         }
03688         XMLArrayCursorFinish(&vcursor);
03689         XMLArrayCursorFinish(&cursor);
03690         if (!ok)
03691             return JS_FALSE;
03692 
03693         if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
03694             n = xml->xml_attrs.length;
03695             if (n != vxml->xml_attrs.length)
03696                 *bp = JS_FALSE;
03697             for (i = 0; *bp && i < n; i++) {
03698                 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
03699                 if (!attr)
03700                     continue;
03701                 j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
03702                 if (j == XML_NOT_FOUND) {
03703                     *bp = JS_FALSE;
03704                     break;
03705                 }
03706                 vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
03707                 if (!vattr)
03708                     continue;
03709                 *bp = js_EqualStrings(attr->xml_value, vattr->xml_value);
03710             }
03711         }
03712     }
03713 
03714     return JS_TRUE;
03715 }
03716 
03717 /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
03718 static JSBool
03719 Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
03720 {
03721     JSObject *vobj;
03722     JSXML *vxml;
03723 
03724     if (JSVAL_IS_PRIMITIVE(v)) {
03725         *bp = JS_FALSE;
03726         if (xml->xml_class == JSXML_CLASS_LIST) {
03727             if (xml->xml_kids.length == 1) {
03728                 vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
03729                 if (!vxml)
03730                     return JS_TRUE;
03731                 vobj = js_GetXMLObject(cx, vxml);
03732                 if (!vobj)
03733                     return JS_FALSE;
03734                 return js_XMLObjectOps.equality(cx, vobj, v, bp);
03735             }
03736             if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
03737                 *bp = JS_TRUE;
03738         }
03739     } else {
03740         vobj = JSVAL_TO_OBJECT(v);
03741         if (!OBJECT_IS_XML(cx, vobj)) {
03742             *bp = JS_FALSE;
03743         } else {
03744             vxml = (JSXML *) JS_GetPrivate(cx, vobj);
03745             if (!XMLEquals(cx, xml, vxml, bp))
03746                 return JS_FALSE;
03747         }
03748     }
03749     return JS_TRUE;
03750 }
03751 
03752 static JSBool
03753 CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
03754 {
03755     JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
03756 
03757     do {
03758         if (xml == kid) {
03759             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
03760                                  JSMSG_CYCLIC_VALUE, js_XML_str);
03761             return JS_FALSE;
03762         }
03763     } while ((xml = xml->parent) != NULL);
03764 
03765     return JS_TRUE;
03766 }
03767 
03768 /* ECMA-357 9.1.1.11 XML [[Insert]]. */
03769 static JSBool
03770 Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v)
03771 {
03772     uint32 j, n;
03773     JSXML *vxml, *kid;
03774     JSObject *vobj;
03775     JSString *str;
03776 
03777     if (!JSXML_HAS_KIDS(xml))
03778         return JS_TRUE;
03779 
03780     n = 1;
03781     vxml = NULL;
03782     if (!JSVAL_IS_PRIMITIVE(v)) {
03783         vobj = JSVAL_TO_OBJECT(v);
03784         if (OBJECT_IS_XML(cx, vobj)) {
03785             vxml = (JSXML *) JS_GetPrivate(cx, vobj);
03786             if (vxml->xml_class == JSXML_CLASS_LIST) {
03787                 n = vxml->xml_kids.length;
03788                 if (n == 0)
03789                     return JS_TRUE;
03790                 for (j = 0; j < n; j++) {
03791                     kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
03792                     if (!kid)
03793                         continue;
03794                     if (!CheckCycle(cx, xml, kid))
03795                         return JS_FALSE;
03796                 }
03797             } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
03798                 /* OPTION: enforce that descendants have superset namespaces. */
03799                 if (!CheckCycle(cx, xml, vxml))
03800                     return JS_FALSE;
03801             }
03802         }
03803     }
03804     if (!vxml) {
03805         str = js_ValueToString(cx, v);
03806         if (!str)
03807             return JS_FALSE;
03808 
03809         vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
03810         if (!vxml)
03811             return JS_FALSE;
03812         vxml->xml_value = str;
03813     }
03814 
03815     if (i > xml->xml_kids.length)
03816         i = xml->xml_kids.length;
03817 
03818     if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
03819         return JS_FALSE;
03820 
03821     if (vxml->xml_class == JSXML_CLASS_LIST) {
03822         for (j = 0; j < n; j++) {
03823             kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
03824             if (!kid)
03825                 continue;
03826             kid->parent = xml;
03827             XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
03828 
03829             /* OPTION: enforce that descendants have superset namespaces. */
03830         }
03831     } else {
03832         vxml->parent = xml;
03833         XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
03834     }
03835     return JS_TRUE;
03836 }
03837 
03838 static JSBool
03839 IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp)
03840 {
03841     JSString *str;
03842 
03843     if (index <= JSVAL_INT_MAX) {
03844         *idvp = INT_TO_JSVAL(index);
03845     } else {
03846         str = js_NumberToString(cx, (jsdouble) index);
03847         if (!str)
03848             return JS_FALSE;
03849         *idvp = STRING_TO_JSVAL(str);
03850     }
03851     return JS_TRUE;
03852 }
03853 
03854 /* ECMA-357 9.1.1.12 XML [[Replace]]. */
03855 static JSBool
03856 Replace(JSContext *cx, JSXML *xml, jsval id, jsval v)
03857 {
03858     uint32 i, n;
03859     JSXML *vxml, *kid;
03860     JSObject *vobj;
03861     jsval junk;
03862     JSString *str;
03863 
03864     if (!JSXML_HAS_KIDS(xml))
03865         return JS_TRUE;
03866 
03867     if (!js_IdIsIndex(id, &i)) {
03868         ReportBadXMLName(cx, id);
03869         return JS_FALSE;
03870     }
03871 
03872     /*
03873      * 9.1.1.12
03874      * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
03875      * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
03876      */
03877     n = xml->xml_kids.length;
03878     if (i >= n) {
03879         if (!IndexToIdVal(cx, n, &id))
03880             return JS_FALSE;
03881         i = n;
03882     }
03883 
03884     vxml = NULL;
03885     if (!JSVAL_IS_PRIMITIVE(v)) {
03886         vobj = JSVAL_TO_OBJECT(v);
03887         if (OBJECT_IS_XML(cx, vobj))
03888             vxml = (JSXML *) JS_GetPrivate(cx, vobj);
03889     }
03890 
03891     switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) {
03892       case JSXML_CLASS_ELEMENT:
03893         /* OPTION: enforce that descendants have superset namespaces. */
03894         if (!CheckCycle(cx, xml, vxml))
03895             return JS_FALSE;
03896       case JSXML_CLASS_COMMENT:
03897       case JSXML_CLASS_PROCESSING_INSTRUCTION:
03898       case JSXML_CLASS_TEXT:
03899         goto do_replace;
03900 
03901       case JSXML_CLASS_LIST:
03902         if (i < n && !DeleteByIndex(cx, xml, id, &junk))
03903             return JS_FALSE;
03904         if (!Insert(cx, xml, i, v))
03905             return JS_FALSE;
03906         break;
03907 
03908       default:
03909         str = js_ValueToString(cx, v);
03910         if (!str)
03911             return JS_FALSE;
03912 
03913         vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
03914         if (!vxml)
03915             return JS_FALSE;
03916         vxml->xml_value = str;
03917 
03918       do_replace:
03919         vxml->parent = xml;
03920         if (i < n) {
03921             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
03922             if (kid)
03923                 kid->parent = NULL;
03924         }
03925         if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
03926             return JS_FALSE;
03927         break;
03928     }
03929 
03930     return JS_TRUE;
03931 }
03932 
03933 /* Forward declared -- its implementation uses other statics that call it. */
03934 static JSBool
03935 ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
03936 
03937 /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */
03938 static JSBool
03939 DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
03940 {
03941     JSXML *xml, *kid, *parent;
03942     JSBool isIndex;
03943     JSXMLArray *array;
03944     uint32 length, index, kidIndex, deleteCount;
03945     JSXMLQName *nameqn;
03946     jsid funid;
03947     JSObject *nameobj, *kidobj;
03948     JSXMLNameMatcher matcher;
03949 
03950     xml = (JSXML *) JS_GetPrivate(cx, obj);
03951     isIndex = js_IdIsIndex(id, &index);
03952     if (JSXML_HAS_KIDS(xml)) {
03953         array = &xml->xml_kids;
03954         length = array->length;
03955     } else {
03956         array = NULL;
03957         length = 0;
03958     }
03959 
03960     if (xml->xml_class == JSXML_CLASS_LIST) {
03961         /* ECMA-357 9.2.1.3. */
03962         if (isIndex && index < length) {
03963             kid = XMLARRAY_MEMBER(array, index, JSXML);
03964             if (!kid)
03965                 goto out;
03966             parent = kid->parent;
03967             if (parent) {
03968                 JS_ASSERT(parent != xml);
03969                 JS_ASSERT(JSXML_HAS_KIDS(parent));
03970 
03971                 if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
03972                     nameqn = kid->name;
03973                     nameobj = js_GetAttributeNameObject(cx, nameqn);
03974                     if (!nameobj || !js_GetXMLObject(cx, parent))
03975                         return JS_FALSE;
03976 
03977                     id = OBJECT_TO_JSVAL(nameobj);
03978                     if (!DeleteProperty(cx, parent->object, id, vp))
03979                         return JS_FALSE;
03980                 } else {
03981                     kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
03982                                                     NULL);
03983                     JS_ASSERT(kidIndex != XML_NOT_FOUND);
03984                     if (!IndexToIdVal(cx, kidIndex, &id))
03985                         return JS_FALSE;
03986                     if (!DeleteByIndex(cx, parent, id, vp))
03987                         return JS_FALSE;
03988                 }
03989             }
03990 
03991             XMLArrayDelete(cx, array, index, JS_TRUE);
03992         } else {
03993             for (index = 0; index < length; index++) {
03994                 kid = XMLARRAY_MEMBER(array, index, JSXML);
03995                 if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
03996                     kidobj = js_GetXMLObject(cx, kid);
03997                     if (!kidobj || !DeleteProperty(cx, kidobj, id, vp))
03998                         return JS_FALSE;
03999                 }
04000             }
04001         }
04002     } else {
04003         /* ECMA-357 9.1.1.3. */
04004         if (isIndex) {
04005             /* See NOTE in spec: this variation is reserved for future use. */
04006             ReportBadXMLName(cx, id);
04007             return JS_FALSE;
04008         }
04009 
04010         nameqn = ToXMLName(cx, id, &funid);
04011         if (!nameqn)
04012             return JS_FALSE;
04013         if (funid)
04014             goto out;
04015         nameobj = nameqn->object;
04016 
04017         if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
04018             if (xml->xml_class != JSXML_CLASS_ELEMENT)
04019                 goto out;
04020             array = &xml->xml_attrs;
04021             length = array->length;
04022             matcher = MatchAttrName;
04023         } else {
04024             matcher = MatchElemName;
04025         }
04026         if (length != 0) {
04027             deleteCount = 0;
04028             for (index = 0; index < length; index++) {
04029                 kid = XMLARRAY_MEMBER(array, index, JSXML);
04030                 if (kid && matcher(nameqn, kid)) {
04031                     kid->parent = NULL;
04032                     XMLArrayDelete(cx, array, index, JS_FALSE);
04033                     ++deleteCount;
04034                 } else if (deleteCount != 0) {
04035                     XMLARRAY_SET_MEMBER(array,
04036                                         index - deleteCount,
04037                                         array->vector[index]);
04038                 }
04039             }
04040             array->length -= deleteCount;
04041         }
04042     }
04043 
04044 out:
04045     *vp = JSVAL_TRUE;
04046     return JS_TRUE;
04047 }
04048 
04049 static JSBool
04050 SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
04051 {
04052     JSXMLArray *nsarray;
04053     uint32 i, n;
04054     JSXMLNamespace *ns;
04055 
04056     nsarray = &xml->xml_namespaces;
04057     while ((xml = xml->parent) != NULL) {
04058         for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
04059             ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
04060             if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
04061                 if (!XMLARRAY_APPEND(cx, nsarray, ns))
04062                     return JS_FALSE;
04063             }
04064         }
04065     }
04066     return JS_TRUE;
04067 }
04068 
04069 static JSBool
04070 GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn,
04071                  JSBool attributes, JSXML *list)
04072 {
04073     JSXMLArray *array;
04074     JSXMLNameMatcher matcher;
04075     JSXMLArrayCursor cursor;
04076     JSXML *kid;
04077     JSBool ok;
04078 
04079     if (!JSXML_HAS_KIDS(xml))
04080         return JS_TRUE;
04081 
04082     if (attributes) {
04083         array = &xml->xml_attrs;
04084         matcher = MatchAttrName;
04085     } else {
04086         array = &xml->xml_kids;
04087         matcher = MatchElemName;
04088     }
04089 
04090     XMLArrayCursorInit(&cursor, array);
04091     while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
04092         if (matcher(nameqn, kid)) {
04093             if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) {
04094                 ok = SyncInScopeNamespaces(cx, kid);
04095                 if (!ok)
04096                     goto out;
04097             }
04098             ok = Append(cx, list, kid);
04099             if (!ok)
04100                 goto out;
04101         }
04102     }
04103     ok = JS_TRUE;
04104 
04105   out:
04106     XMLArrayCursorFinish(&cursor);
04107     return ok;
04108 }
04109 
04110 /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
04111 static JSBool
04112 GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
04113 {
04114     JSXML *xml, *list, *kid;
04115     uint32 index;
04116     JSObject *kidobj, *listobj;
04117     JSXMLQName *nameqn;
04118     jsid funid;
04119     jsval roots[2];
04120     JSTempValueRooter tvr;
04121     JSBool attributes;
04122     JSXMLArrayCursor cursor;
04123 
04124     xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
04125     if (!xml)
04126         return JS_TRUE;
04127 
04128     if (js_IdIsIndex(id, &index)) {
04129         if (xml->xml_class != JSXML_CLASS_LIST) {
04130             *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
04131         } else {
04132             /*
04133              * ECMA-357 9.2.1.1 starts here.
04134              *
04135              * Erratum: 9.2 is not completely clear that indexed properties
04136              * correspond to kids, but that's what it seems to say, and it's
04137              * what any sane user would want.
04138              */
04139             if (index < xml->xml_kids.length) {
04140                 kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
04141                 if (!kid) {
04142                     *vp = JSVAL_VOID;
04143                     return JS_TRUE;
04144                 }
04145                 kidobj = js_GetXMLObject(cx, kid);
04146                 if (!kidobj)
04147                     return JS_FALSE;
04148 
04149                 *vp = OBJECT_TO_JSVAL(kidobj);
04150             } else {
04151                 *vp = JSVAL_VOID;
04152             }
04153         }
04154         return JS_TRUE;
04155     }
04156 
04157     /*
04158      * ECMA-357 9.2.1.1/9.1.1.1 qname case.
04159      */
04160     nameqn = ToXMLName(cx, id, &funid);
04161     if (!nameqn)
04162         return JS_FALSE;
04163     if (funid)
04164         return js_GetXMLFunction(cx, obj, funid, vp);
04165 
04166     roots[0] = OBJECT_TO_JSVAL(nameqn->object);
04167     JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr);
04168 
04169     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
04170     if (listobj) {
04171         roots[1] = OBJECT_TO_JSVAL(listobj);
04172         tvr.count++;
04173 
04174         list = (JSXML *) JS_GetPrivate(cx, listobj);
04175         attributes = (OBJ_GET_CLASS(cx, nameqn->object) ==
04176                       &js_AttributeNameClass);
04177 
04178         if (xml->xml_class == JSXML_CLASS_LIST) {
04179             XMLArrayCursorInit(&cursor, &xml->xml_kids);
04180             while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
04181                 if (kid->xml_class == JSXML_CLASS_ELEMENT &&
04182                     !GetNamedProperty(cx, kid, nameqn, attributes, list)) {
04183                     listobj = NULL;
04184                     break;
04185                 }
04186             }
04187             XMLArrayCursorFinish(&cursor);
04188         } else {
04189             if (!GetNamedProperty(cx, xml, nameqn, attributes, list))
04190                 listobj = NULL;
04191         }
04192 
04193         /*
04194          * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given
04195          * list's [[TargetProperty]] to the property that is being appended.
04196          * This means that any use of the internal [[Get]] property returns
04197          * a list which, when used by e.g. [[Insert]] duplicates the last
04198          * element matched by id.
04199          * See bug 336921.
04200          */
04201         list->xml_target = xml;
04202         list->xml_targetprop = nameqn;
04203         *vp = OBJECT_TO_JSVAL(listobj);
04204     }
04205 
04206     JS_POP_TEMP_ROOT(cx, &tvr);
04207     return listobj != NULL;
04208 }
04209 
04210 static JSXML *
04211 CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
04212 {
04213     JS_ASSERT(xml->object != obj);
04214 
04215     xml = DeepCopy(cx, xml, obj, 0);
04216     if (!xml)
04217         return NULL;
04218 
04219     JS_ASSERT(xml->object == obj);
04220     return xml;
04221 }
04222 
04223 #define CHECK_COPY_ON_WRITE(cx,xml,obj)                                       \
04224     (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
04225 
04226 static JSString *
04227 KidToString(JSContext *cx, JSXML *xml, uint32 index)
04228 {
04229     JSXML *kid;
04230     JSObject *kidobj;
04231 
04232     kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
04233     if (!kid)
04234         return cx->runtime->emptyString;
04235     kidobj = js_GetXMLObject(cx, kid);
04236     if (!kidobj)
04237         return NULL;
04238     return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj));
04239 }
04240 
04241 /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
04242 static JSBool
04243 PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
04244 {
04245     JSBool ok, primitiveAssign;
04246     enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
04247     jsval roots[3];
04248     JSTempValueRooter tvr;
04249     JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
04250     JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
04251     JSXMLQName *targetprop, *nameqn, *attrqn;
04252     uint32 index, i, j, k, n, q;
04253     jsval attrval, nsval, junk;
04254     jsid funid;
04255     JSString *left, *right, *space;
04256     JSXMLNamespace *ns;
04257 
04258     xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
04259     if (!xml)
04260         return JS_TRUE;
04261 
04262     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
04263     if (!xml)
04264         return JS_FALSE;
04265 
04266     /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
04267     vxml = NULL;
04268     if (!JSVAL_IS_PRIMITIVE(*vp)) {
04269         vobj = JSVAL_TO_OBJECT(*vp);
04270         if (OBJECT_IS_XML(cx, vobj))
04271             vxml = (JSXML *) JS_GetPrivate(cx, vobj);
04272     }
04273 
04274     /* Control flow after here must exit via label out. */
04275     ok = js_EnterLocalRootScope(cx);
04276     if (!ok)
04277         return JS_FALSE;
04278     roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
04279     roots[ID_ROOT] = id;
04280     roots[VAL_ROOT] = *vp;
04281     JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr);
04282 
04283     if (xml->xml_class == JSXML_CLASS_LIST) {
04284         /* ECMA-357 9.2.1.2. */
04285         if (js_IdIsIndex(id, &index)) {
04286             /* Step 1 sets i to the property index. */
04287             i = index;
04288 
04289             /* 2(a-b). */
04290             if (xml->xml_target) {
04291                 ok = ResolveValue(cx, xml->xml_target, &rxml);
04292                 if (!ok)
04293                     goto out;
04294                 if (!rxml)
04295                     goto out;
04296                 JS_ASSERT(rxml->object);
04297             } else {
04298                 rxml = NULL;
04299             }
04300 
04301             /* 2(c). */
04302             if (index >= xml->xml_kids.length) {
04303                 /* 2(c)(i). */
04304                 if (rxml) {
04305                     if (rxml->xml_class == JSXML_CLASS_LIST) {
04306                         if (rxml->xml_kids.length != 1)
04307                             goto out;
04308                         rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
04309                         if (!rxml)
04310                             goto out;
04311                         ok = js_GetXMLObject(cx, rxml) != NULL;
04312                         if (!ok)
04313                             goto out;
04314                     }
04315 
04316                     /*
04317                      * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
04318                      * _y.[[Parent]] = r_ where _r_ is the result of
04319                      * [[ResolveValue]] called on _x.[[TargetObject]] in
04320                      * 2(a)(i).  This can result in text parenting text:
04321                      *
04322                      *    var MYXML = new XML();
04323                      *    MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
04324                      *
04325                      * (testcase from Werner Sharp <wsharp@macromedia.com>).
04326                      *
04327                      * To match insertChildAfter, insertChildBefore,
04328                      * prependChild, and setChildren, we should silently
04329                      * do nothing in this case.
04330                      */
04331                     if (!JSXML_HAS_KIDS(rxml))
04332                         goto out;
04333                 }
04334 
04335                 /* 2(c)(ii) is distributed below as several js_NewXML calls. */
04336                 targetprop = xml->xml_targetprop;
04337                 if (!targetprop || IS_STAR(targetprop->localName)) {
04338                     /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
04339                     kid = js_NewXML(cx, JSXML_CLASS_TEXT);
04340                     if (!kid)
04341                         goto bad;
04342                 } else {
04343                     nameobj = js_GetXMLQNameObject(cx, targetprop);
04344                     if (!nameobj)
04345                         goto bad;
04346                     if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
04347                         /*
04348                          * 2(c)(iii)(1-3).
04349                          * Note that rxml can't be null here, because target
04350                          * and targetprop are non-null.
04351                          */
04352                         ok = GetProperty(cx, rxml->object, id, &attrval);
04353                         if (!ok)
04354                             goto out;
04355                         if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
04356                             goto out;
04357                         attrobj = JSVAL_TO_OBJECT(attrval);
04358                         attr = (JSXML *) JS_GetPrivate(cx, attrobj);
04359                         if (JSXML_LENGTH(attr) != 0)
04360                             goto out;
04361 
04362                         kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
04363                     } else {
04364                         /* 2(c)(v). */
04365                         kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
04366                     }
04367                     if (!kid)
04368                         goto bad;
04369 
04370                     /* An important bit of 2(c)(ii). */
04371                     kid->name = targetprop;
04372                 }
04373 
04374                 /* Final important bit of 2(c)(ii). */
04375                 kid->parent = rxml;
04376 
04377                 /* 2(c)(vi-vii). */
04378                 i = xml->xml_kids.length;
04379                 if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
04380                     /*
04381                      * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
04382                      * y.[[Parent]] is here called kid->parent, which we know
04383                      * from 2(c)(ii) is _r_, here called rxml.  So let's just
04384                      * test that!  Erratum, the spec should be simpler here.
04385                      */
04386                     if (rxml) {
04387                         JS_ASSERT(JSXML_HAS_KIDS(rxml));
04388                         n = rxml->xml_kids.length;
04389                         j = n - 1;
04390                         if (n != 0 && i != 0) {
04391                             for (n = j, j = 0; j < n; j++) {
04392                                 if (rxml->xml_kids.vector[j] ==
04393                                     xml->xml_kids.vector[i-1]) {
04394                                     break;
04395                                 }
04396                             }
04397                         }
04398 
04399                         kidobj = js_GetXMLObject(cx, kid);
04400                         if (!kidobj)
04401                             goto bad;
04402                         ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
04403                         if (!ok)
04404                             goto out;
04405                     }
04406 
04407                     /*
04408                      * 2(c)(vii)(2-3).
04409                      * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
04410                      * typo for [[TargetProperty]].
04411                      */
04412                     if (vxml) {
04413                         kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
04414                                     ? vxml->xml_targetprop
04415                                     : vxml->name;
04416                     }
04417                 }
04418 
04419                 /* 2(c)(viii). */
04420                 ok = Append(cx, xml, kid);
04421                 if (!ok)
04422                     goto out;
04423             }
04424 
04425             /* 2(d). */
04426             if (!vxml ||
04427                 vxml->xml_class == JSXML_CLASS_TEXT ||
04428                 vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
04429                 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
04430                 if (!ok)
04431                     goto out;
04432                 roots[VAL_ROOT] = *vp;
04433             }
04434 
04435             /* 2(e). */
04436             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
04437             if (!kid)
04438                 goto out;
04439             parent = kid->parent;
04440             if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
04441                 nameobj = js_GetAttributeNameObject(cx, kid->name);
04442                 if (!nameobj)
04443                     goto bad;
04444                 id = OBJECT_TO_JSVAL(nameobj);
04445 
04446                 if (parent) {
04447                     /* 2(e)(i). */
04448                     parentobj = js_GetXMLObject(cx, parent);
04449                     if (!parentobj)
04450                         goto bad;
04451                     ok = PutProperty(cx, parentobj, id, vp);
04452                     if (!ok)
04453                         goto out;
04454 
04455                     /* 2(e)(ii). */
04456                     ok = GetProperty(cx, parentobj, id, vp);
04457                     if (!ok)
04458                         goto out;
04459                     attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
04460 
04461                     /* 2(e)(iii). */
04462                     xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
04463                 }
04464             }
04465 
04466             /* 2(f). */
04467             else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
04468                 /* 2(f)(i) Create a shallow copy _c_ of _V_. */
04469                 copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
04470                 if (!copyobj)
04471                     goto bad;
04472                 copy = (JSXML *) JS_GetPrivate(cx, copyobj);
04473                 n = vxml->xml_kids.length;
04474                 ok = XMLArraySetCapacity(cx, &copy->xml_kids, n);
04475                 if (!ok)
04476                     goto out;
04477                 for (k = 0; k < n; k++) {
04478                     kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML);
04479                     XMLARRAY_SET_MEMBER(&copy->xml_kids, k, kid2);
04480                 }
04481 
04482                 JS_ASSERT(parent != xml);
04483                 if (parent) {
04484                     q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
04485                     JS_ASSERT(q != XML_NOT_FOUND);
04486 
04487                     ok = IndexToIdVal(cx, q, &id);
04488                     if (!ok)
04489                         goto out;
04490                     ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj));
04491                     if (!ok)
04492                         goto out;
04493 
04494 #ifdef DEBUG
04495                     /* Erratum: this loop in the spec is useless. */
04496                     for (j = 0, n = copy->xml_kids.length; j < n; j++) {
04497                         kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
04498                         JS_ASSERT(XMLARRAY_MEMBER(&copy->xml_kids, j, JSXML)
04499                                   == kid2);
04500                     }
04501 #endif
04502                 }
04503 
04504                 /*
04505                  * 2(f)(iv-vi).
04506                  * Erratum: notice the unhandled zero-length V basis case and
04507                  * the off-by-one errors for the n != 0 cases in the spec.
04508                  */
04509                 if (n == 0) {
04510                     XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
04511                 } else {
04512                     ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
04513                     if (!ok)
04514                         goto out;
04515 
04516                     for (j = 0; j < n; j++)
04517                         xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
04518                 }
04519             }
04520 
04521             /* 2(g). */
04522             else if (vxml || JSXML_HAS_VALUE(kid)) {
04523                 if (parent) {
04524                     q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL);
04525                     JS_ASSERT(q != XML_NOT_FOUND);
04526 
04527                     ok = IndexToIdVal(cx, q, &id);
04528                     if (!ok)
04529                         goto out;
04530                     ok = Replace(cx, parent, id, *vp);
04531                     if (!ok)
04532                         goto out;
04533 
04534                     vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
04535                     if (!vxml)
04536                         goto out;
04537                     roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
04538                 }
04539 
04540                 /*
04541                  * 2(g)(iii).
04542                  * Erratum: _V_ may not be of type XML, but all index-named
04543                  * properties _x[i]_ in an XMLList _x_ must be of type XML,
04544                  * according to 9.2.1.1 Overview and other places in the spec.
04545                  *
04546                  * Thanks to 2(d), we know _V_ (*vp here) is either a string
04547                  * or an XML/XMLList object.  If *vp is a string, call ToXML
04548                  * on it to satisfy the constraint.
04549                  */
04550                 if (!vxml) {
04551                     JS_ASSERT(JSVAL_IS_STRING(*vp));
04552                     vobj = ToXML(cx, *vp);
04553                     if (!vobj)
04554                         goto bad;
04555                     roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
04556                     vxml = (JSXML *) JS_GetPrivate(cx, vobj);
04557                 }
04558                 XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
04559             }
04560 
04561             /* 2(h). */
04562             else {
04563                 kidobj = js_GetXMLObject(cx, kid);
04564                 if (!kidobj)
04565                     goto bad;
04566                 id = ATOM_KEY(cx->runtime->atomState.starAtom);
04567                 ok = PutProperty(cx, kidobj, id, vp);
04568                 if (!ok)
04569                     goto out;
04570             }
04571         } else {
04572             /*
04573              * 3.
04574              * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
04575              * or an r with r.[[Length]] != 1, throw TypeError.
04576              */
04577             n = JSXML_LENGTH(xml);
04578             if (n > 1)
04579                 goto type_error;
04580             if (n == 0) {
04581                 ok = ResolveValue(cx, xml, &rxml);
04582                 if (!ok)
04583                     goto out;
04584                 if (!rxml || JSXML_LENGTH(rxml) != 1)
04585                     goto type_error;
04586                 ok = Append(cx, xml, rxml);
04587                 if (!ok)
04588                     goto out;
04589             }
04590             JS_ASSERT(JSXML_LENGTH(xml) == 1);
04591             kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
04592             if (!kid)
04593                 goto out;
04594             kidobj = js_GetXMLObject(cx, kid);
04595             if (!kidobj)
04596                 goto bad;
04597             ok = PutProperty(cx, kidobj, id, vp);
04598             if (!ok)
04599                 goto out;
04600         }
04601     } else {
04602         /*
04603          * ECMA-357 9.1.1.2.
04604          * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
04605          * effort in ToString or [[DeepCopy]].
04606          */
04607         if (js_IdIsIndex(id, &index)) {
04608             /* See NOTE in spec: this variation is reserved for future use. */
04609             ReportBadXMLName(cx, id);
04610             goto bad;
04611         }
04612 
04613         nameqn = ToXMLName(cx, id, &funid);
04614         if (!nameqn)
04615             goto bad;
04616         if (funid) {
04617             ok = js_SetProperty(cx, obj, funid, vp);
04618             goto out;
04619         }
04620         nameobj = nameqn->object;
04621 
04622         if (JSXML_HAS_VALUE(xml))
04623             goto out;
04624 
04625         if (!vxml ||
04626             vxml->xml_class == JSXML_CLASS_TEXT ||
04627             vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
04628             ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
04629             if (!ok)
04630                 goto out;
04631         } else {
04632             rxml = DeepCopyInLRS(cx, vxml, 0);
04633             if (!rxml || !js_GetXMLObject(cx, rxml))
04634                 goto bad;
04635             vxml = rxml;
04636             *vp = OBJECT_TO_JSVAL(vxml->object);
04637         }
04638         roots[VAL_ROOT] = *vp;
04639 
04640         /*
04641          * 6.
04642          * Erratum: why is this done here, so early? use is way later....
04643          */
04644         ok = js_GetDefaultXMLNamespace(cx, &nsval);
04645         if (!ok)
04646             goto out;
04647 
04648         if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) {
04649             /* 7(a). */
04650             if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
04651                 goto out;
04652 
04653             /* 7(b-c). */
04654             if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
04655                 n = vxml->xml_kids.length;
04656                 if (n == 0) {
04657                     *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
04658                 } else {
04659                     left = KidToString(cx, vxml, 0);
04660                     if (!left)
04661                         goto bad;
04662 
04663                     space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom);
04664                     for (i = 1; i < n; i++) {
04665                         left = js_ConcatStrings(cx, left, space);
04666                         if (!left)
04667                             goto bad;
04668                         right = KidToString(cx, vxml, i);
04669                         if (!right)
04670                             goto bad;
04671                         left = js_ConcatStrings(cx, left, right);
04672                         if (!left)
04673                             goto bad;
04674                     }
04675 
04676                     roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
04677                 }
04678             } else {
04679                 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
04680                 if (!ok)
04681                     goto out;
04682                 roots[VAL_ROOT] = *vp;
04683             }
04684 
04685             /* 7(d-e). */
04686             match = NULL;
04687             for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
04688                 attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
04689                 if (!attr)
04690                     continue;
04691                 attrqn = attr->name;
04692                 if (js_EqualStrings(attrqn->localName, nameqn->localName) &&
04693                     (!nameqn->uri ||
04694                      js_EqualStrings(attrqn->uri, nameqn->uri))) {
04695                     if (!match) {
04696                         match = attr;
04697                     } else {
04698                         nameobj = js_GetAttributeNameObject(cx, attrqn);
04699                         if (!nameobj)
04700                             goto bad;
04701 
04702                         id = OBJECT_TO_JSVAL(nameobj);
04703                         ok = DeleteProperty(cx, obj, id, &junk);
04704                         if (!ok)
04705                             goto out;
04706                         --i;
04707                     }
04708                 }
04709             }
04710 
04711             /* 7(f). */
04712             attr = match;
04713             if (!attr) {
04714                 /* 7(f)(i-ii). */
04715                 if (!nameqn->uri) {
04716                     left = right = cx->runtime->emptyString;
04717                 } else {
04718                     left = nameqn->uri;
04719                     right = nameqn->prefix;
04720                 }
04721                 nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
04722                 if (!nameqn)
04723                     goto bad;
04724 
04725                 /* 7(f)(iii). */
04726                 attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
04727                 if (!attr)
04728                     goto bad;
04729                 attr->parent = xml;
04730                 attr->name = nameqn;
04731 
04732                 /* 7(f)(iv). */
04733                 ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
04734                 if (!ok)
04735                     goto out;
04736 
04737                 /* 7(f)(v-vi). */
04738                 ns = GetNamespace(cx, nameqn, NULL);
04739                 if (!ns)
04740                     goto bad;
04741                 ok = AddInScopeNamespace(cx, xml, ns);
04742                 if (!ok)
04743                     goto out;
04744             }
04745 
04746             /* 7(g). */
04747             attr->xml_value = JSVAL_TO_STRING(*vp);
04748             goto out;
04749         }
04750 
04751         /* 8-9. */
04752         if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
04753             !IS_STAR(nameqn->localName)) {
04754             goto out;
04755         }
04756 
04757         /* 10-11. */
04758         id = JSVAL_VOID;
04759         primitiveAssign = !vxml && !IS_STAR(nameqn->localName);
04760 
04761         /* 12. */
04762         k = n = xml->xml_kids.length;
04763         kid2 = NULL;
04764         while (k != 0) {
04765             --k;
04766             kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
04767             if (kid && MatchElemName(nameqn, kid)) {
04768                 if (!JSVAL_IS_VOID(id)) {
04769                     ok = DeleteByIndex(cx, xml, id, &junk);
04770                     if (!ok)
04771                         goto out;
04772                 }
04773                 ok = IndexToIdVal(cx, k, &id);
04774                 if (!ok)
04775                     goto out;
04776                 kid2 = kid;
04777             }
04778         }
04779 
04780         /*
04781          * Erratum: ECMA-357 specified child insertion inconsistently:
04782          * insertChildBefore and insertChildAfter insert an arbitrary XML
04783          * instance, and therefore can create cycles, but appendChild as
04784          * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
04785          * its argument.  But the "Semantics" in 13.4.4.3 do not include
04786          * any [[DeepCopy]] call.
04787          *
04788          * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
04789          * required adding cycle detection, and allowing duplicate kids to
04790          * be created (see comment 6 in the bug).  Allowing duplicate kid
04791          * references means the loop above will delete all but the lowest
04792          * indexed reference, and each [[DeleteByIndex]] nulls the kid's
04793          * parent.  Thus the need to restore parent here.  This is covered
04794          * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
04795          */
04796         if (kid2) {
04797             JS_ASSERT(kid2->parent == xml || !kid2->parent);
04798             if (!kid2->parent)
04799                 kid2->parent = xml;
04800         }
04801 
04802         /* 13. */
04803         if (JSVAL_IS_VOID(id)) {
04804             /* 13(a). */
04805             ok = IndexToIdVal(cx, n, &id);
04806             if (!ok)
04807                 goto out;
04808 
04809             /* 13(b). */
04810             if (primitiveAssign) {
04811                 if (!nameqn->uri) {
04812                     ns = (JSXMLNamespace *)
04813                          JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval));
04814                     left = ns->uri;
04815                     right = ns->prefix;
04816                 } else {
04817                     left = nameqn->uri;
04818                     right = nameqn->prefix;
04819                 }
04820                 nameqn = js_NewXMLQName(cx, left, right, nameqn->localName);
04821                 if (!nameqn)
04822                     goto bad;
04823 
04824                 /* 13(b)(iii). */
04825                 vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
04826                 if (!vobj)
04827                     goto bad;
04828                 vxml = (JSXML *) JS_GetPrivate(cx, vobj);
04829                 vxml->parent = xml;
04830                 vxml->name = nameqn;
04831 
04832                 /* 13(b)(iv-vi). */
04833                 ns = GetNamespace(cx, nameqn, NULL);
04834                 if (!ns)
04835                     goto bad;
04836                 ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj));
04837                 if (!ok)
04838                     goto out;
04839                 ok = AddInScopeNamespace(cx, vxml, ns);
04840                 if (!ok)
04841                     goto out;
04842             }
04843         }
04844 
04845         /* 14. */
04846         if (primitiveAssign) {
04847             JSXMLArrayCursor cursor;
04848 
04849             js_IdIsIndex(id, &index);
04850             XMLArrayCursorInit(&cursor, &xml->xml_kids);
04851             cursor.index = index;
04852             kid = (JSXML *) XMLArrayCursorItem(&cursor);
04853             if (JSXML_HAS_KIDS(kid)) {
04854                 XMLArrayFinish(cx, &kid->xml_kids);
04855                 ok = XMLArrayInit(cx, &kid->xml_kids, 1);
04856             }
04857 
04858             /* 14(b-c). */
04859             /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
04860             if (ok) {
04861                 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
04862                 if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) {
04863                     roots[VAL_ROOT] = *vp;
04864                     if ((JSXML *) XMLArrayCursorItem(&cursor) == kid)
04865                         ok = Replace(cx, kid, JSVAL_ZERO, *vp);
04866                 }
04867             }
04868             XMLArrayCursorFinish(&cursor);
04869         } else {
04870             /* 15(a). */
04871             ok = Replace(cx, xml, id, *vp);
04872         }
04873     }
04874 
04875 out:
04876     JS_POP_TEMP_ROOT(cx, &tvr);
04877     js_LeaveLocalRootScope(cx);
04878     return ok;
04879 
04880 type_error:
04881     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04882                          JSMSG_BAD_XMLLIST_PUT,
04883                          js_ValueToPrintableString(cx, id));
04884 bad:
04885     ok = JS_FALSE;
04886     goto out;
04887 }
04888 
04889 /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
04890 static JSBool
04891 ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
04892 {
04893     JSXML *target, *base;
04894     JSXMLQName *targetprop;
04895     JSObject *targetpropobj;
04896     jsval id, tv;
04897 
04898     /* Our caller must be protecting newborn objects. */
04899     JS_ASSERT(cx->localRootStack);
04900 
04901     if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
04902         if (!js_GetXMLObject(cx, list))
04903             return JS_FALSE;
04904         *result = list;
04905         return JS_TRUE;
04906     }
04907 
04908     target = list->xml_target;
04909     targetprop = list->xml_targetprop;
04910     if (!target || !targetprop || IS_STAR(targetprop->localName)) {
04911         *result = NULL;
04912         return JS_TRUE;
04913     }
04914 
04915     targetpropobj = js_GetXMLQNameObject(cx, targetprop);
04916     if (!targetpropobj)
04917         return JS_FALSE;
04918     if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) {
04919         *result = NULL;
04920         return JS_TRUE;
04921     }
04922 
04923     if (!ResolveValue(cx, target, &base))
04924         return JS_FALSE;
04925     if (!base) {
04926         *result = NULL;
04927         return JS_TRUE;
04928     }
04929     if (!js_GetXMLObject(cx, base))
04930         return JS_FALSE;
04931 
04932     id = OBJECT_TO_JSVAL(targetpropobj);
04933     if (!GetProperty(cx, base->object, id, &tv))
04934         return JS_FALSE;
04935     target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
04936 
04937     if (JSXML_LENGTH(target) == 0) {
04938         if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
04939             *result = NULL;
04940             return JS_TRUE;
04941         }
04942         tv = STRING_TO_JSVAL(cx->runtime->emptyString);
04943         if (!PutProperty(cx, base->object, id, &tv))
04944             return JS_FALSE;
04945         if (!GetProperty(cx, base->object, id, &tv))
04946             return JS_FALSE;
04947         target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv));
04948     }
04949 
04950     *result = target;
04951     return JS_TRUE;
04952 }
04953 
04954 /*
04955  * HasProperty must be able to return a found JSProperty and the object in
04956  * which it was found, if id is of the form function::name.  For other ids,
04957  * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp
04958  * and null in *objp.
04959  *
04960  * DROP_PROPERTY helps HasProperty callers drop function properties without
04961  * trying to drop the magic FOUND_XML_PROPERTY cookie.
04962  */
04963 #define FOUND_XML_PROPERTY              ((JSProperty *) 1)
04964 #define DROP_PROPERTY(cx,pobj,prop)     (((prop) != FOUND_XML_PROPERTY)       \
04965                                          ? OBJ_DROP_PROPERTY(cx, pobj, prop)  \
04966                                          : (void) 0)
04967 
04968 /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
04969 static JSBool
04970 HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp,
04971             JSProperty **propp)
04972 {
04973     JSXML *xml, *kid;
04974     JSXMLArrayCursor cursor;
04975     JSObject *kidobj;
04976     JSXMLQName *qn;
04977     jsid funid;
04978     JSXMLArray *array;
04979     JSXMLNameMatcher matcher;
04980     uint32 i, n;
04981 
04982     *objp = NULL;
04983     *propp = NULL;
04984 
04985     xml = (JSXML *) JS_GetPrivate(cx, obj);
04986     if (xml->xml_class == JSXML_CLASS_LIST) {
04987         n = JSXML_LENGTH(xml);
04988         if (js_IdIsIndex(id, &i)) {
04989             if (i < n)
04990                 *propp = FOUND_XML_PROPERTY;
04991             return JS_TRUE;
04992         }
04993 
04994         XMLArrayCursorInit(&cursor, &xml->xml_kids);
04995         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
04996             if (kid->xml_class == JSXML_CLASS_ELEMENT) {
04997                 kidobj = js_GetXMLObject(cx, kid);
04998                 if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp))
04999                     break;
05000                 if (*propp)
05001                     break;
05002             }
05003         }
05004         XMLArrayCursorFinish(&cursor);
05005         if (kid)
05006             return *propp != NULL;
05007     } else {
05008         if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) {
05009             if (i == 0)
05010                 *propp = FOUND_XML_PROPERTY;
05011             return JS_TRUE;
05012         }
05013 
05014         qn = ToXMLName(cx, id, &funid);
05015         if (!qn)
05016             return JS_FALSE;
05017         if (funid)
05018             return js_LookupProperty(cx, obj, funid, objp, propp);
05019 
05020         if (xml->xml_class != JSXML_CLASS_ELEMENT)
05021             return JS_TRUE;
05022 
05023         if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) {
05024             array = &xml->xml_attrs;
05025             matcher = MatchAttrName;
05026         } else {
05027             array = &xml->xml_kids;
05028             matcher = MatchElemName;
05029         }
05030         for (i = 0, n = array->length; i < n; i++) {
05031             kid = XMLARRAY_MEMBER(array, i, JSXML);
05032             if (kid && matcher(qn, kid)) {
05033                 *propp = FOUND_XML_PROPERTY;
05034                 return JS_TRUE;
05035             }
05036         }
05037     }
05038 
05039     return JS_TRUE;
05040 }
05041 
05042 static void
05043 xml_finalize(JSContext *cx, JSObject *obj)
05044 {
05045     JSXML *xml;
05046 
05047     xml = (JSXML *) JS_GetPrivate(cx, obj);
05048     if (!xml)
05049         return;
05050     if (xml->object == obj)
05051         xml->object = NULL;
05052     UNMETER(xml_stats.livexmlobj);
05053 }
05054 
05055 static void
05056 xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len)
05057 {
05058     uint32 i;
05059     JSXML *elt;
05060 
05061     for (i = 0; i < len; i++) {
05062         elt = vec[i];
05063         {
05064 #ifdef GC_MARK_DEBUG
05065             char buf[120];
05066 
05067             if (elt->xml_class == JSXML_CLASS_LIST) {
05068                 strcpy(buf, js_XMLList_str);
05069             } else if (JSXML_HAS_NAME(elt)) {
05070                 JSXMLQName *qn = elt->name;
05071 
05072                 JS_snprintf(buf, sizeof buf, "%s::%s",
05073                             qn->uri ? JS_GetStringBytes(qn->uri) : "*",
05074                             JS_GetStringBytes(qn->localName));
05075             } else {
05076                 JSString *str = elt->xml_value;
05077                 size_t srclen = JSSTRING_LENGTH(str);
05078                 size_t dstlen = sizeof buf;
05079 
05080                 if (srclen >= sizeof buf / 6)
05081                     srclen = sizeof buf / 6 - 1;
05082                 js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen,
05083                                          buf, &dstlen);
05084             }
05085 #endif
05086             GC_MARK(cx, elt, buf);
05087         }
05088     }
05089 }
05090 
05091 /*
05092  * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to
05093  * be native.  Therefore, xml_lookupProperty must return a valid JSProperty
05094  * pointer parameter via *propp to signify "property found".  Since the only
05095  * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from
05096  * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from
05097  * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a
05098  * JSScopeProperty here is when an unqualified name or XML name is being
05099  * accessed or when "name in xml" is called.
05100  *
05101  * This scope property keeps the JSOP_NAME code in js_Interpret happy by
05102  * giving it an sprop with (getter, setter) == (GetProperty, PutProperty).
05103  *
05104  * NB: xml_deleteProperty must take care to remove any property added here.
05105  *
05106  * FIXME This clashes with the function namespace implementation which also
05107  * uses native properties. Effectively after xml_lookupProperty any property
05108  * stored previously using assignments to xml.function::name will be removed.
05109  * We partially workaround the problem in js_GetXMLFunction. There we take
05110  * advantage of the fact that typically function:: is used to access the
05111  * functions from XML.prototype. So when js_GetProperty returns a non-function
05112  * property, we assume that it represents the result of GetProperty setter
05113  * hiding the function and use an extra prototype chain lookup to recover it.
05114  * For a proper solution see bug 355257.
05115  */
05116 static JSBool
05117 xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
05118                    JSProperty **propp)
05119 {
05120     JSScopeProperty *sprop;
05121 
05122     if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp))
05123         return JS_FALSE;
05124 
05125     if (*propp == FOUND_XML_PROPERTY) {
05126         sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
05127                                      SPROP_INVALID_SLOT, JSPROP_ENUMERATE,
05128                                      0, 0);
05129         if (!sprop)
05130             return JS_FALSE;
05131 
05132         JS_LOCK_OBJ(cx, obj);
05133         *objp = obj;
05134         *propp = (JSProperty *) sprop;
05135     }
05136     return JS_TRUE;
05137 }
05138 
05139 static JSBool
05140 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
05141                    JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
05142                    JSProperty **propp)
05143 {
05144     if (VALUE_IS_FUNCTION(cx, value) || getter || setter ||
05145         (attrs & JSPROP_ENUMERATE) == 0 ||
05146         (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
05147         return js_DefineProperty(cx, obj, id, value, getter, setter, attrs,
05148                                  propp);
05149     }
05150 
05151     if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value))
05152         return JS_FALSE;
05153     if (propp)
05154         *propp = NULL;
05155     return JS_TRUE;
05156 }
05157 
05158 static JSBool
05159 xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
05160 {
05161     if (id == JS_DEFAULT_XML_NAMESPACE_ID) {
05162         *vp = JSVAL_VOID;
05163         return JS_TRUE;
05164     }
05165 
05166     return GetProperty(cx, obj, ID_TO_VALUE(id), vp);
05167 }
05168 
05169 static JSBool
05170 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
05171 {
05172     return PutProperty(cx, obj, ID_TO_VALUE(id), vp);
05173 }
05174 
05175 static JSBool
05176 FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
05177               JSBool *foundp)
05178 {
05179     JSObject *pobj;
05180 
05181     if (prop) {
05182         *foundp = JS_TRUE;
05183     } else {
05184         if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop))
05185             return JS_FALSE;
05186         if (prop)
05187             DROP_PROPERTY(cx, pobj, prop);
05188         *foundp = (prop != NULL);
05189     }
05190     return JS_TRUE;
05191 }
05192 
05193 static JSBool
05194 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
05195                   uintN *attrsp)
05196 {
05197     JSBool found;
05198 
05199     if (!FoundProperty(cx, obj, id, prop, &found))
05200         return JS_FALSE;
05201     *attrsp = found ? JSPROP_ENUMERATE : 0;
05202     return JS_TRUE;
05203 }
05204 
05205 static JSBool
05206 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
05207                   uintN *attrsp)
05208 {
05209     JSBool found;
05210 
05211     if (!FoundProperty(cx, obj, id, prop, &found))
05212         return JS_FALSE;
05213     if (found) {
05214         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
05215                              JSMSG_CANT_SET_XML_ATTRS);
05216     }
05217     return !found;
05218 }
05219 
05220 static JSBool
05221 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
05222 {
05223     /*
05224      * If this object has its own (mutable) scope, and if id isn't an index,
05225      * then we may have added a property to the scope in xml_lookupProperty
05226      * for it to return to mean "found" and to provide a handle for access
05227      * operations to call the property's getter or setter.  The property also
05228      * helps speed up unqualified accesses via the property cache, avoiding
05229      * what amount to two HasProperty searches.
05230      *
05231      * But now it's time to remove any such property, to purge the property
05232      * cache and remove the scope entry.
05233      */
05234     if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) {
05235         if (!js_DeleteProperty(cx, obj, id, rval))
05236             return JS_FALSE;
05237     }
05238 
05239     return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval);
05240 }
05241 
05242 static JSBool
05243 xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
05244 {
05245     JSXML *xml;
05246 
05247     if (hint == JSTYPE_OBJECT) {
05248         /* Called from for..in code in js_Interpret: return an XMLList. */
05249         xml = (JSXML *) JS_GetPrivate(cx, obj);
05250         if (xml->xml_class != JSXML_CLASS_LIST) {
05251             obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj));
05252             if (!obj)
05253                 return JS_FALSE;
05254         }
05255         *vp = OBJECT_TO_JSVAL(obj);
05256         return JS_TRUE;
05257     }
05258 
05259     return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp);
05260 }
05261 
05262 static JSBool
05263 xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
05264               jsval *statep, jsid *idp)
05265 {
05266     JSXML *xml;
05267     uint32 length, index;
05268     JSXMLArrayCursor *cursor;
05269 
05270     xml = (JSXML *) JS_GetPrivate(cx, obj);
05271     length = JSXML_LENGTH(xml);
05272 
05273     switch (enum_op) {
05274       case JSENUMERATE_INIT:
05275         if (length == 0) {
05276             cursor = NULL;
05277         } else {
05278             cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
05279             if (!cursor)
05280                 return JS_FALSE;
05281             XMLArrayCursorInit(cursor, &xml->xml_kids);
05282         }
05283         *statep = PRIVATE_TO_JSVAL(cursor);
05284         if (idp)
05285             *idp = INT_TO_JSID(length);
05286         break;
05287 
05288       case JSENUMERATE_NEXT:
05289         cursor = JSVAL_TO_PRIVATE(*statep);
05290         if (cursor && cursor->array && (index = cursor->index) < length) {
05291             *idp = INT_TO_JSID(index);
05292             cursor->index = index + 1;
05293             break;
05294         }
05295         /* FALL THROUGH */
05296 
05297       case JSENUMERATE_DESTROY:
05298         cursor = JSVAL_TO_PRIVATE(*statep);
05299         if (cursor) {
05300             XMLArrayCursorFinish(cursor);
05301             JS_free(cx, cursor);
05302         }
05303         *statep = JSVAL_NULL;
05304         break;
05305     }
05306     return JS_TRUE;
05307 }
05308 
05309 static JSBool
05310 xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
05311 {
05312     return JS_TRUE;
05313 }
05314 
05315 static uint32
05316 xml_mark(JSContext *cx, JSObject *obj, void *arg)
05317 {
05318     JSXML *xml;
05319 
05320     xml = (JSXML *) JS_GetPrivate(cx, obj);
05321     GC_MARK(cx, xml, "private");
05322     return js_Mark(cx, obj, NULL);
05323 }
05324 
05325 static void
05326 xml_clear(JSContext *cx, JSObject *obj)
05327 {
05328 }
05329 
05330 static JSBool
05331 HasSimpleContent(JSXML *xml)
05332 {
05333     JSXML *kid;
05334     JSBool simple;
05335     uint32 i, n;
05336 
05337 again:
05338     switch (xml->xml_class) {
05339       case JSXML_CLASS_COMMENT:
05340       case JSXML_CLASS_PROCESSING_INSTRUCTION:
05341         return JS_FALSE;
05342       case JSXML_CLASS_LIST:
05343         if (xml->xml_kids.length == 0)
05344             return JS_TRUE;
05345         if (xml->xml_kids.length == 1) {
05346             kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
05347             if (kid) {
05348                 xml = kid;
05349                 goto again;
05350             }
05351         }
05352         /* FALL THROUGH */
05353       default:
05354         simple = JS_TRUE;
05355         for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
05356             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
05357             if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
05358                 simple = JS_FALSE;
05359                 break;
05360             }
05361         }
05362         return simple;
05363     }
05364 }
05365 
05366 /*
05367  * 11.2.2.1 Step 3(d) onward.
05368  */
05369 static JSObject *
05370 xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
05371 {
05372     JSTempValueRooter tvr;
05373 
05374     JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL));
05375 
05376     /*
05377      * As our callers have a bad habit of passing a pointer to an unrooted
05378      * local value as vp, we use a proper root here.
05379      */
05380     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
05381     if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value))
05382         obj = NULL;
05383     *vp = tvr.u.value;
05384     JS_POP_TEMP_ROOT(cx, &tvr);
05385     return obj;
05386 }
05387 
05388 static JSBool
05389 xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
05390 {
05391     return js_SetProperty(cx, obj, id, vp);
05392 }
05393 
05394 static JSBool
05395 xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
05396                     jsval *statep, jsid *idp, jsval *vp)
05397 {
05398     JSXML *xml, *kid;
05399     uint32 length, index;
05400     JSXMLArrayCursor *cursor;
05401     JSObject *kidobj;
05402 
05403     xml = (JSXML *) JS_GetPrivate(cx, obj);
05404     length = JSXML_LENGTH(xml);
05405     JS_ASSERT(INT_FITS_IN_JSVAL(length));
05406 
05407     switch (enum_op) {
05408       case JSENUMERATE_INIT:
05409         if (length == 0) {
05410             cursor = NULL;
05411         } else {
05412             cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor);
05413             if (!cursor)
05414                 return JS_FALSE;
05415             XMLArrayCursorInit(cursor, &xml->xml_kids);
05416         }
05417         *statep = PRIVATE_TO_JSVAL(cursor);
05418         if (idp)
05419             *idp = INT_TO_JSID(length);
05420         if (vp)
05421             *vp = JSVAL_VOID;
05422         break;
05423 
05424       case JSENUMERATE_NEXT:
05425         cursor = JSVAL_TO_PRIVATE(*statep);
05426         if (cursor && cursor->array && (index = cursor->index) < length) {
05427             while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) {
05428                 if (++index == length)
05429                     goto destroy;
05430             }
05431             kidobj = js_GetXMLObject(cx, kid);
05432             if (!kidobj)
05433                 return JS_FALSE;
05434             JS_ASSERT(INT_FITS_IN_JSVAL(index));
05435             *idp = INT_TO_JSID(index);
05436             *vp = OBJECT_TO_JSVAL(kidobj);
05437             cursor->index = index + 1;
05438             break;
05439         }
05440         /* FALL THROUGH */
05441 
05442       case JSENUMERATE_DESTROY:
05443         cursor = JSVAL_TO_PRIVATE(*statep);
05444         if (cursor) {
05445       destroy:
05446             XMLArrayCursorFinish(cursor);
05447             JS_free(cx, cursor);
05448         }
05449         *statep = JSVAL_NULL;
05450         break;
05451     }
05452     return JS_TRUE;
05453 }
05454 
05455 static JSBool
05456 xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
05457 {
05458     JSXML *xml, *vxml;
05459     JSObject *vobj;
05460     JSBool ok;
05461     JSString *str, *vstr;
05462     jsdouble d, d2;
05463 
05464     xml = (JSXML *) JS_GetPrivate(cx, obj);
05465     vxml = NULL;
05466     if (!JSVAL_IS_PRIMITIVE(v)) {
05467         vobj = JSVAL_TO_OBJECT(v);
05468         if (OBJECT_IS_XML(cx, vobj))
05469             vxml = (JSXML *) JS_GetPrivate(cx, vobj);
05470     }
05471 
05472     if (xml->xml_class == JSXML_CLASS_LIST) {
05473         ok = Equals(cx, xml, v, bp);
05474     } else if (vxml) {
05475         if (vxml->xml_class == JSXML_CLASS_LIST) {
05476             ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
05477         } else {
05478             if (((xml->xml_class == JSXML_CLASS_TEXT ||
05479                   xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
05480                  HasSimpleContent(vxml)) ||
05481                 ((vxml->xml_class == JSXML_CLASS_TEXT ||
05482                   vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
05483                  HasSimpleContent(xml))) {
05484                 ok = js_EnterLocalRootScope(cx);
05485                 if (ok) {
05486                     str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
05487                     vstr = js_ValueToString(cx, v);
05488                     ok = str && vstr;
05489                     if (ok)
05490                         *bp = js_EqualStrings(str, vstr);
05491                     js_LeaveLocalRootScope(cx);
05492                 }
05493             } else {
05494                 ok = XMLEquals(cx, xml, vxml, bp);
05495             }
05496         }
05497     } else {
05498         ok = js_EnterLocalRootScope(cx);
05499         if (ok) {
05500             if (HasSimpleContent(xml)) {
05501                 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
05502                 vstr = js_ValueToString(cx, v);
05503                 ok = str && vstr;
05504                 if (ok)
05505                     *bp = js_EqualStrings(str, vstr);
05506             } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
05507                 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
05508                 if (!str) {
05509                     ok = JS_FALSE;
05510                 } else if (JSVAL_IS_STRING(v)) {
05511                     *bp = js_EqualStrings(str, JSVAL_TO_STRING(v));
05512                 } else {
05513                     ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
05514                     if (ok) {
05515                         d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
05516                                              : *JSVAL_TO_DOUBLE(v);
05517                         *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
05518                     }
05519                 }
05520             } else {
05521                 *bp = JS_FALSE;
05522             }
05523             js_LeaveLocalRootScope(cx);
05524         }
05525     }
05526     return ok;
05527 }
05528 
05529 static JSBool
05530 xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp)
05531 {
05532     JSBool ok;
05533     JSObject *listobj, *robj;
05534     JSXML *list, *lxml, *rxml;
05535 
05536     ok = js_EnterLocalRootScope(cx);
05537     if (!ok)
05538         return JS_FALSE;
05539 
05540     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
05541     if (!listobj) {
05542         ok = JS_FALSE;
05543         goto out;
05544     }
05545 
05546     list = (JSXML *) JS_GetPrivate(cx, listobj);
05547     lxml = (JSXML *) JS_GetPrivate(cx, obj);
05548     ok = Append(cx, list, lxml);
05549     if (!ok)
05550         goto out;
05551 
05552     if (VALUE_IS_XML(cx, v)) {
05553         rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
05554     } else {
05555         robj = ToXML(cx, v);
05556         if (!robj) {
05557             ok = JS_FALSE;
05558             goto out;
05559         }
05560         rxml = (JSXML *) JS_GetPrivate(cx, robj);
05561     }
05562     ok = Append(cx, list, rxml);
05563     if (!ok)
05564         goto out;
05565 
05566     *vp = OBJECT_TO_JSVAL(listobj);
05567 out:
05568     js_LeaveLocalRootScopeWithResult(cx, *vp);
05569     return ok;
05570 }
05571 
05572 /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */
05573 JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = {
05574   { js_NewObjectMap,            js_DestroyObjectMap,
05575     xml_lookupProperty,         xml_defineProperty,
05576     xml_getProperty,            xml_setProperty,
05577     xml_getAttributes,          xml_setAttributes,
05578     xml_deleteProperty,         xml_defaultValue,
05579     xml_enumerate,              js_CheckAccess,
05580     NULL,                       NULL,
05581     NULL,                       NULL,
05582     NULL,                       xml_hasInstance,
05583     js_SetProtoOrParent,        js_SetProtoOrParent,
05584     xml_mark,                   xml_clear,
05585     NULL,                       NULL },
05586     xml_getMethod,              xml_setMethod,
05587     xml_enumerateValues,        xml_equality,
05588     xml_concatenate
05589 };
05590 
05591 static JSObjectOps *
05592 xml_getObjectOps(JSContext *cx, JSClass *clasp)
05593 {
05594     return &js_XMLObjectOps.base;
05595 }
05596 
05597 JS_FRIEND_DATA(JSClass) js_XMLClass = {
05598     js_XML_str,
05599     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
05600     JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
05601     JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    xml_finalize,
05602     xml_getObjectOps,  NULL,              NULL,              NULL,
05603     NULL,              NULL,              NULL,              NULL
05604 };
05605 
05606 static JSXML *
05607 StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv)
05608 {
05609     JSXML *xml;
05610     JSFunction *fun;
05611 
05612     JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2]));
05613 
05614     xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv);
05615     if (!xml || xml->xml_class != JSXML_CLASS_LIST)
05616         return xml;
05617 
05618     if (xml->xml_kids.length == 1) {
05619         xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
05620         if (xml) {
05621             *objp = js_GetXMLObject(cx, xml);
05622             if (!*objp)
05623                 return NULL;
05624             argv[-1] = OBJECT_TO_JSVAL(*objp);
05625             return xml;
05626         }
05627     }
05628 
05629     fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
05630     if (fun) {
05631         char numBuf[12];
05632         JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
05633         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
05634                              JSMSG_NON_LIST_XML_METHOD,
05635                              JS_GetFunctionName(fun), numBuf);
05636     }
05637     return NULL;
05638 }
05639 
05640 #define XML_METHOD_PROLOG                                                     \
05641     JS_BEGIN_MACRO                                                            \
05642         xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv);   \
05643         if (!xml)                                                             \
05644             return JS_FALSE;                                                  \
05645     JS_END_MACRO
05646 
05647 #define NON_LIST_XML_METHOD_PROLOG                                            \
05648     JS_BEGIN_MACRO                                                            \
05649         xml = StartNonListXMLMethod(cx, &obj, argv);                          \
05650         if (!xml)                                                             \
05651             return JS_FALSE;                                                  \
05652         JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);                        \
05653     JS_END_MACRO
05654 
05655 static JSBool
05656 xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05657                  jsval *rval)
05658 {
05659     JSXML *xml;
05660     JSXMLNamespace *ns;
05661 
05662     NON_LIST_XML_METHOD_PROLOG;
05663     if (xml->xml_class != JSXML_CLASS_ELEMENT)
05664         goto done;
05665     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
05666     if (!xml)
05667         return JS_FALSE;
05668     
05669     if (!NamespaceHelper(cx, NULL, 1, argv, rval))
05670         return JS_FALSE;
05671     JS_ASSERT(!JSVAL_IS_PRIMITIVE(*rval));
05672     
05673     ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
05674     if (!AddInScopeNamespace(cx, xml, ns))
05675         return JS_FALSE;
05676     ns->declared = JS_TRUE;
05677 
05678   done:
05679     *rval = OBJECT_TO_JSVAL(obj);
05680     return JS_TRUE;
05681 }
05682 
05683 static JSBool
05684 xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05685                 jsval *rval)
05686 {
05687     JSXML *xml, *vxml;
05688     jsval name, v;
05689     JSObject *vobj;
05690 
05691     NON_LIST_XML_METHOD_PROLOG;
05692     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
05693     if (!xml)
05694         return JS_FALSE;
05695 
05696     if (!js_GetAnyName(cx, &name))
05697         return JS_FALSE;
05698 
05699     if (!GetProperty(cx, obj, name, &v))
05700         return JS_FALSE;
05701 
05702     JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
05703     vobj = JSVAL_TO_OBJECT(v);
05704     JS_ASSERT(OBJECT_IS_XML(cx, vobj));
05705     vxml = (JSXML *) JS_GetPrivate(cx, vobj);
05706     JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
05707 
05708     if (!IndexToIdVal(cx, vxml->xml_kids.length, &name))
05709         return JS_FALSE;
05710     if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0]))
05711         return JS_FALSE;
05712 
05713     *rval = OBJECT_TO_JSVAL(obj);
05714     return JS_TRUE;
05715 }
05716 
05717 /* XML and XMLList */
05718 static JSBool
05719 xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05720               jsval *rval)
05721 {
05722     JSXMLQName *qn;
05723 
05724     qn = ToAttributeName(cx, argv[0]);
05725     if (!qn)
05726         return JS_FALSE;
05727     argv[0] = OBJECT_TO_JSVAL(qn->object);      /* local root */
05728     return GetProperty(cx, obj, argv[0], rval);
05729 }
05730 
05731 /* XML and XMLList */
05732 static JSBool
05733 xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05734                jsval *rval)
05735 {
05736     jsval name;
05737     JSXMLQName *qn;
05738     JSTempValueRooter tvr;
05739     JSBool ok;
05740 
05741     name = ATOM_KEY(cx->runtime->atomState.starAtom);
05742     qn = ToAttributeName(cx, name);
05743     if (!qn)
05744         return JS_FALSE;
05745     name = OBJECT_TO_JSVAL(qn->object);
05746     JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr);
05747     ok = GetProperty(cx, obj, name, rval);
05748     JS_POP_TEMP_ROOT(cx, &tvr);
05749     return ok;
05750 }
05751 
05752 static JSXML *
05753 xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
05754 {
05755     JSObject *listobj;
05756     JSXML *list;
05757 
05758     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
05759     if (!listobj)
05760         return NULL;
05761 
05762     *rval = OBJECT_TO_JSVAL(listobj);
05763     list = (JSXML *) JS_GetPrivate(cx, listobj);
05764     list->xml_target = xml;
05765     return list;
05766 }
05767 
05768 static JSBool
05769 xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
05770                  jsval *rval)
05771 {
05772     uint32 index;
05773     JSXML *kid;
05774     JSObject *kidobj;
05775 
05776     /* ECMA-357 13.4.4.6 */
05777     JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
05778 
05779     if (js_IdIsIndex(name, &index)) {
05780         if (index >= JSXML_LENGTH(xml)) {
05781             *rval = JSVAL_VOID;
05782         } else {
05783             kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
05784             if (!kid) {
05785                 *rval = JSVAL_VOID;
05786             } else {
05787                 kidobj = js_GetXMLObject(cx, kid);
05788                 if (!kidobj)
05789                     return JS_FALSE;
05790                 *rval = OBJECT_TO_JSVAL(kidobj);
05791             }
05792         }
05793         return JS_TRUE;
05794     }
05795 
05796     return GetProperty(cx, obj, name, rval);
05797 }
05798 
05799 /* XML and XMLList */
05800 static JSBool
05801 xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
05802 {
05803     JSXML *xml, *list, *kid, *vxml;
05804     JSXMLArrayCursor cursor;
05805     jsval name, v;
05806     JSObject *kidobj;
05807 
05808     XML_METHOD_PROLOG;
05809     name = argv[0];
05810     if (xml->xml_class == JSXML_CLASS_LIST) {
05811         /* ECMA-357 13.5.4.4 */
05812         list = xml_list_helper(cx, xml, rval);
05813         if (!list)
05814             return JS_FALSE;
05815 
05816         XMLArrayCursorInit(&cursor, &xml->xml_kids);
05817         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
05818             kidobj = js_GetXMLObject(cx, kid);
05819             if (!kidobj)
05820                 break;
05821             if (!xml_child_helper(cx, kidobj, kid, name, &v))
05822                 break;
05823             if (JSVAL_IS_VOID(v)) {
05824                 /* The property didn't exist in this kid. */
05825                 continue;
05826             }
05827 
05828             JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
05829             vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
05830             if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
05831                 !Append(cx, list, vxml)) {
05832                 break;
05833             }
05834         }
05835         XMLArrayCursorFinish(&cursor);
05836         return !kid;
05837     }
05838 
05839     /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
05840     if (!xml_child_helper(cx, obj, xml, name, rval))
05841         return JS_FALSE;
05842     if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval))
05843         return JS_FALSE;
05844     return JS_TRUE;
05845 }
05846 
05847 static JSBool
05848 xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05849                jsval *rval)
05850 {
05851     JSXML *xml, *parent;
05852     uint32 i, n;
05853 
05854     NON_LIST_XML_METHOD_PROLOG;
05855     parent = xml->parent;
05856     if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
05857         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
05858         return JS_TRUE;
05859     }
05860     for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
05861         if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
05862             break;
05863     }
05864     JS_ASSERT(i < n);
05865     return js_NewNumberValue(cx, i, rval);
05866 }
05867 
05868 /* XML and XMLList */
05869 static JSBool
05870 xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05871              jsval *rval)
05872 {
05873     jsval name;
05874 
05875     name = ATOM_KEY(cx->runtime->atomState.starAtom);
05876     return GetProperty(cx, obj, name, rval);
05877 }
05878 
05879 /* XML and XMLList */
05880 static JSBool
05881 xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05882              jsval *rval)
05883 {
05884     JSXML *xml, *list, *kid, *vxml;
05885     JSBool ok;
05886     uint32 i, n;
05887     JSObject *kidobj;
05888     jsval v;
05889 
05890     XML_METHOD_PROLOG;
05891     list = xml_list_helper(cx, xml, rval);
05892     if (!list)
05893         return JS_FALSE;
05894 
05895     ok = JS_TRUE;
05896 
05897     if (xml->xml_class == JSXML_CLASS_LIST) {
05898         /* 13.5.4.6 Step 2. */
05899         for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
05900             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
05901             if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
05902                 ok = js_EnterLocalRootScope(cx);
05903                 if (!ok)
05904                     break;
05905                 kidobj = js_GetXMLObject(cx, kid);
05906                 if (kidobj) {
05907                     ok = xml_comments(cx, kidobj, argc, argv, &v);
05908                 } else {
05909                     ok = JS_FALSE;
05910                     v = JSVAL_NULL;
05911                 }
05912                 js_LeaveLocalRootScopeWithResult(cx, v);
05913                 if (!ok)
05914                     break;
05915                 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
05916                 if (JSXML_LENGTH(vxml) != 0) {
05917                     ok = Append(cx, list, vxml);
05918                     if (!ok)
05919                         break;
05920                 }
05921             }
05922         }
05923     } else {
05924         /* 13.4.4.9 Step 2. */
05925         for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
05926             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
05927             if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
05928                 ok = Append(cx, list, kid);
05929                 if (!ok)
05930                     break;
05931             }
05932         }
05933     }
05934 
05935     return ok;
05936 }
05937 
05938 /* XML and XMLList */
05939 static JSBool
05940 xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05941              jsval *rval)
05942 {
05943     JSXML *xml, *kid;
05944     jsval value;
05945     JSBool eq;
05946     JSXMLArrayCursor cursor;
05947     JSObject *kidobj;
05948 
05949     XML_METHOD_PROLOG;
05950     value = argv[0];
05951     if (xml->xml_class == JSXML_CLASS_LIST) {
05952         eq = JS_FALSE;
05953         XMLArrayCursorInit(&cursor, &xml->xml_kids);
05954         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
05955             kidobj = js_GetXMLObject(cx, kid);
05956             if (!kidobj || !xml_equality(cx, kidobj, value, &eq))
05957                 break;
05958             if (eq)
05959                 break;
05960         }
05961         XMLArrayCursorFinish(&cursor);
05962         if (kid && !eq)
05963             return JS_FALSE;
05964     } else {
05965         if (!xml_equality(cx, obj, value, &eq))
05966             return JS_FALSE;
05967     }
05968     *rval = BOOLEAN_TO_JSVAL(eq);
05969     return JS_TRUE;
05970 }
05971 
05972 /* XML and XMLList */
05973 static JSBool
05974 xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
05975 {
05976     JSXML *xml, *copy;
05977 
05978     XML_METHOD_PROLOG;
05979     copy = DeepCopy(cx, xml, NULL, 0);
05980     if (!copy)
05981         return JS_FALSE;
05982     *rval = OBJECT_TO_JSVAL(copy->object);
05983     return JS_TRUE;
05984 }
05985 
05986 /* XML and XMLList */
05987 static JSBool
05988 xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
05989                 jsval *rval)
05990 {
05991     JSXML *xml, *list;
05992     jsval name;
05993 
05994     XML_METHOD_PROLOG;
05995     name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
05996     list = Descendants(cx, xml, name);
05997     if (!list)
05998         return JS_FALSE;
05999     *rval = OBJECT_TO_JSVAL(list->object);
06000     return JS_TRUE;
06001 }
06002 
06003 /* XML and XMLList */
06004 static JSBool
06005 xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06006              jsval *rval)
06007 {
06008     JSXML *xml, *list, *kid, *vxml;
06009     jsval name, v;
06010     JSXMLQName *nameqn;
06011     jsid funid;
06012     JSBool ok;
06013     JSXMLArrayCursor cursor;
06014     JSObject *kidobj;
06015     uint32 i, n;
06016 
06017     XML_METHOD_PROLOG;
06018     name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
06019     nameqn = ToXMLName(cx, name, &funid);
06020     if (!nameqn)
06021         return JS_FALSE;
06022     argv[0] = OBJECT_TO_JSVAL(nameqn->object);
06023 
06024     list = xml_list_helper(cx, xml, rval);
06025     if (!list)
06026         return JS_FALSE;
06027     if (funid)
06028         return JS_TRUE;
06029 
06030     list->xml_targetprop = nameqn;
06031     ok = JS_TRUE;
06032 
06033     if (xml->xml_class == JSXML_CLASS_LIST) {
06034         /* 13.5.4.6 */
06035         XMLArrayCursorInit(&cursor, &xml->xml_kids);
06036         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
06037             if (kid->xml_class == JSXML_CLASS_ELEMENT) {
06038                 ok = js_EnterLocalRootScope(cx);
06039                 if (!ok)
06040                     break;
06041                 kidobj = js_GetXMLObject(cx, kid);
06042                 if (kidobj) {
06043                     ok = xml_elements(cx, kidobj, argc, argv, &v);
06044                 } else {
06045                     ok = JS_FALSE;
06046                     v = JSVAL_NULL;
06047                 }
06048                 js_LeaveLocalRootScopeWithResult(cx, v);
06049                 if (!ok)
06050                     break;
06051                 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
06052                 if (JSXML_LENGTH(vxml) != 0) {
06053                     ok = Append(cx, list, vxml);
06054                     if (!ok)
06055                         break;
06056                 }
06057             }
06058         }
06059         XMLArrayCursorFinish(&cursor);
06060     } else {
06061         for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
06062             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
06063             if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
06064                 MatchElemName(nameqn, kid)) {
06065                 ok = Append(cx, list, kid);
06066                 if (!ok)
06067                     break;
06068             }
06069         }
06070     }
06071 
06072     return ok;
06073 }
06074 
06075 /* XML and XMLList */
06076 static JSBool
06077 xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06078                    jsval *rval)
06079 {
06080     jsval name;
06081     JSObject *pobj;
06082     JSProperty *prop;
06083 
06084     if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv))
06085         return JS_FALSE;
06086 
06087     name = argv[0];
06088     if (!HasProperty(cx, obj, name, &pobj, &prop))
06089         return JS_FALSE;
06090     if (!prop) {
06091         return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv,
06092                                        rval);
06093     }
06094     DROP_PROPERTY(cx, pobj, prop);
06095     *rval = JSVAL_TRUE;
06096     return JS_TRUE;
06097 }
06098 
06099 /* XML and XMLList */
06100 static JSBool
06101 xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06102                       jsval *rval)
06103 {
06104     JSXML *xml, *kid;
06105     JSObject *kidobj;
06106     uint32 i, n;
06107 
06108     XML_METHOD_PROLOG;
06109 again:
06110     switch (xml->xml_class) {
06111       case JSXML_CLASS_ATTRIBUTE:
06112       case JSXML_CLASS_COMMENT:
06113       case JSXML_CLASS_PROCESSING_INSTRUCTION:
06114       case JSXML_CLASS_TEXT:
06115         *rval = JSVAL_FALSE;
06116         break;
06117       case JSXML_CLASS_LIST:
06118         if (xml->xml_kids.length == 0) {
06119             *rval = JSVAL_TRUE;
06120         } else if (xml->xml_kids.length == 1) {
06121             kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
06122             if (kid) {
06123                 kidobj = js_GetXMLObject(cx, kid);
06124                 if (!kidobj)
06125                     return JS_FALSE;
06126                 obj = kidobj;
06127                 xml = (JSXML *) JS_GetPrivate(cx, obj);
06128                 goto again;
06129             }
06130         }
06131         /* FALL THROUGH */
06132       default:
06133         *rval = JSVAL_FALSE;
06134         for (i = 0, n = xml->xml_kids.length; i < n; i++) {
06135             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
06136             if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
06137                 *rval = JSVAL_TRUE;
06138                 break;
06139             }
06140         }
06141         break;
06142     }
06143     return JS_TRUE;
06144 }
06145 
06146 /* XML and XMLList */
06147 static JSBool
06148 xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06149                      jsval *rval)
06150 {
06151     JSXML *xml;
06152 
06153     XML_METHOD_PROLOG;
06154     *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
06155     return JS_TRUE;
06156 }
06157 
06158 typedef struct JSTempRootedNSArray {
06159     JSTempValueRooter   tvr;
06160     JSXMLArray          array;
06161     jsval               value;  /* extra root for temporaries */
06162 } JSTempRootedNSArray;
06163 
06164 JS_STATIC_DLL_CALLBACK(void)
06165 mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr)
06166 {
06167     JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr;
06168 
06169     namespace_mark_vector(cx,
06170                           (JSXMLNamespace **)tmp->array.vector,
06171                           tmp->array.length);
06172     XMLArrayCursorMark(cx, tmp->array.cursors);
06173     if (JSVAL_IS_GCTHING(tmp->value))
06174         GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value");
06175 }
06176 
06177 static void
06178 InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
06179 {
06180     XMLArrayInit(cx, &tmp->array, 0);
06181     tmp->value = JSVAL_NULL;
06182     JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr);
06183 }
06184 
06185 static void
06186 FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
06187 {
06188     JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array);
06189     JS_POP_TEMP_ROOT(cx, &tmp->tvr);
06190     XMLArrayFinish(cx, &tmp->array);
06191 }
06192 
06193 /*
06194  * Populate a new JS array with elements of JSTempRootedNSArray.array and
06195  * place the result into rval.  rval must point to a rooted location.
06196  */
06197 static JSBool
06198 TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval)
06199 {
06200     JSObject *arrayobj;
06201     uint32 i, n;
06202     JSXMLNamespace *ns;
06203     JSObject *nsobj;
06204 
06205     arrayobj = js_NewArrayObject(cx, 0, NULL);
06206     if (!arrayobj)
06207         return JS_FALSE;
06208     *rval = OBJECT_TO_JSVAL(arrayobj);
06209     for (i = 0, n = tmp->array.length; i < n; i++) {
06210         ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace);
06211         if (!ns)
06212             continue;
06213         nsobj = js_GetXMLNamespaceObject(cx, ns);
06214         if (!nsobj)
06215             return JS_FALSE;
06216         tmp->value = OBJECT_TO_JSVAL(nsobj);
06217         if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value))
06218             return JS_FALSE;
06219     }
06220     return JS_TRUE;
06221 }
06222 
06223 static JSBool
06224 FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
06225 {
06226     uint32 length, i, j, n;
06227     JSXMLNamespace *ns, *ns2;
06228 
06229     length = nsarray->length;
06230     do {
06231         if (xml->xml_class != JSXML_CLASS_ELEMENT)
06232             continue;
06233         for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
06234             ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
06235             if (!ns)
06236                 continue;
06237 
06238             for (j = 0; j < length; j++) {
06239                 ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace);
06240                 if (ns2 &&
06241                     ((ns2->prefix && ns->prefix)
06242                      ? js_EqualStrings(ns2->prefix, ns->prefix)
06243                      : js_EqualStrings(ns2->uri, ns->uri))) {
06244                     break;
06245                 }
06246             }
06247 
06248             if (j == length) {
06249                 if (!XMLARRAY_APPEND(cx, nsarray, ns))
06250                     return JS_FALSE;
06251                 ++length;
06252             }
06253         }
06254     } while ((xml = xml->parent) != NULL);
06255     JS_ASSERT(length == nsarray->length);
06256 
06257     return JS_TRUE;
06258 }
06259 
06260 static JSBool
06261 xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06262                       jsval *rval)
06263 {
06264     JSXML *xml;
06265     JSTempRootedNSArray namespaces;
06266     JSBool ok;
06267 
06268     NON_LIST_XML_METHOD_PROLOG;
06269 
06270     InitTempNSArray(cx, &namespaces);
06271     ok = FindInScopeNamespaces(cx, xml, &namespaces.array) &&
06272          TempNSArrayToJSArray(cx, &namespaces, rval);
06273     FinishTempNSArray(cx, &namespaces);
06274     return ok;
06275 }
06276 
06277 static JSBool
06278 xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06279                      jsval *rval)
06280 {
06281     JSXML *xml, *kid;
06282     jsval arg;
06283     uint32 i;
06284 
06285     NON_LIST_XML_METHOD_PROLOG;
06286     if (!JSXML_HAS_KIDS(xml))
06287         return JS_TRUE;
06288 
06289     arg = argv[0];
06290     if (JSVAL_IS_NULL(arg)) {
06291         kid = NULL;
06292         i = 0;
06293     } else {
06294         if (!VALUE_IS_XML(cx, arg))
06295             return JS_TRUE;
06296         kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
06297         i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
06298         if (i == XML_NOT_FOUND)
06299             return JS_TRUE;
06300         ++i;
06301     }
06302 
06303     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06304     if (!xml)
06305         return JS_FALSE;
06306     if (!Insert(cx, xml, i, argv[1]))
06307         return JS_FALSE;
06308     *rval = OBJECT_TO_JSVAL(obj);
06309     return JS_TRUE;
06310 }
06311 
06312 static JSBool
06313 xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06314                       jsval *rval)
06315 {
06316     JSXML *xml, *kid;
06317     jsval arg;
06318     uint32 i;
06319 
06320     NON_LIST_XML_METHOD_PROLOG;
06321     if (!JSXML_HAS_KIDS(xml))
06322         return JS_TRUE;
06323 
06324     arg = argv[0];
06325     if (JSVAL_IS_NULL(arg)) {
06326         kid = NULL;
06327         i = xml->xml_kids.length;
06328     } else {
06329         if (!VALUE_IS_XML(cx, arg))
06330             return JS_TRUE;
06331         kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg));
06332         i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL);
06333         if (i == XML_NOT_FOUND)
06334             return JS_TRUE;
06335     }
06336 
06337     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06338     if (!xml)
06339         return JS_FALSE;
06340     if (!Insert(cx, xml, i, argv[1]))
06341         return JS_FALSE;
06342     *rval = OBJECT_TO_JSVAL(obj);
06343     return JS_TRUE;
06344 }
06345 
06346 /* XML and XMLList */
06347 static JSBool
06348 xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
06349 {
06350     JSXML *xml;
06351 
06352     XML_METHOD_PROLOG;
06353     if (xml->xml_class != JSXML_CLASS_LIST) {
06354         *rval = JSVAL_ONE;
06355     } else {
06356         if (!js_NewNumberValue(cx, xml->xml_kids.length, rval))
06357             return JS_FALSE;
06358     }
06359     return JS_TRUE;
06360 }
06361 
06362 static JSBool
06363 xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06364               jsval *rval)
06365 {
06366     JSXML *xml;
06367 
06368     NON_LIST_XML_METHOD_PROLOG;
06369     *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL;
06370     return JS_TRUE;
06371 }
06372 
06373 static JSBool
06374 xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
06375 {
06376     JSXML *xml;
06377     JSObject *nameobj;
06378 
06379     NON_LIST_XML_METHOD_PROLOG;
06380     if (!xml->name) {
06381         *rval = JSVAL_NULL;
06382     } else {
06383         nameobj = js_GetXMLQNameObject(cx, xml->name);
06384         if (!nameobj)
06385             return JS_FALSE;
06386         *rval = OBJECT_TO_JSVAL(nameobj);
06387     }
06388     return JS_TRUE;
06389 }
06390 
06391 static JSBool
06392 xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06393               jsval *rval)
06394 {
06395     JSXML *xml;
06396     JSString *prefix;
06397     JSTempRootedNSArray inScopeNSes;
06398     JSBool ok;
06399     jsuint i, length;
06400     JSXMLNamespace *ns;
06401     JSObject *nsobj;
06402 
06403     NON_LIST_XML_METHOD_PROLOG;
06404     if (argc == 0 && !JSXML_HAS_NAME(xml)) {
06405         *rval = JSVAL_NULL;
06406         return JS_TRUE;
06407     }
06408 
06409     if (argc == 0) {
06410         prefix = NULL;
06411     } else {
06412         prefix = js_ValueToString(cx, argv[0]);
06413         if (!prefix)
06414             return JS_FALSE;
06415         argv[0] = STRING_TO_JSVAL(prefix);      /* local root */
06416     }
06417 
06418     /* After this point the control must flow through label out. */
06419     InitTempNSArray(cx, &inScopeNSes);
06420     ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array);
06421     if (!ok)
06422         goto out;
06423 
06424     if (!prefix) {
06425         ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
06426         if (!ns) {
06427             ok = JS_FALSE;
06428             goto out;
06429         }
06430     } else {
06431         ns = NULL;
06432         for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
06433             ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace);
06434             if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix))
06435                 break;
06436             ns = NULL;
06437         }
06438     }
06439 
06440     if (!ns) {
06441         *rval = JSVAL_VOID;
06442     } else {
06443         nsobj = js_GetXMLNamespaceObject(cx, ns);
06444         if (!nsobj) {
06445             ok = JS_FALSE;
06446             goto out;
06447         }
06448         *rval = OBJECT_TO_JSVAL(nsobj);
06449     }
06450 
06451   out:
06452     FinishTempNSArray(cx, &inScopeNSes);
06453     return JS_TRUE;
06454 }
06455 
06456 static JSBool
06457 xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06458                           jsval *rval)
06459 {
06460     JSXML *xml, *yml;
06461     JSBool ok;
06462     JSTempRootedNSArray ancestors, declared;
06463     uint32 i, n;
06464     JSXMLNamespace *ns;
06465 
06466     NON_LIST_XML_METHOD_PROLOG;
06467     if (JSXML_HAS_VALUE(xml))
06468         return JS_TRUE;
06469 
06470     /* From here, control flow must goto out to finish these arrays. */
06471     ok = JS_TRUE;
06472     InitTempNSArray(cx, &ancestors);
06473     InitTempNSArray(cx, &declared);
06474     yml = xml;
06475 
06476     while ((yml = yml->parent) != NULL) {
06477         JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
06478         for (i = 0, n = yml->xml_namespaces.length; i < n; i++) {
06479             ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace);
06480             if (ns &&
06481                 !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
06482                 ok = XMLARRAY_APPEND(cx, &ancestors.array, ns);
06483                 if (!ok)
06484                     goto out;
06485             }
06486         }
06487     }
06488 
06489     for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
06490         ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace);
06491         if (!ns)
06492             continue;
06493         if (!ns->declared)
06494             continue;
06495         if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
06496             ok = XMLARRAY_APPEND(cx, &declared.array, ns);
06497             if (!ok)
06498                 goto out;
06499         }
06500     }
06501 
06502     ok = TempNSArrayToJSArray(cx, &declared, rval);
06503 
06504 out:
06505     /* Finishing must be in reverse order of initialization to follow LIFO. */
06506     FinishTempNSArray(cx, &declared);
06507     FinishTempNSArray(cx, &ancestors);
06508     return ok;
06509 }
06510 
06511 static const char js_attribute_str[] = "attribute";
06512 static const char js_text_str[]      = "text";
06513 
06514 /* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */
06515 const char *js_xml_class_str[] = {
06516     "list",
06517     "element",
06518     js_attribute_str,
06519     "processing-instruction",
06520     js_text_str,
06521     "comment"
06522 };
06523 
06524 static JSBool
06525 xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06526              jsval *rval)
06527 {
06528     JSXML *xml;
06529     JSString *str;
06530 
06531     NON_LIST_XML_METHOD_PROLOG;
06532     str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
06533     if (!str)
06534         return JS_FALSE;
06535     *rval = STRING_TO_JSVAL(str);
06536     return JS_TRUE;
06537 }
06538 
06539 static JSBool
06540 NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id)
06541 {
06542     jsval junk;
06543 
06544     if (xml->xml_class == JSXML_CLASS_LIST)
06545         return DeleteProperty(cx, obj, id, &junk);
06546     return DeleteByIndex(cx, xml, id, &junk);
06547 }
06548 
06549 /*
06550  * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace
06551  * text between tags to be removed by normalize.
06552  */
06553 static JSBool
06554 IsXMLSpace(JSString *str)
06555 {
06556     const jschar *cp, *end;
06557 
06558     cp = JSSTRING_CHARS(str);
06559     end = cp + JSSTRING_LENGTH(str);
06560     while (cp < end) {
06561         if (!JS_ISXMLSPACE(*cp))
06562             return JS_FALSE;
06563         ++cp;
06564     }
06565     return JS_TRUE;
06566 }
06567 
06568 /* XML and XMLList */
06569 static JSBool
06570 xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06571               jsval *rval)
06572 {
06573     JSXML *xml, *kid, *kid2;
06574     uint32 i, n;
06575     JSObject *kidobj;
06576     JSString *str;
06577     jsval junk;
06578 
06579     XML_METHOD_PROLOG;
06580     *rval = OBJECT_TO_JSVAL(obj);
06581     if (!JSXML_HAS_KIDS(xml))
06582         return JS_TRUE;
06583 
06584     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06585     if (!xml)
06586         return JS_FALSE;
06587 
06588     for (i = 0, n = xml->xml_kids.length; i < n; i++) {
06589         kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
06590         if (!kid)
06591             continue;
06592         if (kid->xml_class == JSXML_CLASS_ELEMENT) {
06593             kidobj = js_GetXMLObject(cx, kid);
06594             if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk))
06595                 return JS_FALSE;
06596         } else if (kid->xml_class == JSXML_CLASS_TEXT) {
06597             while (i + 1 < n &&
06598                    (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
06599                    kid2->xml_class == JSXML_CLASS_TEXT) {
06600                 str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
06601                 if (!str)
06602                     return JS_FALSE;
06603                 if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1)))
06604                     return JS_FALSE;
06605                 n = xml->xml_kids.length;
06606                 kid->xml_value = str;
06607             }
06608             if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) {
06609                 if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i)))
06610                     return JS_FALSE;
06611                 n = xml->xml_kids.length;
06612                 --i;
06613             }
06614         }
06615     }
06616 
06617     return JS_TRUE;
06618 }
06619 
06620 /* XML and XMLList */
06621 static JSBool
06622 xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
06623 {
06624     JSXML *xml, *parent, *kid;
06625     uint32 i, n;
06626     JSObject *parentobj;
06627 
06628     XML_METHOD_PROLOG;
06629     parent = xml->parent;
06630     if (xml->xml_class == JSXML_CLASS_LIST) {
06631         *rval = JSVAL_VOID;
06632         n = xml->xml_kids.length;
06633         if (n == 0)
06634             return JS_TRUE;
06635 
06636         kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
06637         if (!kid)
06638             return JS_TRUE;
06639         parent = kid->parent;
06640         for (i = 1; i < n; i++) {
06641             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
06642             if (kid && kid->parent != parent)
06643                 return JS_TRUE;
06644         }
06645     }
06646 
06647     if (!parent) {
06648         *rval = JSVAL_NULL;
06649         return JS_TRUE;
06650     }
06651 
06652     parentobj = js_GetXMLObject(cx, parent);
06653     if (!parentobj)
06654         return JS_FALSE;
06655     *rval = OBJECT_TO_JSVAL(parentobj);
06656     return JS_TRUE;
06657 }
06658 
06659 /* XML and XMLList */
06660 static JSBool
06661 xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc,
06662                            jsval *argv, jsval *rval)
06663 {
06664     JSXML *xml, *list, *kid, *vxml;
06665     jsval name, v;
06666     JSXMLQName *nameqn;
06667     jsid funid;
06668     JSBool ok;
06669     JSXMLArrayCursor cursor;
06670     JSObject *kidobj;
06671     uint32 i, n;
06672 
06673     XML_METHOD_PROLOG;
06674     name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0];
06675     nameqn = ToXMLName(cx, name, &funid);
06676     if (!nameqn)
06677         return JS_FALSE;
06678     argv[0] = OBJECT_TO_JSVAL(nameqn->object);
06679 
06680     list = xml_list_helper(cx, xml, rval);
06681     if (!list)
06682         return JS_FALSE;
06683     if (funid)
06684         return JS_TRUE;
06685 
06686     list->xml_targetprop = nameqn;
06687     ok = JS_TRUE;
06688 
06689     if (xml->xml_class == JSXML_CLASS_LIST) {
06690         /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
06691         XMLArrayCursorInit(&cursor, &xml->xml_kids);
06692         while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
06693             if (kid->xml_class == JSXML_CLASS_ELEMENT) {
06694                 ok = js_EnterLocalRootScope(cx);
06695                 if (!ok)
06696                     break;
06697                 kidobj = js_GetXMLObject(cx, kid);
06698                 if (kidobj) {
06699                     ok = xml_processingInstructions(cx, kidobj, argc, argv, &v);
06700                 } else {
06701                     ok = JS_FALSE;
06702                     v = JSVAL_NULL;
06703                 }
06704                 js_LeaveLocalRootScopeWithResult(cx, v);
06705                 if (!ok)
06706                     break;
06707                 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
06708                 if (JSXML_LENGTH(vxml) != 0) {
06709                     ok = Append(cx, list, vxml);
06710                     if (!ok)
06711                         break;
06712                 }
06713             }
06714         }
06715         XMLArrayCursorFinish(&cursor);
06716     } else {
06717         /* 13.4.4.28 Step 4. */
06718         for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
06719             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
06720             if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
06721                 (IS_STAR(nameqn->localName) ||
06722                  js_EqualStrings(nameqn->localName, kid->name->localName))) {
06723                 ok = Append(cx, list, kid);
06724                 if (!ok)
06725                     break;
06726             }
06727         }
06728     }
06729 
06730     return ok;
06731 }
06732 
06733 static JSBool
06734 xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06735                  jsval *rval)
06736 {
06737     JSXML *xml;
06738 
06739     NON_LIST_XML_METHOD_PROLOG;
06740     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06741     if (!xml)
06742         return JS_FALSE;
06743     *rval = OBJECT_TO_JSVAL(obj);
06744     return Insert(cx, xml, 0, argv[0]);
06745 }
06746 
06747 /* XML and XMLList */
06748 static JSBool
06749 xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06750                          jsval *rval)
06751 {
06752     JSXML *xml;
06753     jsval name;
06754     uint32 index;
06755 
06756     XML_METHOD_PROLOG;
06757     name = argv[0];
06758     *rval = JSVAL_FALSE;
06759     if (js_IdIsIndex(name, &index)) {
06760         if (xml->xml_class == JSXML_CLASS_LIST) {
06761             /* 13.5.4.18. */
06762             *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
06763         } else {
06764             /* 13.4.4.30. */
06765             *rval = BOOLEAN_TO_JSVAL(index == 0);
06766         }
06767     }
06768     return JS_TRUE;
06769 }
06770 
06771 static JSBool
06772 namespace_full_match(const void *a, const void *b)
06773 {
06774     const JSXMLNamespace *nsa = (const JSXMLNamespace *) a;
06775     const JSXMLNamespace *nsb = (const JSXMLNamespace *) b;
06776 
06777     if (nsa->prefix && nsb->prefix &&
06778         !js_EqualStrings(nsa->prefix, nsb->prefix)) {
06779         return JS_FALSE;
06780     }
06781     return js_EqualStrings(nsa->uri, nsb->uri);
06782 }
06783 
06784 static JSBool
06785 xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns)
06786 {
06787     JSXMLNamespace *thisns, *attrns;
06788     uint32 i, n;
06789     JSXML *attr, *kid;
06790 
06791     thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
06792     JS_ASSERT(thisns);
06793     if (thisns == ns)
06794         return JS_TRUE;
06795 
06796     for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
06797         attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
06798         if (!attr)
06799             continue;
06800         attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
06801         JS_ASSERT(attrns);
06802         if (attrns == ns)
06803             return JS_TRUE;
06804     }
06805 
06806     i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
06807     if (i != XML_NOT_FOUND)
06808         XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
06809 
06810     for (i = 0, n = xml->xml_kids.length; i < n; i++) {
06811         kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
06812         if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
06813             if (!xml_removeNamespace_helper(cx, kid, ns))
06814                 return JS_FALSE;
06815         }
06816     }
06817     return JS_TRUE;
06818 }
06819 
06820 static JSBool
06821 xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06822                     jsval *rval)
06823 {
06824     JSXML *xml;
06825     JSXMLNamespace *ns;
06826 
06827     NON_LIST_XML_METHOD_PROLOG;
06828     if (xml->xml_class != JSXML_CLASS_ELEMENT)
06829         goto done;
06830     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06831     if (!xml)
06832         return JS_FALSE;
06833 
06834     if (!NamespaceHelper(cx, NULL, 1, argv, rval))
06835         return JS_FALSE;
06836     JS_ASSERT(!JSVAL_IS_PRIMITIVE(*rval));
06837     ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
06838 
06839     /* NOTE: remove ns from each ancestor if not used by that ancestor. */
06840     if (!xml_removeNamespace_helper(cx, xml, ns))
06841         return JS_FALSE;
06842   done:
06843     *rval = OBJECT_TO_JSVAL(obj);
06844     return JS_TRUE;
06845 }
06846 
06847 static JSBool
06848 xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
06849 {
06850     JSXML *xml, *vxml, *kid;
06851     jsval value, id, junk;
06852     uint32 index;
06853     JSXMLQName *nameqn;
06854 
06855     NON_LIST_XML_METHOD_PROLOG;
06856     *rval = OBJECT_TO_JSVAL(obj);
06857     if (xml->xml_class != JSXML_CLASS_ELEMENT)
06858         goto done;
06859 
06860     value = argv[1];
06861     vxml = VALUE_IS_XML(cx, value)
06862            ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value))
06863            : NULL;
06864     if (!vxml) {
06865         if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1]))
06866             return JS_FALSE;
06867         value = argv[1];
06868     } else {
06869         vxml = DeepCopy(cx, vxml, NULL, 0);
06870         if (!vxml)
06871             return JS_FALSE;
06872         value = argv[1] = OBJECT_TO_JSVAL(vxml->object);
06873     }
06874 
06875     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06876     if (!xml)
06877         return JS_FALSE;
06878 
06879     id = argv[0];
06880     if (!js_IdIsIndex(id, &index)) {
06881         /*
06882          * Call function QName per spec, not ToXMLName, to avoid attribute
06883          * names.
06884          */
06885         if (!QNameHelper(cx, NULL, &js_QNameClass.base, 1, argv, rval))
06886             return JS_FALSE;
06887         JS_ASSERT(!JSVAL_IS_PRIMITIVE(*rval));
06888         nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
06889         
06890         id = JSVAL_VOID;
06891         index = xml->xml_kids.length;
06892         while (index != 0) {
06893             --index;
06894             kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
06895             if (kid && MatchElemName(nameqn, kid)) {
06896                 if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk))
06897                     return JS_FALSE;
06898                 if (!IndexToIdVal(cx, index, &id))
06899                     return JS_FALSE;
06900             }
06901         }
06902         if (JSVAL_IS_VOID(id))
06903             goto done;
06904     }
06905 
06906     if (!Replace(cx, xml, id, value))
06907         return JS_FALSE;
06908 
06909   done:
06910     *rval = OBJECT_TO_JSVAL(obj);
06911     return JS_TRUE;
06912 }
06913 
06914 static JSBool
06915 xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06916                 jsval *rval)
06917 {
06918     if (!StartNonListXMLMethod(cx, &obj, argv))
06919         return JS_FALSE;
06920 
06921     if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom),
06922                      &argv[0])) {
06923         return JS_FALSE;
06924     }
06925 
06926     *rval = OBJECT_TO_JSVAL(obj);
06927     return JS_TRUE;
06928 }
06929 
06930 static JSBool
06931 xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
06932                  jsval *rval)
06933 {
06934     JSXML *xml;
06935     jsval name;
06936     JSXMLQName *nameqn;
06937     JSString *namestr;
06938 
06939     NON_LIST_XML_METHOD_PROLOG;
06940     if (!JSXML_HAS_NAME(xml))
06941         return JS_TRUE;
06942 
06943     name = argv[0];
06944     if (!JSVAL_IS_PRIMITIVE(name) &&
06945         OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) {
06946         nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name));
06947         namestr = nameqn->localName;
06948     } else {
06949         if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0]))
06950             return JS_FALSE;
06951         name = argv[0];
06952         namestr = JSVAL_TO_STRING(name);
06953     }
06954 
06955     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06956     if (!xml)
06957         return JS_FALSE;
06958     xml->name->localName = namestr;
06959     return JS_TRUE;
06960 }
06961 
06962 static JSBool
06963 xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
06964 {
06965     JSXML *xml, *nsowner;
06966     jsval name;
06967     JSXMLQName *nameqn;
06968     JSObject *nameobj;
06969     JSXMLArray *nsarray;
06970     uint32 i, n;
06971     JSXMLNamespace *ns;
06972 
06973     NON_LIST_XML_METHOD_PROLOG;
06974     if (!JSXML_HAS_NAME(xml))
06975         return JS_TRUE;
06976 
06977     name = argv[0];
06978     if (!JSVAL_IS_PRIMITIVE(name) &&
06979         OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base &&
06980         !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)))
06981          ->uri) {
06982         name = argv[0] = STRING_TO_JSVAL(nameqn->localName);
06983     }
06984 
06985     nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name);
06986     if (!nameobj)
06987         return JS_FALSE;
06988     nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
06989 
06990     /* ECMA-357 13.4.4.35 Step 4. */
06991     if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
06992         nameqn->uri = cx->runtime->emptyString;
06993 
06994     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
06995     if (!xml)
06996         return JS_FALSE;
06997     xml->name = nameqn;
06998 
06999     /*
07000      * Erratum: nothing in 13.4.4.35 talks about making the name match the
07001      * in-scope namespaces, either by finding an in-scope namespace with a
07002      * matching uri and setting the new name's prefix to that namespace's
07003      * prefix, or by extending the in-scope namespaces for xml (which are in
07004      * xml->parent if xml is an attribute or a PI).
07005      */
07006     if (xml->xml_class == JSXML_CLASS_ELEMENT) {
07007         nsowner = xml;
07008     } else {
07009         if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
07010             return JS_TRUE;
07011         nsowner = xml->parent;
07012     }
07013 
07014     if (nameqn->prefix) {
07015         /*
07016          * The name being set has a prefix, which originally came from some
07017          * namespace object (which may be the null namespace, where both the
07018          * prefix and uri are the empty string).  We must go through a full
07019          * GetNamespace in case that namespace is in-scope in nsowner.
07020          *
07021          * If we find such an in-scope namespace, we return true right away,
07022          * in this block.  Otherwise, we fall through to the final return of
07023          * AddInScopeNamespace(cx, nsowner, ns).
07024          */
07025         ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
07026         if (!ns)
07027             return JS_FALSE;
07028 
07029         /* XXXbe have to test membership to see whether GetNamespace added */
07030         if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL))
07031             return JS_TRUE;
07032     } else {
07033         /*
07034          * At this point, we know nameqn->prefix is null, so nameqn->uri can't
07035          * be the empty string (the null namespace always uses the empty string
07036          * for both prefix and uri).
07037          *
07038          * This means we must inline GetNamespace and specialize it to match
07039          * uri only, never prefix.  If we find a namespace with nameqn's uri
07040          * already in nsowner->xml_namespaces, then all that we need do is set
07041          * nameqn->prefix to that namespace's prefix.
07042          *
07043          * If no such namespace exists, we can create one without going through
07044          * the constructor, because we know nameqn->uri is non-empty (so prefix
07045          * does not need to be converted from null to empty by QName).
07046          */
07047         JS_ASSERT(!IS_EMPTY(nameqn->uri));
07048 
07049         nsarray = &nsowner->xml_namespaces;
07050         for (i = 0, n = nsarray->length; i < n; i++) {
07051             ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace);
07052             if (ns && js_EqualStrings(ns->uri, nameqn->uri)) {
07053                 nameqn->prefix = ns->prefix;
07054                 return JS_TRUE;
07055             }
07056         }
07057 
07058         ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE);
07059         if (!ns)
07060             return JS_FALSE;
07061     }
07062 
07063     return AddInScopeNamespace(cx, nsowner, ns);
07064 }
07065 
07066 static JSBool
07067 xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
07068                  jsval *rval)
07069 {
07070     JSXML *xml, *nsowner;
07071     JSObject *nsobj, *qnobj;
07072     JSXMLNamespace *ns;
07073     jsval qnargv[2];
07074 
07075     NON_LIST_XML_METHOD_PROLOG;
07076     if (!JSXML_HAS_NAME(xml))
07077         return JS_TRUE;
07078 
07079     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
07080     if (!xml || !js_GetXMLQNameObject(cx, xml->name))
07081         return JS_FALSE;
07082 
07083     nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv);
07084     if (!nsobj)
07085         return JS_FALSE;
07086     ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
07087     ns->declared = JS_TRUE;
07088 
07089     qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj);
07090     qnargv[1] = OBJECT_TO_JSVAL(xml->name->object);
07091     qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv);
07092     if (!qnobj)
07093         return JS_FALSE;
07094 
07095     xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj);
07096 
07097     /*
07098      * Erratum: the spec fails to update the governing in-scope namespaces.
07099      * See the erratum noted in xml_setName, above.
07100      */
07101     if (xml->xml_class == JSXML_CLASS_ELEMENT) {
07102         nsowner = xml;
07103     } else {
07104         if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
07105             return JS_TRUE;
07106         nsowner = xml->parent;
07107     }
07108     return AddInScopeNamespace(cx, nsowner, ns);
07109 }
07110 
07111 /* XML and XMLList */
07112 static JSBool
07113 xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
07114 {
07115     JSXML *xml, *list, *kid, *vxml;
07116     uint32 i, n;
07117     JSBool ok;
07118     JSObject *kidobj;
07119     jsval v;
07120 
07121     XML_METHOD_PROLOG;
07122     list = xml_list_helper(cx, xml, rval);
07123     if (!list)
07124         return JS_FALSE;
07125 
07126     if (xml->xml_class == JSXML_CLASS_LIST) {
07127         ok = JS_TRUE;
07128         for (i = 0, n = xml->xml_kids.length; i < n; i++) {
07129             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
07130             if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
07131                 ok = js_EnterLocalRootScope(cx);
07132                 if (!ok)
07133                     break;
07134                 kidobj = js_GetXMLObject(cx, kid);
07135                 if (kidobj) {
07136                     ok = xml_text(cx, kidobj, argc, argv, &v);
07137                 } else {
07138                     ok = JS_FALSE;
07139                     v = JSVAL_NULL;
07140                 }
07141                 js_LeaveLocalRootScopeWithResult(cx, v);
07142                 if (!ok)
07143                     return JS_FALSE;
07144                 vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
07145                 if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
07146                     return JS_FALSE;
07147             }
07148         }
07149     } else {
07150         for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
07151             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
07152             if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
07153                 if (!Append(cx, list, kid))
07154                     return JS_FALSE;
07155             }
07156         }
07157     }
07158     return JS_TRUE;
07159 }
07160 
07161 /* XML and XMLList */
07162 static JSBool
07163 xml_toXMLString(JSContext *cx, JSObject *obj,