1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey E4X code, released August, 2004.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "jsversion.h"
41 :
42 : #if JS_HAS_XML_SUPPORT
43 :
44 : #include <math.h>
45 : #include <stdlib.h>
46 : #include <string.h>
47 :
48 : #include "mozilla/Util.h"
49 :
50 : #include "jstypes.h"
51 : #include "jsprf.h"
52 : #include "jsutil.h"
53 : #include "jsapi.h"
54 : #include "jsarray.h"
55 : #include "jsatom.h"
56 : #include "jsbool.h"
57 : #include "jscntxt.h"
58 : #include "jsfun.h"
59 : #include "jsgc.h"
60 : #include "jsgcmark.h"
61 : #include "jslock.h"
62 : #include "jsnum.h"
63 : #include "jsobj.h"
64 : #include "jsopcode.h"
65 : #include "jsscope.h"
66 : #include "jsscript.h"
67 : #include "jsstr.h"
68 : #include "jsxml.h"
69 :
70 : #include "frontend/Parser.h"
71 : #include "frontend/TokenStream.h"
72 : #include "vm/GlobalObject.h"
73 : #include "vm/MethodGuard.h"
74 : #include "vm/StringBuffer.h"
75 :
76 : #include "jsatominlines.h"
77 : #include "jsinferinlines.h"
78 : #include "jsobjinlines.h"
79 :
80 : #include "vm/Stack-inl.h"
81 : #include "vm/String-inl.h"
82 :
83 : #ifdef DEBUG
84 : #include <string.h> /* for #ifdef DEBUG memset calls */
85 : #endif
86 :
87 : using namespace mozilla;
88 : using namespace js;
89 : using namespace js::gc;
90 : using namespace js::types;
91 :
92 : template<class T, class U>
93 : struct IdentityOp
94 : {
95 : typedef JSBool (* compare)(const T *a, const U *b);
96 : };
97 :
98 : template<class T>
99 : static JSBool
100 0 : pointer_match(const T *a, const T *b)
101 : {
102 0 : return a == b;
103 : }
104 :
105 : /*
106 : * NOTES
107 : * - in the js shell, you must use the -x command line option, or call
108 : * options('xml') before compiling anything that uses XML literals
109 : *
110 : * TODO
111 : * - XXXbe patrol
112 : * - Fuse objects and their JSXML* private data into single GC-things
113 : * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
114 : * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
115 : */
116 :
117 : static inline bool
118 5170 : js_EnterLocalRootScope(JSContext *cx)
119 : {
120 5170 : return true;
121 : }
122 :
123 : static inline void
124 216 : js_LeaveLocalRootScope(JSContext *cx)
125 : {
126 216 : }
127 :
128 : static inline void
129 0 : js_LeaveLocalRootScopeWithResult(JSContext *cx, Value rval)
130 : {
131 0 : }
132 :
133 : static inline void
134 4954 : js_LeaveLocalRootScopeWithResult(JSContext *cx, void *rval)
135 : {
136 4954 : }
137 :
138 : /*
139 : * Random utilities and global functions.
140 : */
141 : const char js_AttributeName_str[] = "AttributeName";
142 : const char js_isXMLName_str[] = "isXMLName";
143 : const char js_XMLList_str[] = "XMLList";
144 : const char js_localName_str[] = "localName";
145 : const char js_xml_parent_str[] = "parent";
146 : const char js_prefix_str[] = "prefix";
147 : const char js_toXMLString_str[] = "toXMLString";
148 : const char js_uri_str[] = "uri";
149 :
150 : const char js_amp_entity_str[] = "&";
151 : const char js_gt_entity_str[] = ">";
152 : const char js_lt_entity_str[] = "<";
153 : const char js_quot_entity_str[] = """;
154 : const char js_leftcurly_entity_str[] = "{";
155 :
156 : #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*')
157 :
158 : static JSBool
159 : GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
160 :
161 : static JSBool
162 2359431 : IsDeclared(const JSObject *obj)
163 : {
164 : jsval v;
165 :
166 2359431 : JS_ASSERT(obj->getClass() == &NamespaceClass);
167 2359431 : v = obj->getNamespaceDeclared();
168 2359431 : JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
169 2359431 : return v == JSVAL_TRUE;
170 : }
171 :
172 : static JSBool
173 0 : xml_isXMLName(JSContext *cx, unsigned argc, jsval *vp)
174 : {
175 0 : *vp = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argc ? vp[2] : JSVAL_VOID));
176 0 : return JS_TRUE;
177 : }
178 :
179 : size_t sE4XObjectsCreated = 0;
180 :
181 : /*
182 : * This wrapper is needed because NewBuiltinClassInstance doesn't
183 : * call the constructor, and we need a place to set the
184 : * HAS_EQUALITY bit.
185 : */
186 : static inline JSObject *
187 4734778 : NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
188 : {
189 4734778 : if (!cx->runningWithTrustedPrincipals())
190 4734747 : ++sE4XObjectsCreated;
191 :
192 4734778 : return NewBuiltinClassInstance(cx, clasp);
193 : }
194 :
195 : #define DEFINE_GETTER(name,code) \
196 : static JSBool \
197 : name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
198 : { \
199 : code; \
200 : return true; \
201 : }
202 :
203 : /*
204 : * Namespace class and library functions.
205 : */
206 0 : DEFINE_GETTER(NamePrefix_getter,
207 : if (obj->getClass() == &NamespaceClass) *vp = obj->getNamePrefixVal())
208 0 : DEFINE_GETTER(NameURI_getter,
209 : if (obj->getClass() == &NamespaceClass) *vp = obj->getNameURIVal())
210 :
211 : static JSBool
212 0 : namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
213 : {
214 : JSObject *obj2;
215 :
216 0 : JS_ASSERT(v->isObjectOrNull());
217 0 : obj2 = v->toObjectOrNull();
218 0 : *bp = (!obj2 || obj2->getClass() != &NamespaceClass)
219 : ? JS_FALSE
220 0 : : EqualStrings(obj->getNameURI(), obj2->getNameURI());
221 0 : return JS_TRUE;
222 : }
223 :
224 : JS_FRIEND_DATA(Class) js::NamespaceClass = {
225 : "Namespace",
226 : JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) |
227 : JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
228 : JS_PropertyStub, /* addProperty */
229 : JS_PropertyStub, /* delProperty */
230 : JS_PropertyStub, /* getProperty */
231 : JS_StrictPropertyStub, /* setProperty */
232 : JS_EnumerateStub,
233 : JS_ResolveStub,
234 : JS_ConvertStub,
235 : NULL, /* finalize */
236 : NULL, /* checkAccess */
237 : NULL, /* call */
238 : NULL, /* construct */
239 : NULL, /* hasInstance */
240 : NULL, /* trace */
241 : {
242 : namespace_equality,
243 : NULL, /* outerObject */
244 : NULL, /* innerObject */
245 : NULL, /* iteratorObject */
246 : NULL, /* wrappedObject */
247 : }
248 : };
249 :
250 : #define NAMESPACE_ATTRS \
251 : (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
252 :
253 : static JSPropertySpec namespace_props[] = {
254 : {js_prefix_str, 0, NAMESPACE_ATTRS, NamePrefix_getter, 0},
255 : {js_uri_str, 0, NAMESPACE_ATTRS, NameURI_getter, 0},
256 : {0,0,0,0,0}
257 : };
258 :
259 : static JSBool
260 0 : namespace_toString(JSContext *cx, unsigned argc, Value *vp)
261 : {
262 0 : JSObject *obj = ToObject(cx, &vp[1]);
263 0 : if (!obj)
264 0 : return JS_FALSE;
265 0 : if (!obj->isNamespace()) {
266 0 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &NamespaceClass);
267 0 : return JS_FALSE;
268 : }
269 0 : *vp = obj->getNameURIVal();
270 0 : return JS_TRUE;
271 : }
272 :
273 : static JSFunctionSpec namespace_methods[] = {
274 : JS_FN(js_toString_str, namespace_toString, 0,0),
275 : JS_FS_END
276 : };
277 :
278 : static JSObject *
279 2360827 : NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared)
280 : {
281 : JSObject *obj;
282 :
283 2360827 : obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass);
284 2360827 : if (!obj)
285 0 : return NULL;
286 2360827 : JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
287 2360827 : JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
288 2360827 : JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
289 :
290 : /* Per ECMA-357, 13.2.5, these properties must be "own". */
291 2360827 : if (!JS_DefineProperties(cx, obj, namespace_props))
292 0 : return NULL;
293 :
294 2360827 : if (prefix)
295 2360827 : obj->setNamePrefix(prefix);
296 2360827 : if (uri)
297 2360827 : obj->setNameURI(uri);
298 2360827 : if (declared)
299 1378 : obj->setNamespaceDeclared(JSVAL_TRUE);
300 2360827 : return obj;
301 : }
302 :
303 : /*
304 : * QName class and library functions.
305 : */
306 0 : DEFINE_GETTER(QNameNameURI_getter,
307 : if (obj->getClass() == &QNameClass)
308 : *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal())
309 0 : DEFINE_GETTER(QNameLocalName_getter,
310 : if (obj->getClass() == &QNameClass)
311 : *vp = obj->getQNameLocalNameVal())
312 :
313 : static JSBool
314 0 : qname_identity(JSObject *qna, const JSObject *qnb)
315 : {
316 0 : JSLinearString *uri1 = qna->getNameURI();
317 0 : JSLinearString *uri2 = qnb->getNameURI();
318 :
319 0 : if (!uri1 ^ !uri2)
320 0 : return JS_FALSE;
321 0 : if (uri1 && !EqualStrings(uri1, uri2))
322 0 : return JS_FALSE;
323 0 : return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName());
324 : }
325 :
326 : static JSBool
327 0 : qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp)
328 : {
329 : JSObject *obj2;
330 :
331 0 : obj2 = v->toObjectOrNull();
332 0 : *bp = (!obj2 || obj2->getClass() != &QNameClass)
333 : ? JS_FALSE
334 0 : : qname_identity(qn, obj2);
335 0 : return JS_TRUE;
336 : }
337 :
338 : JS_FRIEND_DATA(Class) js::QNameClass = {
339 : "QName",
340 : JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
341 : JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
342 : JS_PropertyStub, /* addProperty */
343 : JS_PropertyStub, /* delProperty */
344 : JS_PropertyStub, /* getProperty */
345 : JS_StrictPropertyStub, /* setProperty */
346 : JS_EnumerateStub,
347 : JS_ResolveStub,
348 : JS_ConvertStub,
349 : NULL, /* finalize */
350 : NULL, /* checkAccess */
351 : NULL, /* call */
352 : NULL, /* construct */
353 : NULL, /* hasInstance */
354 : NULL, /* trace */
355 : {
356 : qname_equality,
357 : NULL, /* outerObject */
358 : NULL, /* innerObject */
359 : NULL, /* iteratorObject */
360 : NULL, /* wrappedObject */
361 : }
362 : };
363 :
364 : /*
365 : * Classes for the ECMA-357-internal types AttributeName and AnyName, which
366 : * are like QName, except that they have no property getters. They share the
367 : * qname_toString method, and therefore are exposed as constructable objects
368 : * in this implementation.
369 : */
370 : JS_FRIEND_DATA(Class) js::AttributeNameClass = {
371 : js_AttributeName_str,
372 : JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
373 : JSCLASS_IS_ANONYMOUS,
374 : JS_PropertyStub, /* addProperty */
375 : JS_PropertyStub, /* delProperty */
376 : JS_PropertyStub, /* getProperty */
377 : JS_StrictPropertyStub, /* setProperty */
378 : JS_EnumerateStub,
379 : JS_ResolveStub,
380 : JS_ConvertStub
381 : };
382 :
383 : JS_FRIEND_DATA(Class) js::AnyNameClass = {
384 : js_AnyName_str,
385 : JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
386 : JSCLASS_IS_ANONYMOUS,
387 : JS_PropertyStub, /* addProperty */
388 : JS_PropertyStub, /* delProperty */
389 : JS_PropertyStub, /* getProperty */
390 : JS_StrictPropertyStub, /* setProperty */
391 : JS_EnumerateStub,
392 : JS_ResolveStub,
393 : JS_ConvertStub
394 : };
395 :
396 : #define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
397 :
398 : static JSPropertySpec qname_props[] = {
399 : {js_uri_str, 0, QNAME_ATTRS, QNameNameURI_getter, 0},
400 : {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0},
401 : {0,0,0,0,0}
402 : };
403 :
404 : static JSString *
405 63 : ConvertQNameToString(JSContext *cx, JSObject *obj)
406 : {
407 63 : JS_ASSERT(obj->isQName());
408 63 : JSString *uri = obj->getNameURI();
409 : JSString *str;
410 63 : if (!uri) {
411 : /* No uri means wildcard qualifier. */
412 18 : str = cx->runtime->atomState.starQualifierAtom;
413 45 : } else if (uri->empty()) {
414 : /* Empty string for uri means localName is in no namespace. */
415 36 : str = cx->runtime->emptyString;
416 : } else {
417 9 : JSString *qualstr = cx->runtime->atomState.qualifierAtom;
418 9 : str = js_ConcatStrings(cx, uri, qualstr);
419 9 : if (!str)
420 0 : return NULL;
421 : }
422 63 : str = js_ConcatStrings(cx, str, obj->getQNameLocalName());
423 63 : if (!str)
424 0 : return NULL;
425 :
426 63 : if (obj->getClass() == &AttributeNameClass) {
427 0 : JS::Anchor<JSString *> anchor(str);
428 0 : size_t length = str->length();
429 0 : jschar *chars = (jschar *) cx->malloc_((length + 2) * sizeof(jschar));
430 0 : if (!chars)
431 0 : return JS_FALSE;
432 0 : *chars = '@';
433 0 : const jschar *strChars = str->getChars(cx);
434 0 : if (!strChars) {
435 0 : cx->free_(chars);
436 0 : return NULL;
437 : }
438 0 : js_strncpy(chars + 1, strChars, length);
439 0 : chars[++length] = 0;
440 0 : str = js_NewString(cx, chars, length);
441 0 : if (!str) {
442 0 : cx->free_(chars);
443 0 : return NULL;
444 : }
445 : }
446 63 : return str;
447 : }
448 :
449 : static JSBool
450 36 : qname_toString(JSContext *cx, unsigned argc, Value *vp)
451 : {
452 36 : JSObject *obj = ToObject(cx, &vp[1]);
453 36 : if (!obj)
454 0 : return false;
455 :
456 36 : if (!obj->isQName()) {
457 0 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &QNameClass);
458 0 : return false;
459 : }
460 :
461 36 : JSString *str = ConvertQNameToString(cx, obj);
462 36 : if (!str)
463 0 : return false;
464 :
465 36 : vp->setString(str);
466 36 : return true;
467 : }
468 :
469 : static JSFunctionSpec qname_methods[] = {
470 : JS_FN(js_toString_str, qname_toString, 0,0),
471 : JS_FS_END
472 : };
473 :
474 :
475 : static bool
476 2374457 : InitXMLQName(JSContext *cx, JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
477 : JSAtom *localName)
478 : {
479 2374457 : JS_ASSERT(obj->isQName());
480 2374457 : JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
481 2374457 : JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
482 2374457 : JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
483 :
484 : /* Per ECMA-357, 13.3.5, these properties must be "own". */
485 2374457 : if (!JS_DefineProperties(cx, obj, qname_props))
486 0 : return false;
487 :
488 2374457 : if (uri)
489 2373818 : obj->setNameURI(uri);
490 2374457 : if (prefix)
491 2373809 : obj->setNamePrefix(prefix);
492 2374457 : if (localName)
493 2374457 : obj->setQNameLocalName(localName);
494 2374457 : return true;
495 : }
496 :
497 : static JSObject *
498 2361395 : NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
499 : JSAtom *localName)
500 : {
501 2361395 : JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass);
502 2361395 : if (!obj)
503 0 : return NULL;
504 2361395 : if (!InitXMLQName(cx, obj, uri, prefix, localName))
505 0 : return NULL;
506 2361395 : return obj;
507 : }
508 :
509 : static JSObject *
510 0 : NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
511 : JSAtom *localName)
512 : {
513 : /*
514 : * AttributeName is an internal anonymous class which instances are not
515 : * exposed to scripts.
516 : */
517 0 : JSObject *parent = GetGlobalForScopeChain(cx);
518 0 : JSObject *obj = NewObjectWithGivenProto(cx, &AttributeNameClass, NULL, parent);
519 0 : if (!obj)
520 0 : return NULL;
521 0 : JS_ASSERT(obj->isQName());
522 0 : if (!InitXMLQName(cx, obj, uri, prefix, localName))
523 0 : return NULL;
524 0 : return obj;
525 : }
526 :
527 : JSObject *
528 45 : js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval)
529 : {
530 : Value argv[2];
531 :
532 : /*
533 : * ECMA-357 11.1.2,
534 : * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
535 : * production, step 2.
536 : */
537 90 : if (nsval.isObject() &&
538 45 : nsval.toObject().getClass() == &AnyNameClass) {
539 0 : argv[0].setNull();
540 : } else {
541 45 : argv[0] = nsval;
542 : }
543 45 : argv[1] = lnval;
544 45 : return JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 2, argv);
545 : }
546 :
547 : static JSBool
548 207 : IsXMLName(const jschar *cp, size_t n)
549 : {
550 : JSBool rv;
551 : jschar c;
552 :
553 207 : rv = JS_FALSE;
554 207 : if (n != 0 && unicode::IsXMLNamespaceStart(*cp)) {
555 441 : while (--n != 0) {
556 27 : c = *++cp;
557 27 : if (!unicode::IsXMLNamespacePart(c))
558 0 : return rv;
559 : }
560 207 : rv = JS_TRUE;
561 : }
562 207 : return rv;
563 : }
564 :
565 : JSBool
566 207 : js_IsXMLName(JSContext *cx, jsval v)
567 : {
568 207 : JSLinearString *name = NULL;
569 : JSErrorReporter older;
570 :
571 : /*
572 : * Inline specialization of the QName constructor called with v passed as
573 : * the only argument, to compute the localName for the constructed qname,
574 : * without actually allocating the object or computing its uri and prefix.
575 : * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
576 : */
577 414 : if (!JSVAL_IS_PRIMITIVE(v) &&
578 207 : JSVAL_TO_OBJECT(v)->isQName()) {
579 207 : name = JSVAL_TO_OBJECT(v)->getQNameLocalName();
580 : } else {
581 0 : older = JS_SetErrorReporter(cx, NULL);
582 0 : JSString *str = ToString(cx, v);
583 0 : if (str)
584 0 : name = str->ensureLinear(cx);
585 0 : JS_SetErrorReporter(cx, older);
586 0 : if (!name) {
587 0 : JS_ClearPendingException(cx);
588 0 : return JS_FALSE;
589 : }
590 : }
591 :
592 207 : return IsXMLName(name->chars(), name->length());
593 : }
594 :
595 : /*
596 : * When argc is -1, it indicates argv is empty but the code should behave as
597 : * if argc is 1 and argv[0] is JSVAL_VOID.
598 : */
599 : static JSBool
600 334 : NamespaceHelper(JSContext *cx, int argc, jsval *argv, jsval *rval)
601 : {
602 : jsval urival, prefixval;
603 : JSObject *uriobj;
604 : JSBool isNamespace, isQName;
605 : Class *clasp;
606 : JSLinearString *empty, *prefix, *uri;
607 :
608 334 : isNamespace = isQName = JS_FALSE;
609 : #ifdef __GNUC__ /* suppress bogus gcc warnings */
610 334 : uriobj = NULL;
611 : #endif
612 334 : if (argc <= 0) {
613 316 : urival = JSVAL_VOID;
614 : } else {
615 18 : urival = argv[argc > 1];
616 18 : if (!JSVAL_IS_PRIMITIVE(urival)) {
617 9 : uriobj = JSVAL_TO_OBJECT(urival);
618 9 : clasp = uriobj->getClass();
619 9 : isNamespace = (clasp == &NamespaceClass);
620 9 : isQName = (clasp == &QNameClass);
621 : }
622 : }
623 :
624 : /* Namespace called as function. */
625 334 : if (argc == 1 && isNamespace) {
626 : /* Namespace called with one Namespace argument is identity. */
627 0 : *rval = urival;
628 0 : return JS_TRUE;
629 : }
630 :
631 334 : JSObject *obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass);
632 334 : if (!obj)
633 0 : return JS_FALSE;
634 :
635 : /* Per ECMA-357, 13.2.5, these properties must be "own". */
636 334 : if (!JS_DefineProperties(cx, obj, namespace_props))
637 0 : return JS_FALSE;
638 :
639 334 : *rval = OBJECT_TO_JSVAL(obj);
640 :
641 334 : empty = cx->runtime->emptyString;
642 334 : obj->setNamePrefix(empty);
643 334 : obj->setNameURI(empty);
644 :
645 334 : if (argc == 1 || argc == -1) {
646 0 : if (isNamespace) {
647 0 : obj->setNameURI(uriobj->getNameURI());
648 0 : obj->setNamePrefix(uriobj->getNamePrefix());
649 0 : } else if (isQName && (uri = uriobj->getNameURI())) {
650 0 : obj->setNameURI(uri);
651 0 : obj->setNamePrefix(uriobj->getNamePrefix());
652 : } else {
653 0 : JSString *str = ToString(cx, urival);
654 0 : if (!str)
655 0 : return JS_FALSE;
656 0 : uri = str->ensureLinear(cx);
657 0 : if (!uri)
658 0 : return JS_FALSE;
659 0 : obj->setNameURI(uri);
660 0 : if (!uri->empty())
661 0 : obj->clearNamePrefix();
662 0 : }
663 334 : } else if (argc == 2) {
664 18 : if (!isQName || !(uri = uriobj->getNameURI())) {
665 18 : JSString *str = ToString(cx, urival);
666 18 : if (!str)
667 0 : return JS_FALSE;
668 18 : uri = str->ensureLinear(cx);
669 18 : if (!uri)
670 0 : return JS_FALSE;
671 : }
672 18 : obj->setNameURI(uri);
673 :
674 18 : prefixval = argv[0];
675 18 : if (uri->empty()) {
676 18 : if (!JSVAL_IS_VOID(prefixval)) {
677 18 : JSString *str = ToString(cx, prefixval);
678 18 : if (!str)
679 0 : return JS_FALSE;
680 18 : if (!str->empty()) {
681 0 : JSAutoByteString bytes;
682 0 : if (js_ValueToPrintable(cx, StringValue(str), &bytes)) {
683 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
684 0 : JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
685 : }
686 0 : return JS_FALSE;
687 : }
688 : }
689 0 : } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
690 0 : obj->clearNamePrefix();
691 : } else {
692 0 : JSString *str = ToString(cx, prefixval);
693 0 : if (!str)
694 0 : return JS_FALSE;
695 0 : prefix = str->ensureLinear(cx);
696 0 : if (!prefix)
697 0 : return JS_FALSE;
698 0 : obj->setNamePrefix(prefix);
699 : }
700 : }
701 334 : return JS_TRUE;
702 : }
703 :
704 : static JSBool
705 334 : Namespace(JSContext *cx, unsigned argc, Value *vp)
706 : {
707 334 : return NamespaceHelper(cx, argc, vp + 2, vp);
708 : }
709 :
710 : /*
711 : * When argc is -1, it indicates argv is empty but the code should behave as
712 : * if argc is 1 and argv[0] is JSVAL_VOID.
713 : */
714 : static JSBool
715 12222 : QNameHelper(JSContext *cx, int argc, jsval *argv, jsval *rval)
716 : {
717 : jsval nameval, nsval;
718 : JSBool isQName, isNamespace;
719 : JSObject *qn;
720 : JSLinearString *uri, *prefix;
721 : JSObject *obj2;
722 :
723 : JSAtom *name;
724 12222 : if (argc <= 0) {
725 27 : nameval = JSVAL_VOID;
726 27 : isQName = JS_FALSE;
727 : } else {
728 12195 : nameval = argv[argc > 1];
729 : isQName =
730 12195 : !JSVAL_IS_PRIMITIVE(nameval) &&
731 12195 : JSVAL_TO_OBJECT(nameval)->getClass() == &QNameClass;
732 : }
733 :
734 : /* QName called as function. */
735 12222 : if (argc == 1 && isQName) {
736 : /* QName called with one QName argument is identity. */
737 0 : *rval = nameval;
738 0 : return JS_TRUE;
739 : }
740 :
741 : /* Create and return a new QName object exactly as if constructed. */
742 12222 : JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass);
743 12222 : if (!obj)
744 0 : return JS_FALSE;
745 12222 : *rval = OBJECT_TO_JSVAL(obj);
746 :
747 12222 : if (isQName) {
748 : /* If namespace is not specified and name is a QName, clone it. */
749 0 : qn = JSVAL_TO_OBJECT(nameval);
750 0 : if (argc == 1) {
751 0 : uri = qn->getNameURI();
752 0 : prefix = qn->getNamePrefix();
753 0 : name = qn->getQNameLocalName();
754 0 : goto out;
755 : }
756 :
757 : /* Namespace and qname were passed -- use the qname's localName. */
758 0 : nameval = qn->getQNameLocalNameVal();
759 : }
760 :
761 12222 : if (argc == 0) {
762 27 : name = cx->runtime->emptyString;
763 12195 : } else if (argc < 0) {
764 0 : name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
765 : } else {
766 12195 : if (!js_ValueToAtom(cx, nameval, &name))
767 0 : return false;
768 : }
769 :
770 12222 : if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
771 45 : nsval = argv[0];
772 12177 : } else if (IS_STAR(name)) {
773 639 : nsval = JSVAL_NULL;
774 : } else {
775 11538 : if (!js_GetDefaultXMLNamespace(cx, &nsval))
776 0 : return JS_FALSE;
777 11538 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
778 11538 : JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
779 11538 : &NamespaceClass);
780 : }
781 :
782 12222 : if (JSVAL_IS_NULL(nsval)) {
783 : /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
784 639 : prefix = uri = NULL;
785 : } else {
786 : /*
787 : * Inline specialization of the Namespace constructor called with
788 : * nsval passed as the only argument, to compute the uri and prefix
789 : * for the constructed namespace, without actually allocating the
790 : * object or computing other members. See ECMA-357 13.3.2 6(a) and
791 : * 13.2.2.
792 : */
793 11583 : isNamespace = isQName = JS_FALSE;
794 11583 : if (!JSVAL_IS_PRIMITIVE(nsval)) {
795 11583 : obj2 = JSVAL_TO_OBJECT(nsval);
796 11583 : isNamespace = (obj2->getClass() == &NamespaceClass);
797 11583 : isQName = (obj2->getClass() == &QNameClass);
798 : }
799 : #ifdef __GNUC__ /* suppress bogus gcc warnings */
800 0 : else obj2 = NULL;
801 : #endif
802 :
803 11583 : if (isNamespace) {
804 11574 : uri = obj2->getNameURI();
805 11574 : prefix = obj2->getNamePrefix();
806 9 : } else if (isQName && (uri = obj2->getNameURI())) {
807 0 : JS_ASSERT(argc > 1);
808 0 : prefix = obj2->getNamePrefix();
809 : } else {
810 9 : JS_ASSERT(argc > 1);
811 9 : JSString *str = ToString(cx, nsval);
812 9 : if (!str)
813 0 : return JS_FALSE;
814 9 : uri = str->ensureLinear(cx);
815 9 : if (!uri)
816 0 : return JS_FALSE;
817 9 : argv[0] = STRING_TO_JSVAL(uri); /* local root */
818 :
819 : /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
820 9 : prefix = uri->empty() ? cx->runtime->emptyString : NULL;
821 : }
822 : }
823 :
824 : out:
825 12222 : return InitXMLQName(cx, obj, uri, prefix, name);
826 : }
827 :
828 : static JSBool
829 12222 : QName(JSContext *cx, unsigned argc, Value *vp)
830 : {
831 12222 : return QNameHelper(cx, argc, vp + 2, vp);
832 : }
833 :
834 : /*
835 : * XMLArray library functions.
836 : */
837 : static JSBool
838 0 : namespace_identity(const JSObject *nsa, const JSObject *nsb)
839 : {
840 0 : JSLinearString *prefixa = nsa->getNamePrefix();
841 0 : JSLinearString *prefixb = nsb->getNamePrefix();
842 :
843 0 : if (prefixa && prefixb) {
844 0 : if (!EqualStrings(prefixa, prefixb))
845 0 : return JS_FALSE;
846 : } else {
847 0 : if (prefixa || prefixb)
848 0 : return JS_FALSE;
849 : }
850 0 : return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
851 : }
852 :
853 : static JSBool
854 0 : attr_identity(const JSXML *xmla, const JSXML *xmlb)
855 : {
856 0 : return qname_identity(xmla->name, xmlb->name);
857 : }
858 :
859 : void
860 50 : js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSXML> *cursor)
861 : {
862 50 : for (; cursor; cursor = cursor->next) {
863 0 : if (cursor->root)
864 0 : MarkXML(trc, &(HeapPtr<JSXML> &)cursor->root, "cursor_root");
865 : }
866 50 : }
867 :
868 : void
869 25 : js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSObject> *cursor)
870 : {
871 25 : for (; cursor; cursor = cursor->next) {
872 0 : if (cursor->root)
873 0 : MarkObject(trc, &(HeapPtr<JSObject> &)cursor->root, "cursor_root");
874 : }
875 25 : }
876 :
877 : template<class T>
878 : static HeapPtr<T> *
879 4723987 : ReallocateVector(HeapPtr<T> *vector, size_t count)
880 : {
881 : #if JS_BITS_PER_WORD == 32
882 4723987 : if (count > ~(size_t)0 / sizeof(HeapPtr<T>))
883 0 : return NULL;
884 : #endif
885 :
886 4723987 : size_t size = count * sizeof(HeapPtr<T>);
887 4723987 : return (HeapPtr<T> *) OffTheBooks::realloc_(vector, size);
888 : }
889 :
890 : /* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
891 : template<class T>
892 : bool
893 : JSXMLArray<T>::setCapacity(JSContext *cx, uint32_t newCapacity)
894 : {
895 7083383 : if (newCapacity == 0) {
896 : /* We could let realloc(p, 0) free this, but purify gets confused. */
897 2361693 : if (vector) {
898 0 : if (cx)
899 0 : cx->free_(vector);
900 : else
901 0 : Foreground::free_(vector);
902 : }
903 2361693 : vector = NULL;
904 : } else {
905 4721690 : HeapPtr<T> *tmp = ReallocateVector(vector, newCapacity);
906 4721690 : if (!tmp) {
907 0 : if (cx)
908 0 : JS_ReportOutOfMemory(cx);
909 0 : return false;
910 : }
911 4721690 : vector = tmp;
912 : }
913 7083383 : capacity = JSXML_PRESET_CAPACITY | newCapacity;
914 7083383 : return true;
915 : }
916 :
917 : template<class T>
918 : void
919 : JSXMLArray<T>::trim()
920 : {
921 2027 : if (capacity & JSXML_PRESET_CAPACITY)
922 0 : return;
923 2027 : if (length < capacity)
924 0 : setCapacity(NULL, length);
925 : }
926 :
927 : template<class T>
928 : void
929 : JSXMLArray<T>::finish(FreeOp *fop)
930 : {
931 7092858 : if (!fop->runtime()->gcRunning) {
932 : /* We need to clear these to trigger a write barrier. */
933 2044 : for (uint32_t i = 0; i < length; i++)
934 189 : vector[i].~HeapPtr<T>();
935 : }
936 :
937 7092858 : fop->free_(vector);
938 :
939 7092858 : while (JSXMLArrayCursor<T> *cursor = cursors)
940 0 : cursor->disconnect();
941 :
942 : #ifdef DEBUG
943 7092858 : memset(this, 0xd5, sizeof *this);
944 : #endif
945 7092858 : }
946 :
947 : #define XML_NOT_FOUND UINT32_MAX
948 :
949 : template<class T, class U>
950 : static uint32_t
951 1504 : XMLArrayFindMember(const JSXMLArray<T> *array, U *elt, typename IdentityOp<T, U>::compare identity)
952 : {
953 : HeapPtr<T> *vector;
954 : uint32_t i, n;
955 :
956 : /* The identity op must not reallocate array->vector. */
957 1504 : vector = array->vector;
958 1504 : for (i = 0, n = array->length; i < n; i++) {
959 0 : if (identity(vector[i].get(), elt))
960 0 : return i;
961 : }
962 1504 : return XML_NOT_FOUND;
963 : }
964 :
965 : /*
966 : * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
967 : * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
968 : * should be greater than increment.
969 : */
970 : #define LINEAR_THRESHOLD 256
971 : #define LINEAR_INCREMENT 32
972 :
973 : template<class T>
974 : static JSBool
975 3927 : XMLArrayAddMember(JSContext *cx, JSXMLArray<T> *array, uint32_t index, T *elt)
976 : {
977 : uint32_t capacity, i;
978 : int log2;
979 : HeapPtr<T> *vector;
980 :
981 3927 : if (index >= array->length) {
982 3927 : if (index >= JSXML_CAPACITY(array)) {
983 : /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
984 2297 : capacity = index + 1;
985 2297 : if (index >= LINEAR_THRESHOLD) {
986 0 : capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
987 : } else {
988 2297 : JS_CEILING_LOG2(log2, capacity);
989 2297 : capacity = JS_BIT(log2);
990 : }
991 2297 : if (!(vector = ReallocateVector(array->vector, capacity))) {
992 0 : JS_ReportOutOfMemory(cx);
993 0 : return JS_FALSE;
994 : }
995 2297 : array->capacity = capacity;
996 2297 : array->vector = vector;
997 2297 : for (i = array->length; i < index; i++)
998 0 : vector[i].init(NULL);
999 : }
1000 3927 : array->vector[index].init(NULL);
1001 3927 : array->length = index + 1;
1002 : }
1003 :
1004 3927 : array->vector[index] = elt;
1005 3927 : return JS_TRUE;
1006 : }
1007 :
1008 : template<class T>
1009 : static JSBool
1010 0 : XMLArrayInsert(JSContext *cx, JSXMLArray<T> *array, uint32_t i, uint32_t n)
1011 : {
1012 : uint32_t j, k;
1013 : JSXMLArrayCursor<T> *cursor;
1014 :
1015 0 : j = array->length;
1016 0 : JS_ASSERT(i <= j);
1017 0 : if (!array->setCapacity(cx, j + n))
1018 0 : return JS_FALSE;
1019 :
1020 0 : k = j;
1021 0 : while (k != j + n) {
1022 0 : array->vector[k].init(NULL);
1023 0 : k++;
1024 : }
1025 :
1026 0 : array->length = j + n;
1027 0 : JS_ASSERT(n != (uint32_t)-1);
1028 0 : while (j != i) {
1029 0 : --j;
1030 0 : array->vector[j + n] = array->vector[j];
1031 : }
1032 :
1033 0 : for (cursor = array->cursors; cursor; cursor = cursor->next) {
1034 0 : if (cursor->index > i)
1035 0 : cursor->index += n;
1036 : }
1037 0 : return JS_TRUE;
1038 : }
1039 :
1040 : template<class T>
1041 : static T *
1042 0 : XMLArrayDelete(JSContext *cx, JSXMLArray<T> *array, uint32_t index, JSBool compress)
1043 : {
1044 : uint32_t length;
1045 : HeapPtr<T> *vector;
1046 : T *elt;
1047 : JSXMLArrayCursor<T> *cursor;
1048 :
1049 0 : length = array->length;
1050 0 : if (index >= length)
1051 0 : return NULL;
1052 :
1053 0 : vector = array->vector;
1054 0 : elt = vector[index];
1055 0 : if (compress) {
1056 0 : vector[length - 1].~HeapPtr<T>();
1057 0 : while (++index < length)
1058 0 : vector[index-1] = vector[index];
1059 0 : array->length = length - 1;
1060 0 : array->capacity = JSXML_CAPACITY(array);
1061 : } else {
1062 0 : vector[index] = NULL;
1063 : }
1064 :
1065 0 : for (cursor = array->cursors; cursor; cursor = cursor->next) {
1066 0 : if (cursor->index > index)
1067 0 : --cursor->index;
1068 : }
1069 0 : return elt;
1070 : }
1071 :
1072 : template<class T>
1073 : static void
1074 2027 : XMLArrayTruncate(JSContext *cx, JSXMLArray<T> *array, uint32_t length)
1075 : {
1076 : HeapPtr<T> *vector;
1077 :
1078 2027 : JS_ASSERT(!array->cursors);
1079 2027 : if (length >= array->length)
1080 649 : return;
1081 :
1082 2756 : for (uint32_t i = length; i < array->length; i++)
1083 1378 : array->vector[i].~HeapPtr<T>();
1084 :
1085 1378 : if (length == 0) {
1086 1378 : if (array->vector)
1087 1378 : cx->free_(array->vector);
1088 1378 : vector = NULL;
1089 : } else {
1090 0 : vector = ReallocateVector(array->vector, length);
1091 0 : if (!vector)
1092 0 : return;
1093 : }
1094 :
1095 1378 : if (array->length > length)
1096 1378 : array->length = length;
1097 1378 : array->capacity = length;
1098 1378 : array->vector = vector;
1099 : }
1100 :
1101 : #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, e, f)
1102 : #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, e, f) != \
1103 : XML_NOT_FOUND)
1104 : #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1105 : ? (a)->vector[i].get() \
1106 : : NULL)
1107 : #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1108 : if ((a)->length <= (i)) { \
1109 : (a)->length = (i) + 1; \
1110 : ((a)->vector[i].init(e)); \
1111 : } else { \
1112 : ((a)->vector[i] = e); \
1113 : } \
1114 : JS_END_MACRO
1115 : #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, e)
1116 : #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1117 : #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1118 : #define XMLARRAY_DELETE(x,a,i,c,t) (XMLArrayDelete<t>(x, a, i, c))
1119 : #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1120 :
1121 : /*
1122 : * Define XML setting property strings and constants early, so everyone can
1123 : * use the same names.
1124 : */
1125 : static const char js_ignoreComments_str[] = "ignoreComments";
1126 : static const char js_ignoreProcessingInstructions_str[]
1127 : = "ignoreProcessingInstructions";
1128 : static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1129 : static const char js_prettyPrinting_str[] = "prettyPrinting";
1130 : static const char js_prettyIndent_str[] = "prettyIndent";
1131 :
1132 : #define XSF_IGNORE_COMMENTS JS_BIT(0)
1133 : #define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1134 : #define XSF_IGNORE_WHITESPACE JS_BIT(2)
1135 : #define XSF_PRETTY_PRINTING JS_BIT(3)
1136 :
1137 : static JSPropertySpec xml_static_props[] = {
1138 : {js_ignoreComments_str, 0, JSPROP_PERMANENT, NULL, NULL},
1139 : {js_ignoreProcessingInstructions_str, 0, JSPROP_PERMANENT, NULL, NULL},
1140 : {js_ignoreWhitespace_str, 0, JSPROP_PERMANENT, NULL, NULL},
1141 : {js_prettyPrinting_str, 0, JSPROP_PERMANENT, NULL, NULL},
1142 : {js_prettyIndent_str, 0, JSPROP_PERMANENT, NULL, NULL},
1143 : {0,0,0,0,0}
1144 : };
1145 :
1146 : /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1147 : #define IS_XML(str) \
1148 : (str->length() == 3 && IS_XML_CHARS(str->chars()))
1149 :
1150 : #define IS_XMLNS(str) \
1151 : (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1152 :
1153 : static inline bool
1154 1378 : IS_XML_CHARS(const jschar *chars)
1155 : {
1156 1378 : return (chars[0] == 'x' || chars[0] == 'X') &&
1157 1378 : (chars[1] == 'm' || chars[1] == 'M') &&
1158 2756 : (chars[2] == 'l' || chars[2] == 'L');
1159 : }
1160 :
1161 : static inline bool
1162 1378 : HAS_NS_AFTER_XML(const jschar *chars)
1163 : {
1164 1378 : return (chars[3] == 'n' || chars[3] == 'N') &&
1165 1378 : (chars[4] == 's' || chars[4] == 'S');
1166 : }
1167 :
1168 : #define IS_XMLNS_CHARS(chars) \
1169 : (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1170 :
1171 : #define STARTS_WITH_XML(chars,length) \
1172 : (length >= 3 && IS_XML_CHARS(chars))
1173 :
1174 : static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1175 : static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1176 :
1177 : void
1178 4729195 : JSXML::finalize(FreeOp *fop)
1179 : {
1180 4729195 : if (JSXML_HAS_KIDS(this)) {
1181 2368231 : xml_kids.finish(fop);
1182 2368231 : if (xml_class == JSXML_CLASS_ELEMENT) {
1183 2361386 : xml_namespaces.finish(fop);
1184 2361386 : xml_attrs.finish(fop);
1185 : }
1186 : }
1187 : #ifdef DEBUG_notme
1188 : JS_REMOVE_LINK(&links);
1189 : #endif
1190 4729195 : }
1191 :
1192 : static JSObject *
1193 2036 : ParseNodeToQName(Parser *parser, ParseNode *pn,
1194 : JSXMLArray<JSObject> *inScopeNSes, JSBool isAttributeName)
1195 : {
1196 2036 : JSContext *cx = parser->context;
1197 : JSLinearString *uri, *prefix;
1198 : size_t length, offset;
1199 : const jschar *start, *limit, *colon;
1200 : uint32_t n;
1201 : JSObject *ns;
1202 : JSLinearString *nsprefix;
1203 :
1204 2036 : JS_ASSERT(pn->isArity(PN_NULLARY));
1205 2036 : JSAtom *str = pn->pn_atom;
1206 2036 : start = str->chars();
1207 2036 : length = str->length();
1208 2036 : JS_ASSERT(length != 0 && *start != '@');
1209 2036 : JS_ASSERT(length != 1 || *start != '*');
1210 :
1211 : JSAtom *localName;
1212 :
1213 2036 : uri = cx->runtime->emptyString;
1214 2036 : limit = start + length;
1215 2036 : colon = js_strchr_limit(start, ':', limit);
1216 2036 : if (colon) {
1217 0 : offset = colon - start;
1218 0 : prefix = js_NewDependentString(cx, str, 0, offset);
1219 0 : if (!prefix)
1220 0 : return NULL;
1221 :
1222 0 : if (STARTS_WITH_XML(start, offset)) {
1223 0 : if (offset == 3) {
1224 0 : uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str));
1225 0 : if (!uri)
1226 0 : return NULL;
1227 0 : } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1228 0 : uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str));
1229 0 : if (!uri)
1230 0 : return NULL;
1231 : } else {
1232 0 : uri = NULL;
1233 : }
1234 : } else {
1235 0 : uri = NULL;
1236 0 : n = inScopeNSes->length;
1237 0 : while (n != 0) {
1238 0 : --n;
1239 0 : ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1240 0 : nsprefix = ns->getNamePrefix();
1241 0 : if (nsprefix && EqualStrings(nsprefix, prefix)) {
1242 0 : uri = ns->getNameURI();
1243 0 : break;
1244 : }
1245 : }
1246 : }
1247 :
1248 0 : if (!uri) {
1249 0 : Value v = StringValue(prefix);
1250 0 : JSAutoByteString bytes;
1251 0 : if (js_ValueToPrintable(parser->context, v, &bytes)) {
1252 : ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
1253 0 : JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
1254 : }
1255 0 : return NULL;
1256 : }
1257 :
1258 0 : localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1));
1259 0 : if (!localName)
1260 0 : return NULL;
1261 : } else {
1262 2036 : if (isAttributeName) {
1263 : /*
1264 : * An unprefixed attribute is not in any namespace, so set prefix
1265 : * as well as uri to the empty string.
1266 : */
1267 9 : prefix = uri;
1268 : } else {
1269 : /*
1270 : * Loop from back to front looking for the closest declared default
1271 : * namespace.
1272 : */
1273 2027 : n = inScopeNSes->length;
1274 4054 : while (n != 0) {
1275 2027 : --n;
1276 2027 : ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1277 2027 : nsprefix = ns->getNamePrefix();
1278 2027 : if (!nsprefix || nsprefix->empty()) {
1279 2027 : uri = ns->getNameURI();
1280 2027 : break;
1281 : }
1282 : }
1283 2027 : prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
1284 : }
1285 2036 : localName = str;
1286 : }
1287 :
1288 2036 : return NewXMLQName(parser->context, uri, prefix, localName);
1289 : }
1290 :
1291 : static JSString *
1292 18 : ChompXMLWhitespace(JSContext *cx, JSString *str)
1293 : {
1294 : size_t length, newlength, offset;
1295 : const jschar *cp, *start, *end;
1296 : jschar c;
1297 :
1298 18 : length = str->length();
1299 18 : start = str->getChars(cx);
1300 18 : if (!start)
1301 0 : return NULL;
1302 :
1303 18 : for (cp = start, end = cp + length; cp < end; cp++) {
1304 18 : c = *cp;
1305 18 : if (!unicode::IsXMLSpace(c))
1306 18 : break;
1307 : }
1308 36 : while (end > cp) {
1309 18 : c = end[-1];
1310 18 : if (!unicode::IsXMLSpace(c))
1311 18 : break;
1312 0 : --end;
1313 : }
1314 18 : newlength = end - cp;
1315 18 : if (newlength == length)
1316 18 : return str;
1317 0 : offset = cp - start;
1318 0 : return js_NewDependentString(cx, str, offset, newlength);
1319 : }
1320 :
1321 : static JSXML *
1322 4693 : ParseNodeToXML(Parser *parser, ParseNode *pn,
1323 : JSXMLArray<JSObject> *inScopeNSes, unsigned flags)
1324 : {
1325 4693 : JSContext *cx = parser->context;
1326 : JSXML *xml, *kid, *attr, *attrj;
1327 : JSLinearString *str;
1328 : uint32_t length, n, i, j;
1329 : ParseNode *pn2, *pn3, *head, **pnp;
1330 : JSObject *ns;
1331 : JSObject *qn, *attrjqn;
1332 : JSXMLClass xml_class;
1333 : int stackDummy;
1334 :
1335 4693 : if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy)) {
1336 : ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR,
1337 0 : JSMSG_OVER_RECURSED);
1338 0 : return NULL;
1339 : }
1340 :
1341 : #define PN2X_SKIP_CHILD ((JSXML *) 1)
1342 :
1343 : /*
1344 : * Cases return early to avoid common code that gets an outermost xml's
1345 : * object, which protects GC-things owned by xml and its descendants from
1346 : * garbage collection.
1347 : */
1348 4693 : xml = NULL;
1349 4693 : if (!js_EnterLocalRootScope(cx))
1350 0 : return NULL;
1351 4693 : switch (pn->getKind()) {
1352 : case PNK_XMLELEM:
1353 1793 : length = inScopeNSes->length;
1354 1793 : pn2 = pn->pn_head;
1355 1793 : xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1356 1793 : if (!xml)
1357 0 : goto fail;
1358 :
1359 1793 : n = pn->pn_count;
1360 1793 : JS_ASSERT(n >= 2);
1361 1793 : n -= 2;
1362 1793 : if (!xml->xml_kids.setCapacity(cx, n))
1363 0 : goto fail;
1364 :
1365 1793 : i = 0;
1366 5108 : while ((pn2 = pn2->pn_next) != NULL) {
1367 3315 : if (!pn2->pn_next) {
1368 : /* Don't append the end tag! */
1369 1793 : JS_ASSERT(pn2->isKind(PNK_XMLETAGO));
1370 1793 : break;
1371 : }
1372 :
1373 1558 : if ((flags & XSF_IGNORE_WHITESPACE) &&
1374 36 : n > 1 && pn2->isKind(PNK_XMLSPACE)) {
1375 0 : --n;
1376 0 : continue;
1377 : }
1378 :
1379 1522 : kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1380 1522 : if (kid == PN2X_SKIP_CHILD) {
1381 0 : --n;
1382 0 : continue;
1383 : }
1384 :
1385 1522 : if (!kid)
1386 0 : goto fail;
1387 :
1388 : /* Store kid in xml right away, to protect it from GC. */
1389 1522 : XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1390 1522 : kid->parent = xml;
1391 1522 : ++i;
1392 :
1393 : /* XXX where is this documented in an XML spec, or in E4X? */
1394 1522 : if ((flags & XSF_IGNORE_WHITESPACE) &&
1395 : n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1396 0 : JSString *str = ChompXMLWhitespace(cx, kid->xml_value);
1397 0 : if (!str)
1398 0 : goto fail;
1399 0 : kid->xml_value = str;
1400 : }
1401 : }
1402 :
1403 1793 : JS_ASSERT(i == n);
1404 1793 : if (n < pn->pn_count - 2)
1405 0 : xml->xml_kids.trim();
1406 1793 : XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1407 1793 : break;
1408 :
1409 : case PNK_XMLLIST:
1410 0 : xml = js_NewXML(cx, JSXML_CLASS_LIST);
1411 0 : if (!xml)
1412 0 : goto fail;
1413 :
1414 0 : n = pn->pn_count;
1415 0 : if (!xml->xml_kids.setCapacity(cx, n))
1416 0 : goto fail;
1417 :
1418 0 : i = 0;
1419 0 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1420 : /*
1421 : * Always ignore insignificant whitespace in lists -- we shouldn't
1422 : * condition this on an XML.ignoreWhitespace setting when the list
1423 : * constructor is XMLList (note XML/XMLList unification hazard).
1424 : */
1425 0 : if (pn2->isKind(PNK_XMLSPACE)) {
1426 0 : --n;
1427 0 : continue;
1428 : }
1429 :
1430 0 : kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1431 0 : if (kid == PN2X_SKIP_CHILD) {
1432 0 : --n;
1433 0 : continue;
1434 : }
1435 :
1436 0 : if (!kid)
1437 0 : goto fail;
1438 :
1439 0 : XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1440 0 : ++i;
1441 : }
1442 :
1443 0 : if (n < pn->pn_count)
1444 0 : xml->xml_kids.trim();
1445 0 : break;
1446 :
1447 : case PNK_XMLSTAGO:
1448 : case PNK_XMLPTAGC:
1449 2027 : length = inScopeNSes->length;
1450 2027 : pn2 = pn->pn_head;
1451 2027 : JS_ASSERT(pn2->isKind(PNK_XMLNAME));
1452 2027 : if (pn2->isArity(PN_LIST))
1453 0 : goto syntax;
1454 :
1455 2027 : xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1456 2027 : if (!xml)
1457 0 : goto fail;
1458 :
1459 : /* First pass: check syntax and process namespace declarations. */
1460 2027 : JS_ASSERT(pn->pn_count >= 1);
1461 2027 : n = pn->pn_count - 1;
1462 2027 : pnp = &pn2->pn_next;
1463 2027 : head = *pnp;
1464 5441 : while ((pn2 = *pnp) != NULL) {
1465 : size_t length;
1466 : const jschar *chars;
1467 :
1468 1387 : if (!pn2->isKind(PNK_XMLNAME) || !pn2->isArity(PN_NULLARY))
1469 0 : goto syntax;
1470 :
1471 : /* Enforce "Well-formedness constraint: Unique Att Spec". */
1472 1387 : for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1473 0 : if (pn3->pn_atom == pn2->pn_atom) {
1474 0 : Value v = StringValue(pn2->pn_atom);
1475 0 : JSAutoByteString bytes;
1476 0 : if (js_ValueToPrintable(cx, v, &bytes)) {
1477 : ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1478 : JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1479 0 : bytes.ptr());
1480 : }
1481 : goto fail;
1482 : }
1483 : }
1484 :
1485 1387 : JSAtom *atom = pn2->pn_atom;
1486 1387 : pn2 = pn2->pn_next;
1487 1387 : JS_ASSERT(pn2);
1488 1387 : if (!pn2->isKind(PNK_XMLATTR))
1489 0 : goto syntax;
1490 :
1491 1387 : chars = atom->chars();
1492 1387 : length = atom->length();
1493 4143 : if (length >= 5 &&
1494 2756 : IS_XMLNS_CHARS(chars) &&
1495 0 : (length == 5 || chars[5] == ':')) {
1496 : JSLinearString *uri, *prefix;
1497 :
1498 1378 : uri = pn2->pn_atom;
1499 1378 : if (length == 5) {
1500 : /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1501 1378 : prefix = cx->runtime->emptyString;
1502 : } else {
1503 0 : prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1504 0 : if (!prefix)
1505 0 : goto fail;
1506 : }
1507 :
1508 : /*
1509 : * Once the new ns is appended to xml->xml_namespaces, it is
1510 : * protected from GC by the object that owns xml -- which is
1511 : * either xml->object if outermost, or the object owning xml's
1512 : * oldest ancestor if !outermost.
1513 : */
1514 1378 : ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1515 1378 : if (!ns)
1516 0 : goto fail;
1517 :
1518 : /*
1519 : * Don't add a namespace that's already in scope. If someone
1520 : * extracts a child property from its parent via [[Get]], then
1521 : * we enforce the invariant, noted many times in ECMA-357, that
1522 : * the child's namespaces form a possibly-improper superset of
1523 : * its ancestors' namespaces.
1524 : */
1525 1378 : if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1526 2756 : if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1527 1378 : !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1528 0 : goto fail;
1529 : }
1530 : }
1531 :
1532 1378 : JS_ASSERT(n >= 2);
1533 1378 : n -= 2;
1534 1378 : *pnp = pn2->pn_next;
1535 : /* XXXbe recycle pn2 */
1536 1378 : continue;
1537 : }
1538 :
1539 9 : pnp = &pn2->pn_next;
1540 : }
1541 :
1542 2027 : xml->xml_namespaces.trim();
1543 :
1544 : /* Second pass: process tag name and attributes, using namespaces. */
1545 2027 : pn2 = pn->pn_head;
1546 2027 : qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_FALSE);
1547 2027 : if (!qn)
1548 0 : goto fail;
1549 2027 : xml->name = qn;
1550 :
1551 2027 : JS_ASSERT((n & 1) == 0);
1552 2027 : n >>= 1;
1553 2027 : if (!xml->xml_attrs.setCapacity(cx, n))
1554 0 : goto fail;
1555 :
1556 2036 : for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1557 9 : qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_TRUE);
1558 9 : if (!qn) {
1559 0 : xml->xml_attrs.length = i;
1560 0 : goto fail;
1561 : }
1562 :
1563 : /*
1564 : * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1565 : * this time checking local name and namespace URI.
1566 : */
1567 9 : for (j = 0; j < i; j++) {
1568 0 : attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1569 0 : attrjqn = attrj->name;
1570 0 : if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) &&
1571 0 : EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) {
1572 0 : Value v = StringValue(pn2->pn_atom);
1573 0 : JSAutoByteString bytes;
1574 0 : if (js_ValueToPrintable(cx, v, &bytes)) {
1575 : ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1576 : JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1577 0 : bytes.ptr());
1578 : }
1579 : goto fail;
1580 : }
1581 : }
1582 :
1583 9 : pn2 = pn2->pn_next;
1584 9 : JS_ASSERT(pn2);
1585 9 : JS_ASSERT(pn2->isKind(PNK_XMLATTR));
1586 :
1587 9 : attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1588 9 : if (!attr)
1589 0 : goto fail;
1590 :
1591 9 : XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1592 9 : attr->parent = xml;
1593 9 : attr->name = qn;
1594 9 : attr->xml_value = pn2->pn_atom;
1595 : }
1596 :
1597 : /* Point tag closes its own namespace scope. */
1598 2027 : if (pn->isKind(PNK_XMLPTAGC))
1599 234 : XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1600 2027 : break;
1601 :
1602 : case PNK_XMLSPACE:
1603 : case PNK_XMLTEXT:
1604 : case PNK_XMLCDATA:
1605 : case PNK_XMLCOMMENT:
1606 : case PNK_XMLPI:
1607 873 : str = pn->pn_atom;
1608 873 : qn = NULL;
1609 873 : if (pn->isKind(PNK_XMLCOMMENT)) {
1610 0 : if (flags & XSF_IGNORE_COMMENTS)
1611 0 : goto skip_child;
1612 0 : xml_class = JSXML_CLASS_COMMENT;
1613 873 : } else if (pn->isKind(PNK_XMLPI)) {
1614 0 : XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
1615 0 : if (IS_XML(str)) {
1616 0 : Value v = StringValue(str);
1617 0 : JSAutoByteString bytes;
1618 0 : if (js_ValueToPrintable(cx, v, &bytes)) {
1619 : ReportCompileErrorNumber(cx, &parser->tokenStream, &pi,
1620 0 : JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
1621 : }
1622 : goto fail;
1623 : }
1624 :
1625 0 : if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1626 0 : goto skip_child;
1627 :
1628 0 : qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE);
1629 0 : if (!qn)
1630 0 : goto fail;
1631 :
1632 0 : str = pi.data();
1633 0 : xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1634 : } else {
1635 : /* CDATA section content, or element text. */
1636 873 : xml_class = JSXML_CLASS_TEXT;
1637 : }
1638 :
1639 873 : xml = js_NewXML(cx, xml_class);
1640 873 : if (!xml)
1641 0 : goto fail;
1642 873 : xml->name = qn;
1643 873 : if (pn->isKind(PNK_XMLSPACE))
1644 0 : xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1645 873 : xml->xml_value = str;
1646 873 : break;
1647 :
1648 : default:
1649 0 : goto syntax;
1650 : }
1651 :
1652 4693 : js_LeaveLocalRootScopeWithResult(cx, xml);
1653 4693 : return xml;
1654 :
1655 : skip_child:
1656 0 : js_LeaveLocalRootScope(cx);
1657 0 : return PN2X_SKIP_CHILD;
1658 :
1659 : #undef PN2X_SKIP_CHILD
1660 :
1661 : syntax:
1662 0 : ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
1663 : fail:
1664 0 : js_LeaveLocalRootScope(cx);
1665 0 : return NULL;
1666 : }
1667 :
1668 : /*
1669 : * XML helper, object-ops, and library functions. We start with the helpers,
1670 : * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1671 : */
1672 : static JSBool
1673 5629 : GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1674 : {
1675 : jsval v;
1676 :
1677 5629 : if (!js_FindClassObject(cx, NULL, JSProto_XML, &v))
1678 0 : return JS_FALSE;
1679 5629 : if (JSVAL_IS_PRIMITIVE(v) || !JSVAL_TO_OBJECT(v)->isFunction()) {
1680 0 : *vp = JSVAL_VOID;
1681 0 : return JS_TRUE;
1682 : }
1683 5629 : return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1684 : }
1685 :
1686 : static JSBool
1687 5575 : GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1688 : {
1689 : jsval v;
1690 :
1691 5575 : return GetXMLSetting(cx, name, &v) && JS_ValueToBoolean(cx, v, bp);
1692 : }
1693 :
1694 : static JSBool
1695 54 : GetUint32XMLSetting(JSContext *cx, const char *name, uint32_t *uip)
1696 : {
1697 : jsval v;
1698 :
1699 54 : return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1700 : }
1701 :
1702 : static JSBool
1703 1378 : GetXMLSettingFlags(JSContext *cx, unsigned *flagsp)
1704 : {
1705 : JSBool flag[4];
1706 :
1707 5512 : if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag[0]) ||
1708 1378 : !GetBooleanXMLSetting(cx, js_ignoreProcessingInstructions_str, &flag[1]) ||
1709 1378 : !GetBooleanXMLSetting(cx, js_ignoreWhitespace_str, &flag[2]) ||
1710 1378 : !GetBooleanXMLSetting(cx, js_prettyPrinting_str, &flag[3])) {
1711 0 : return false;
1712 : }
1713 :
1714 1378 : *flagsp = 0;
1715 6890 : for (size_t n = 0; n < 4; ++n)
1716 5512 : if (flag[n])
1717 5512 : *flagsp |= JS_BIT(n);
1718 1378 : return true;
1719 : }
1720 :
1721 : static JSObject *
1722 14501 : GetCurrentScopeChain(JSContext *cx)
1723 : {
1724 14501 : if (cx->hasfp())
1725 14501 : return &cx->fp()->scopeChain();
1726 0 : return JS_ObjectToInnerObject(cx, cx->globalObject);
1727 : }
1728 :
1729 : static JSXML *
1730 1378 : ParseXMLSource(JSContext *cx, JSString *src)
1731 : {
1732 : jsval nsval;
1733 : JSLinearString *uri;
1734 : size_t urilen, srclen, length, offset, dstlen;
1735 : jschar *chars;
1736 : const jschar *srcp, *endp;
1737 : JSXML *xml;
1738 : const char *filename;
1739 : unsigned lineno;
1740 : JSOp op;
1741 :
1742 : static const char prefix[] = "<parent xmlns=\"";
1743 : static const char middle[] = "\">";
1744 : static const char suffix[] = "</parent>";
1745 :
1746 : #define constrlen(constr) (sizeof(constr) - 1)
1747 :
1748 1378 : if (!js_GetDefaultXMLNamespace(cx, &nsval))
1749 0 : return NULL;
1750 1378 : uri = JSVAL_TO_OBJECT(nsval)->getNameURI();
1751 1378 : uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1752 1378 : if (!uri)
1753 0 : return NULL;
1754 :
1755 1378 : urilen = uri->length();
1756 1378 : srclen = src->length();
1757 : length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1758 1378 : constrlen(suffix);
1759 :
1760 1378 : chars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar));
1761 1378 : if (!chars)
1762 0 : return NULL;
1763 :
1764 1378 : dstlen = length;
1765 1378 : InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1766 1378 : offset = dstlen;
1767 1378 : js_strncpy(chars + offset, uri->chars(), urilen);
1768 1378 : offset += urilen;
1769 1378 : dstlen = length - offset + 1;
1770 1378 : InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen);
1771 1378 : offset += dstlen;
1772 1378 : srcp = src->getChars(cx);
1773 1378 : if (!srcp) {
1774 0 : cx->free_(chars);
1775 0 : return NULL;
1776 : }
1777 1378 : js_strncpy(chars + offset, srcp, srclen);
1778 1378 : offset += srclen;
1779 1378 : dstlen = length - offset + 1;
1780 1378 : InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen);
1781 1378 : chars [offset + dstlen] = 0;
1782 :
1783 1378 : xml = NULL;
1784 1378 : filename = NULL;
1785 1378 : lineno = 1;
1786 1378 : FrameRegsIter i(cx);
1787 1378 : if (!i.done()) {
1788 1378 : op = (JSOp) *i.pc();
1789 1378 : if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1790 613 : filename = i.fp()->script()->filename;
1791 613 : lineno = PCToLineNumber(i.fp()->script(), i.pc());
1792 5781 : for (endp = srcp + srclen; srcp < endp; srcp++) {
1793 5168 : if (*srcp == '\n')
1794 0 : --lineno;
1795 : }
1796 : }
1797 : }
1798 :
1799 : {
1800 2756 : Parser parser(cx);
1801 1378 : if (parser.init(chars, length, filename, lineno, cx->findVersion())) {
1802 1378 : JSObject *scopeChain = GetCurrentScopeChain(cx);
1803 1378 : if (!scopeChain) {
1804 0 : cx->free_(chars);
1805 0 : return NULL;
1806 : }
1807 :
1808 1378 : ParseNode *pn = parser.parseXMLText(scopeChain, false);
1809 : unsigned flags;
1810 1378 : if (pn && GetXMLSettingFlags(cx, &flags)) {
1811 2756 : AutoNamespaceArray namespaces(cx);
1812 1378 : if (namespaces.array.setCapacity(cx, 1))
1813 1378 : xml = ParseNodeToXML(&parser, pn, &namespaces.array, flags);
1814 : }
1815 : }
1816 : }
1817 :
1818 1378 : cx->free_(chars);
1819 1378 : return xml;
1820 :
1821 : #undef constrlen
1822 : }
1823 :
1824 : /*
1825 : * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1826 : *
1827 : * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1828 : * the constraint:
1829 : *
1830 : * for all x belonging to XML:
1831 : * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1832 : *
1833 : * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1834 : * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1835 : *
1836 : * Same goes for 10.4.1 Step 7(a).
1837 : *
1838 : * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1839 : * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1840 : * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1841 : * undeclared namespaces associated with x not belonging to ancestorNS.
1842 : */
1843 : static JSXML *
1844 1396 : OrphanXMLChild(JSContext *cx, JSXML *xml, uint32_t i)
1845 : {
1846 : JSObject *ns;
1847 :
1848 1396 : ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1849 1396 : xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1850 1396 : if (!ns || !xml)
1851 0 : return xml;
1852 1396 : if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1853 631 : if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1854 0 : return NULL;
1855 631 : ns->setNamespaceDeclared(JSVAL_VOID);
1856 : }
1857 1396 : xml->parent = NULL;
1858 1396 : return xml;
1859 : }
1860 :
1861 : static JSObject *
1862 1351 : ToXML(JSContext *cx, jsval v)
1863 : {
1864 : JSObject *obj;
1865 : JSXML *xml;
1866 : Class *clasp;
1867 : JSString *str;
1868 : uint32_t length;
1869 :
1870 1351 : if (JSVAL_IS_PRIMITIVE(v)) {
1871 1351 : if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1872 0 : goto bad;
1873 : } else {
1874 0 : obj = JSVAL_TO_OBJECT(v);
1875 0 : if (obj->isXML()) {
1876 0 : xml = (JSXML *) obj->getPrivate();
1877 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
1878 0 : if (xml->xml_kids.length != 1)
1879 0 : goto bad;
1880 0 : xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1881 0 : if (xml) {
1882 0 : JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1883 0 : return js_GetXMLObject(cx, xml);
1884 : }
1885 : }
1886 0 : return obj;
1887 : }
1888 :
1889 0 : clasp = obj->getClass();
1890 0 : if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1891 0 : JS_ASSERT(0);
1892 : }
1893 :
1894 0 : if (clasp != &StringClass &&
1895 : clasp != &NumberClass &&
1896 : clasp != &BooleanClass) {
1897 0 : goto bad;
1898 : }
1899 : }
1900 :
1901 1351 : str = ToString(cx, v);
1902 1351 : if (!str)
1903 0 : return NULL;
1904 1351 : if (str->empty()) {
1905 0 : length = 0;
1906 : #ifdef __GNUC__ /* suppress bogus gcc warnings */
1907 0 : xml = NULL;
1908 : #endif
1909 : } else {
1910 1351 : xml = ParseXMLSource(cx, str);
1911 1351 : if (!xml)
1912 0 : return NULL;
1913 1351 : length = JSXML_LENGTH(xml);
1914 : }
1915 :
1916 1351 : if (length == 0) {
1917 0 : obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
1918 0 : if (!obj)
1919 0 : return NULL;
1920 1351 : } else if (length == 1) {
1921 1351 : xml = OrphanXMLChild(cx, xml, 0);
1922 1351 : if (!xml)
1923 0 : return NULL;
1924 1351 : obj = js_GetXMLObject(cx, xml);
1925 1351 : if (!obj)
1926 0 : return NULL;
1927 : } else {
1928 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
1929 0 : return NULL;
1930 : }
1931 1351 : return obj;
1932 :
1933 : bad:
1934 : js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
1935 0 : JSDVG_IGNORE_STACK, v, NULL);
1936 0 : return NULL;
1937 : }
1938 :
1939 : static JSBool
1940 : Append(JSContext *cx, JSXML *list, JSXML *kid);
1941 :
1942 : static JSObject *
1943 63 : ToXMLList(JSContext *cx, jsval v)
1944 : {
1945 : JSObject *obj, *listobj;
1946 : JSXML *xml, *list, *kid;
1947 : Class *clasp;
1948 : JSString *str;
1949 : uint32_t i, length;
1950 :
1951 63 : if (JSVAL_IS_PRIMITIVE(v)) {
1952 63 : if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1953 0 : goto bad;
1954 : } else {
1955 0 : obj = JSVAL_TO_OBJECT(v);
1956 0 : if (obj->isXML()) {
1957 0 : xml = (JSXML *) obj->getPrivate();
1958 0 : if (xml->xml_class != JSXML_CLASS_LIST) {
1959 0 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
1960 0 : if (!listobj)
1961 0 : return NULL;
1962 0 : list = (JSXML *) listobj->getPrivate();
1963 0 : if (!Append(cx, list, xml))
1964 0 : return NULL;
1965 0 : return listobj;
1966 : }
1967 0 : return obj;
1968 : }
1969 :
1970 0 : clasp = obj->getClass();
1971 0 : if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1972 0 : JS_ASSERT(0);
1973 : }
1974 :
1975 0 : if (clasp != &StringClass &&
1976 : clasp != &NumberClass &&
1977 : clasp != &BooleanClass) {
1978 0 : goto bad;
1979 : }
1980 : }
1981 :
1982 63 : str = ToString(cx, v);
1983 63 : if (!str)
1984 0 : return NULL;
1985 63 : if (str->empty()) {
1986 36 : xml = NULL;
1987 36 : length = 0;
1988 : } else {
1989 27 : if (!js_EnterLocalRootScope(cx))
1990 0 : return NULL;
1991 27 : xml = ParseXMLSource(cx, str);
1992 27 : if (!xml) {
1993 0 : js_LeaveLocalRootScope(cx);
1994 0 : return NULL;
1995 : }
1996 27 : length = JSXML_LENGTH(xml);
1997 : }
1998 :
1999 63 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2000 63 : if (listobj) {
2001 63 : list = (JSXML *) listobj->getPrivate();
2002 108 : for (i = 0; i < length; i++) {
2003 45 : kid = OrphanXMLChild(cx, xml, i);
2004 45 : if (!kid || !Append(cx, list, kid)) {
2005 0 : listobj = NULL;
2006 0 : break;
2007 : }
2008 : }
2009 : }
2010 :
2011 63 : if (xml)
2012 27 : js_LeaveLocalRootScopeWithResult(cx, listobj);
2013 63 : return listobj;
2014 :
2015 : bad:
2016 : js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2017 0 : JSDVG_IGNORE_STACK, v, NULL);
2018 0 : return NULL;
2019 : }
2020 :
2021 : /*
2022 : * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2023 : * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2024 : * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2025 : * MakeXMLSpecialString subroutine.
2026 : *
2027 : * These functions mutate sb, leaving it empty.
2028 : */
2029 : static JSFlatString *
2030 0 : MakeXMLSpecialString(JSContext *cx, StringBuffer &sb,
2031 : JSString *str, JSString *str2,
2032 : const jschar *prefix, size_t prefixlength,
2033 : const jschar *suffix, size_t suffixlength)
2034 : {
2035 0 : if (!sb.append(prefix, prefixlength) || !sb.append(str))
2036 0 : return NULL;
2037 0 : if (str2 && !str2->empty()) {
2038 0 : if (!sb.append(' ') || !sb.append(str2))
2039 0 : return NULL;
2040 : }
2041 0 : if (!sb.append(suffix, suffixlength))
2042 0 : return NULL;
2043 :
2044 0 : return sb.finishString();
2045 : }
2046 :
2047 : static JSFlatString *
2048 0 : MakeXMLCDATAString(JSContext *cx, StringBuffer &sb, JSString *str)
2049 : {
2050 : static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2051 : 'C', 'D', 'A', 'T', 'A',
2052 : '['};
2053 : static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2054 :
2055 : return MakeXMLSpecialString(cx, sb, str, NULL,
2056 : cdata_prefix_ucNstr, 9,
2057 0 : cdata_suffix_ucNstr, 3);
2058 : }
2059 :
2060 : static JSFlatString *
2061 0 : MakeXMLCommentString(JSContext *cx, StringBuffer &sb, JSString *str)
2062 : {
2063 : static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2064 : static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2065 :
2066 : return MakeXMLSpecialString(cx, sb, str, NULL,
2067 : comment_prefix_ucNstr, 4,
2068 0 : comment_suffix_ucNstr, 3);
2069 : }
2070 :
2071 : static JSFlatString *
2072 0 : MakeXMLPIString(JSContext *cx, StringBuffer &sb, JSString *name,
2073 : JSString *value)
2074 : {
2075 : static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2076 : static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2077 :
2078 : return MakeXMLSpecialString(cx, sb, name, value,
2079 : pi_prefix_ucNstr, 2,
2080 0 : pi_suffix_ucNstr, 2);
2081 : }
2082 :
2083 : /*
2084 : * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2085 : *
2086 : * This function appends the output into the supplied string buffer.
2087 : */
2088 : static bool
2089 1378 : EscapeAttributeValueBuffer(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote)
2090 : {
2091 1378 : size_t length = str->length();
2092 1378 : const jschar *start = str->getChars(cx);
2093 1378 : if (!start)
2094 0 : return false;
2095 :
2096 1378 : if (quote && !sb.append('"'))
2097 0 : return false;
2098 :
2099 1378 : for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2100 0 : jschar c = *cp;
2101 0 : switch (c) {
2102 : case '"':
2103 0 : if (!sb.append(js_quot_entity_str))
2104 0 : return false;
2105 0 : break;
2106 : case '<':
2107 0 : if (!sb.append(js_lt_entity_str))
2108 0 : return false;
2109 0 : break;
2110 : case '&':
2111 0 : if (!sb.append(js_amp_entity_str))
2112 0 : return false;
2113 0 : break;
2114 : case '\n':
2115 0 : if (!sb.append("
"))
2116 0 : return false;
2117 0 : break;
2118 : case '\r':
2119 0 : if (!sb.append("
"))
2120 0 : return false;
2121 0 : break;
2122 : case '\t':
2123 0 : if (!sb.append("	"))
2124 0 : return false;
2125 0 : break;
2126 : default:
2127 0 : if (!sb.append(c))
2128 0 : return false;
2129 : }
2130 : }
2131 :
2132 1378 : if (quote && !sb.append('"'))
2133 0 : return false;
2134 :
2135 1378 : return true;
2136 : }
2137 :
2138 : /*
2139 : * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2140 : *
2141 : * This function mutates sb, leaving it empty.
2142 : */
2143 : static JSFlatString *
2144 1378 : EscapeAttributeValue(JSContext *cx, StringBuffer &sb, JSString *str, JSBool quote)
2145 : {
2146 1378 : if (!EscapeAttributeValueBuffer(cx, sb, str, quote))
2147 0 : return NULL;
2148 1378 : return sb.finishString();
2149 : }
2150 :
2151 : /*
2152 : * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2153 : * equals, a double quote, an attribute value, and a closing double quote.
2154 : */
2155 : static bool
2156 0 : AppendAttributeValue(JSContext *cx, StringBuffer &sb, JSString *valstr)
2157 : {
2158 0 : if (!sb.append('='))
2159 0 : return false;
2160 0 : return EscapeAttributeValueBuffer(cx, sb, valstr, JS_TRUE);
2161 : }
2162 :
2163 : /*
2164 : * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2165 :
2166 : * These functions mutate sb, leaving it empty.
2167 : */
2168 : static JSFlatString *
2169 18 : EscapeElementValue(JSContext *cx, StringBuffer &sb, JSString *str, uint32_t toSourceFlag)
2170 : {
2171 18 : size_t length = str->length();
2172 18 : const jschar *start = str->getChars(cx);
2173 18 : if (!start)
2174 0 : return NULL;
2175 :
2176 72 : for (const jschar *cp = start, *end = start + length; cp != end; ++cp) {
2177 54 : jschar c = *cp;
2178 54 : switch (*cp) {
2179 : case '<':
2180 0 : if (!sb.append(js_lt_entity_str))
2181 0 : return NULL;
2182 0 : break;
2183 : case '>':
2184 0 : if (!sb.append(js_gt_entity_str))
2185 0 : return NULL;
2186 0 : break;
2187 : case '&':
2188 0 : if (!sb.append(js_amp_entity_str))
2189 0 : return NULL;
2190 0 : break;
2191 : case '{':
2192 : /*
2193 : * If EscapeElementValue is called by toSource/uneval, we also need
2194 : * to escape '{'. See bug 463360.
2195 : */
2196 0 : if (toSourceFlag) {
2197 0 : if (!sb.append(js_leftcurly_entity_str))
2198 0 : return NULL;
2199 0 : break;
2200 : }
2201 : /* FALL THROUGH */
2202 : default:
2203 54 : if (!sb.append(c))
2204 0 : return NULL;
2205 : }
2206 : }
2207 18 : return sb.finishString();
2208 : }
2209 :
2210 : /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2211 : static JSObject *
2212 126 : GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray<JSObject> *inScopeNSes)
2213 : {
2214 : JSLinearString *uri, *prefix, *nsprefix;
2215 : JSObject *match, *ns;
2216 : uint32_t i, n;
2217 : jsval argv[2];
2218 :
2219 126 : uri = qn->getNameURI();
2220 126 : prefix = qn->getNamePrefix();
2221 126 : JS_ASSERT(uri);
2222 126 : if (!uri) {
2223 0 : JSAutoByteString bytes;
2224 : const char *s = !prefix ?
2225 : js_undefined_str
2226 0 : : js_ValueToPrintable(cx, StringValue(prefix), &bytes);
2227 0 : if (s)
2228 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, s);
2229 0 : return NULL;
2230 : }
2231 :
2232 : /* Look for a matching namespace in inScopeNSes, if provided. */
2233 126 : match = NULL;
2234 126 : if (inScopeNSes) {
2235 117 : for (i = 0, n = inScopeNSes->length; i < n; i++) {
2236 117 : ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2237 117 : if (!ns)
2238 0 : continue;
2239 :
2240 : /*
2241 : * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2242 : * If we preserve prefixes, we must match null prefix against
2243 : * an empty prefix of ns, in order to avoid generating redundant
2244 : * prefixed and default namespaces for cases such as:
2245 : *
2246 : * x = <t xmlns="http://foo.com"/>
2247 : * print(x.toXMLString());
2248 : *
2249 : * Per 10.3.2.1, the namespace attribute in t has an empty string
2250 : * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2251 : *
2252 : * 1. If the [local name] property of a is "xmlns"
2253 : * a. Map ns.prefix to the empty string
2254 : *
2255 : * But t's name has a null prefix in this implementation, meaning
2256 : * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2257 : * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2258 : * saying how "no value" maps to an ECMA-357 value -- but it must
2259 : * map to the *undefined* prefix value).
2260 : *
2261 : * Since "" != undefined (or null, in the current implementation)
2262 : * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2263 : * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2264 : * This spec bug leads to ToXMLString results that duplicate the
2265 : * declared namespace.
2266 : */
2267 117 : if (EqualStrings(ns->getNameURI(), uri)) {
2268 117 : nsprefix = ns->getNamePrefix();
2269 117 : if (nsprefix == prefix ||
2270 : ((nsprefix && prefix)
2271 0 : ? EqualStrings(nsprefix, prefix)
2272 0 : : (nsprefix ? nsprefix : prefix)->empty())) {
2273 117 : match = ns;
2274 117 : break;
2275 : }
2276 : }
2277 : }
2278 : }
2279 :
2280 : /* If we didn't match, make a new namespace from qn. */
2281 126 : if (!match) {
2282 9 : argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2283 9 : argv[1] = STRING_TO_JSVAL(uri);
2284 9 : ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, 2, argv);
2285 9 : if (!ns)
2286 0 : return NULL;
2287 9 : match = ns;
2288 : }
2289 126 : return match;
2290 : }
2291 :
2292 : static JSLinearString *
2293 0 : GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray<JSObject> *decls)
2294 : {
2295 : const jschar *cp, *start, *end;
2296 : size_t length, newlength, offset;
2297 : uint32_t i, n, m, serial;
2298 : jschar *bp, *dp;
2299 : JSBool done;
2300 : JSObject *ns;
2301 : JSLinearString *nsprefix, *prefix;
2302 :
2303 0 : JS_ASSERT(!uri->empty());
2304 :
2305 : /*
2306 : * If there are no *declared* namespaces, skip all collision detection and
2307 : * return a short prefix quickly; an example of such a situation:
2308 : *
2309 : * var x = <f/>;
2310 : * var n = new Namespace("http://example.com/");
2311 : * x.@n::att = "val";
2312 : * x.toXMLString();
2313 : *
2314 : * This is necessary for various log10 uses below to be valid.
2315 : */
2316 0 : if (decls->length == 0)
2317 0 : return js_NewStringCopyZ(cx, "a");
2318 :
2319 : /*
2320 : * Try peeling off the last filename suffix or pathname component till
2321 : * we have a valid XML name. This heuristic will prefer "xul" given
2322 : * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2323 : * likely URI of the form ".../xbl2/2005".
2324 : */
2325 0 : start = uri->chars();
2326 0 : end = start + uri->length();
2327 0 : cp = end;
2328 0 : while (--cp > start) {
2329 0 : if (*cp == '.' || *cp == '/' || *cp == ':') {
2330 0 : ++cp;
2331 0 : length = end - cp;
2332 0 : if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2333 0 : break;
2334 0 : end = --cp;
2335 : }
2336 : }
2337 0 : length = end - cp;
2338 :
2339 : /*
2340 : * If the namespace consisted only of non-XML names or names that begin
2341 : * case-insensitively with "xml", arbitrarily create a prefix consisting
2342 : * of 'a's of size length (allowing dp-calculating code to work with or
2343 : * without this branch executing) plus the space for storing a hyphen and
2344 : * the serial number (avoiding reallocation if a collision happens).
2345 : */
2346 0 : bp = (jschar *) cp;
2347 0 : newlength = length;
2348 0 : if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2349 0 : newlength = length + 2 + (size_t) log10((double) decls->length);
2350 : bp = (jschar *)
2351 0 : cx->malloc_((newlength + 1) * sizeof(jschar));
2352 0 : if (!bp)
2353 0 : return NULL;
2354 :
2355 0 : bp[newlength] = 0;
2356 0 : for (i = 0; i < newlength; i++)
2357 0 : bp[i] = 'a';
2358 : }
2359 :
2360 : /*
2361 : * Now search through decls looking for a collision. If we collide with
2362 : * an existing prefix, start tacking on a hyphen and a serial number.
2363 : */
2364 0 : serial = 0;
2365 0 : do {
2366 0 : done = JS_TRUE;
2367 0 : for (i = 0, n = decls->length; i < n; i++) {
2368 0 : ns = XMLARRAY_MEMBER(decls, i, JSObject);
2369 0 : if (ns && (nsprefix = ns->getNamePrefix()) &&
2370 0 : nsprefix->length() == newlength &&
2371 0 : !memcmp(nsprefix->chars(), bp,
2372 0 : newlength * sizeof(jschar))) {
2373 0 : if (bp == cp) {
2374 0 : newlength = length + 2 + (size_t) log10((double) n);
2375 : bp = (jschar *)
2376 0 : cx->malloc_((newlength + 1) * sizeof(jschar));
2377 0 : if (!bp)
2378 0 : return NULL;
2379 0 : js_strncpy(bp, cp, length);
2380 : }
2381 :
2382 0 : ++serial;
2383 0 : JS_ASSERT(serial <= n);
2384 0 : dp = bp + length + 2 + (size_t) log10((double) serial);
2385 0 : *dp = 0;
2386 0 : for (m = serial; m != 0; m /= 10)
2387 0 : *--dp = (jschar)('0' + m % 10);
2388 0 : *--dp = '-';
2389 0 : JS_ASSERT(dp == bp + length);
2390 :
2391 0 : done = JS_FALSE;
2392 0 : break;
2393 : }
2394 : }
2395 0 : } while (!done);
2396 :
2397 0 : if (bp == cp) {
2398 0 : offset = cp - start;
2399 0 : prefix = js_NewDependentString(cx, uri, offset, length);
2400 : } else {
2401 0 : prefix = js_NewString(cx, bp, newlength);
2402 0 : if (!prefix)
2403 0 : cx->free_(bp);
2404 : }
2405 0 : return prefix;
2406 : }
2407 :
2408 : static JSBool
2409 0 : namespace_match(const JSObject *nsa, const JSObject *nsb)
2410 : {
2411 0 : JSLinearString *prefixa, *prefixb = nsb->getNamePrefix();
2412 :
2413 0 : if (prefixb) {
2414 0 : prefixa = nsa->getNamePrefix();
2415 0 : return prefixa && EqualStrings(prefixa, prefixb);
2416 : }
2417 0 : return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
2418 : }
2419 :
2420 : /* ECMA-357 10.2.1 and 10.2.2 */
2421 : #define TO_SOURCE_FLAG 0x80000000
2422 :
2423 : static JSString *
2424 153 : XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray<JSObject> *ancestorNSes,
2425 : uint32_t indentLevel, JSBool pretty)
2426 : {
2427 : JSBool indentKids;
2428 306 : StringBuffer sb(cx);
2429 : JSString *str;
2430 : JSLinearString *prefix, *nsuri;
2431 : uint32_t i, n, nextIndentLevel;
2432 : JSObject *ns, *ns2;
2433 306 : AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx);
2434 :
2435 153 : if (pretty) {
2436 153 : if (!sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2437 0 : return NULL;
2438 : }
2439 :
2440 153 : str = NULL;
2441 :
2442 153 : switch (xml->xml_class) {
2443 : case JSXML_CLASS_TEXT:
2444 : /* Step 4. */
2445 18 : if (pretty) {
2446 18 : str = ChompXMLWhitespace(cx, xml->xml_value);
2447 18 : if (!str)
2448 0 : return NULL;
2449 : } else {
2450 0 : str = xml->xml_value;
2451 : }
2452 18 : return EscapeElementValue(cx, sb, str, indentLevel & TO_SOURCE_FLAG);
2453 :
2454 : case JSXML_CLASS_ATTRIBUTE:
2455 : /* Step 5. */
2456 : return EscapeAttributeValue(cx, sb, xml->xml_value,
2457 0 : (indentLevel & TO_SOURCE_FLAG) != 0);
2458 :
2459 : case JSXML_CLASS_COMMENT:
2460 : /* Step 6. */
2461 0 : return MakeXMLCommentString(cx, sb, xml->xml_value);
2462 :
2463 : case JSXML_CLASS_PROCESSING_INSTRUCTION:
2464 : /* Step 7. */
2465 0 : return MakeXMLPIString(cx, sb, xml->name->getQNameLocalName(),
2466 0 : xml->xml_value);
2467 :
2468 : case JSXML_CLASS_LIST:
2469 : /* ECMA-357 10.2.2. */
2470 : {
2471 36 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
2472 18 : i = 0;
2473 54 : while (JSXML *kid = cursor.getNext()) {
2474 18 : if (pretty && i != 0) {
2475 0 : if (!sb.append('\n'))
2476 0 : return NULL;
2477 : }
2478 :
2479 18 : JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel, pretty);
2480 18 : if (!kidstr || !sb.append(kidstr))
2481 0 : return NULL;
2482 18 : ++i;
2483 : }
2484 : }
2485 :
2486 18 : if (sb.empty())
2487 0 : return cx->runtime->emptyString;
2488 18 : return sb.finishString();
2489 :
2490 : default:;
2491 : }
2492 :
2493 : /* After this point, control must flow through label out: to exit. */
2494 117 : if (!js_EnterLocalRootScope(cx))
2495 0 : return NULL;
2496 :
2497 : /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */
2498 117 : if (!ancestorNSes) {
2499 : // Ensure a namespace with empty strings exists in the initial array,
2500 : // otherwise every call to GetNamespace() when running toString() on
2501 : // an XML object with no namespace defined will create a new Namespace
2502 : // object on every call.
2503 63 : JSObject *emptyns = NewXMLNamespace(cx, cx->runtime->emptyString, cx->runtime->emptyString, JS_FALSE);
2504 63 : if (!emptyns || !XMLARRAY_APPEND(cx, &empty.array, emptyns))
2505 0 : goto out;
2506 63 : ancestorNSes = &empty.array;
2507 : }
2508 :
2509 : /* Clone in-scope namespaces not in ancestorNSes into decls. */
2510 : {
2511 234 : JSXMLArrayCursor<JSObject> cursor(&xml->xml_namespaces);
2512 315 : while ((ns = cursor.getNext()) != NULL) {
2513 81 : if (!IsDeclared(ns))
2514 81 : continue;
2515 0 : if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) {
2516 : /* NOTE: may want to exclude unused namespaces here. */
2517 0 : ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), JS_TRUE);
2518 0 : if (!ns2 || !XMLARRAY_APPEND(cx, &decls.array, ns2))
2519 : goto out;
2520 : }
2521 : }
2522 : }
2523 :
2524 : /*
2525 : * Union ancestorNSes and decls into ancdecls. Note that ancdecls does
2526 : * not own its member references. In the spec, ancdecls has no name, but
2527 : * is always written out as (AncestorNamespaces U namespaceDeclarations).
2528 : */
2529 :
2530 117 : if (!ancdecls.array.setCapacity(cx, ancestorNSes->length + decls.length()))
2531 0 : goto out;
2532 234 : for (i = 0, n = ancestorNSes->length; i < n; i++) {
2533 117 : ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject);
2534 117 : if (!ns2)
2535 0 : continue;
2536 117 : JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls.array, ns2, namespace_identity));
2537 117 : if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2538 0 : goto out;
2539 : }
2540 117 : for (i = 0, n = decls.length(); i < n; i++) {
2541 0 : ns2 = XMLARRAY_MEMBER(&decls.array, i, JSObject);
2542 0 : if (!ns2)
2543 0 : continue;
2544 0 : JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls.array, ns2, namespace_identity));
2545 0 : if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2))
2546 0 : goto out;
2547 : }
2548 :
2549 : /* Step 11, except we don't clone ns unless its prefix is undefined. */
2550 117 : ns = GetNamespace(cx, xml->name, &ancdecls.array);
2551 117 : if (!ns)
2552 0 : goto out;
2553 :
2554 : /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */
2555 117 : prefix = ns->getNamePrefix();
2556 117 : if (!prefix) {
2557 : /*
2558 : * Create a namespace prefix that isn't used by any member of decls.
2559 : * Assign the new prefix to a copy of ns. Flag this namespace as if
2560 : * it were declared, for assertion-testing's sake later below.
2561 : *
2562 : * Erratum: if prefix and xml->name are both null (*undefined* in
2563 : * ECMA-357), we know that xml was named using the default namespace
2564 : * (proof: see GetNamespace and the Namespace constructor called with
2565 : * two arguments). So we ought not generate a new prefix here, when
2566 : * we can declare ns as the default namespace for xml.
2567 : *
2568 : * This helps descendants inherit the namespace instead of redundantly
2569 : * redeclaring it with generated prefixes in each descendant.
2570 : */
2571 0 : nsuri = ns->getNameURI();
2572 0 : if (!xml->name->getNamePrefix()) {
2573 0 : prefix = cx->runtime->emptyString;
2574 : } else {
2575 0 : prefix = GeneratePrefix(cx, nsuri, &ancdecls.array);
2576 0 : if (!prefix)
2577 0 : goto out;
2578 : }
2579 0 : ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE);
2580 0 : if (!ns)
2581 0 : goto out;
2582 :
2583 : /*
2584 : * If the xml->name was unprefixed, we must remove any declared default
2585 : * namespace from decls before appending ns. How can you get a default
2586 : * namespace in decls that doesn't match the one from name? Apparently
2587 : * by calling x.setNamespace(ns) where ns has no prefix. The other way
2588 : * to fix this is to update x's in-scope namespaces when setNamespace
2589 : * is called, but that's not specified by ECMA-357.
2590 : *
2591 : * Likely Erratum here, depending on whether the lack of update to x's
2592 : * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an
2593 : * erratum or not. Note that changing setNamespace to update the list
2594 : * of in-scope namespaces will change x.namespaceDeclarations().
2595 : */
2596 0 : if (prefix->empty()) {
2597 0 : i = XMLArrayFindMember(&decls.array, ns, namespace_match);
2598 0 : if (i != XML_NOT_FOUND)
2599 0 : XMLArrayDelete(cx, &decls.array, i, JS_TRUE);
2600 : }
2601 :
2602 : /*
2603 : * In the spec, ancdecls has no name, but is always written out as
2604 : * (AncestorNamespaces U namespaceDeclarations). Since we compute
2605 : * that union in ancdecls, any time we append a namespace strong
2606 : * ref to decls, we must also append a weak ref to ancdecls. Order
2607 : * matters here: code at label out: releases strong refs in decls.
2608 : */
2609 0 : if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns) ||
2610 0 : !XMLARRAY_APPEND(cx, &decls.array, ns)) {
2611 0 : goto out;
2612 : }
2613 : }
2614 :
2615 : /* Format the element or point-tag into sb. */
2616 117 : if (!sb.append('<'))
2617 0 : goto out;
2618 :
2619 117 : if (!prefix->empty()) {
2620 0 : if (!sb.append(prefix) || !sb.append(':'))
2621 0 : goto out;
2622 : }
2623 117 : if (!sb.append(xml->name->getQNameLocalName()))
2624 0 : goto out;
2625 :
2626 : /*
2627 : * Step 16 makes a union to avoid writing two loops in step 17, to share
2628 : * common attribute value appending spec-code. We prefer two loops for
2629 : * faster code and less data overhead.
2630 : */
2631 :
2632 : /* Step 17(b): append attributes. */
2633 : {
2634 234 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_attrs);
2635 117 : while (JSXML *attr = cursor.getNext()) {
2636 0 : if (!sb.append(' '))
2637 : goto out;
2638 0 : ns2 = GetNamespace(cx, attr->name, &ancdecls.array);
2639 0 : if (!ns2)
2640 : goto out;
2641 :
2642 : /* 17(b)(ii): NULL means *undefined* here. */
2643 0 : prefix = ns2->getNamePrefix();
2644 0 : if (!prefix) {
2645 0 : prefix = GeneratePrefix(cx, ns2->getNameURI(), &ancdecls.array);
2646 0 : if (!prefix)
2647 : goto out;
2648 :
2649 : /* Again, we avoid copying ns2 until we know it's prefix-less. */
2650 0 : ns2 = NewXMLNamespace(cx, prefix, ns2->getNameURI(), JS_TRUE);
2651 0 : if (!ns2)
2652 : goto out;
2653 :
2654 : /*
2655 : * In the spec, ancdecls has no name, but is always written out as
2656 : * (AncestorNamespaces U namespaceDeclarations). Since we compute
2657 : * that union in ancdecls, any time we append a namespace strong
2658 : * ref to decls, we must also append a weak ref to ancdecls. Order
2659 : * matters here: code at label out: releases strong refs in decls.
2660 : */
2661 0 : if (!XMLARRAY_APPEND(cx, &ancdecls.array, ns2) ||
2662 0 : !XMLARRAY_APPEND(cx, &decls.array, ns2)) {
2663 : goto out;
2664 : }
2665 : }
2666 :
2667 : /* 17(b)(iii). */
2668 0 : if (!prefix->empty()) {
2669 0 : if (!sb.append(prefix) || !sb.append(':'))
2670 : goto out;
2671 : }
2672 :
2673 : /* 17(b)(iv). */
2674 0 : if (!sb.append(attr->name->getQNameLocalName()))
2675 : goto out;
2676 :
2677 : /* 17(d-g). */
2678 0 : if (!AppendAttributeValue(cx, sb, attr->xml_value))
2679 : goto out;
2680 : }
2681 : }
2682 :
2683 : /* Step 17(c): append XML namespace declarations. */
2684 : {
2685 234 : JSXMLArrayCursor<JSObject> cursor(&decls.array);
2686 117 : while (JSObject *ns3 = cursor.getNext()) {
2687 0 : JS_ASSERT(IsDeclared(ns3));
2688 :
2689 0 : if (!sb.append(" xmlns"))
2690 : goto out;
2691 :
2692 : /* 17(c)(ii): NULL means *undefined* here. */
2693 0 : prefix = ns3->getNamePrefix();
2694 0 : if (!prefix) {
2695 0 : prefix = GeneratePrefix(cx, ns3->getNameURI(), &ancdecls.array);
2696 0 : if (!prefix)
2697 : goto out;
2698 0 : ns3->setNamePrefix(prefix);
2699 : }
2700 :
2701 : /* 17(c)(iii). */
2702 0 : if (!prefix->empty()) {
2703 0 : if (!sb.append(':') || !sb.append(prefix))
2704 : goto out;
2705 : }
2706 :
2707 : /* 17(d-g). */
2708 0 : if (!AppendAttributeValue(cx, sb, ns3->getNameURI()))
2709 : goto out;
2710 : }
2711 : }
2712 :
2713 : /* Step 18: handle point tags. */
2714 117 : n = xml->xml_kids.length;
2715 117 : if (n == 0) {
2716 45 : if (!sb.append("/>"))
2717 0 : goto out;
2718 : } else {
2719 : /* Steps 19 through 25: handle element content, and open the end-tag. */
2720 72 : if (!sb.append('>'))
2721 0 : goto out;
2722 : {
2723 : JSXML *kid;
2724 : indentKids = n > 1 ||
2725 : (n == 1 &&
2726 72 : (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) &&
2727 144 : kid->xml_class != JSXML_CLASS_TEXT);
2728 : }
2729 :
2730 72 : if (pretty && indentKids) {
2731 54 : if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i))
2732 0 : goto out;
2733 54 : nextIndentLevel = indentLevel + i;
2734 : } else {
2735 18 : nextIndentLevel = indentLevel & TO_SOURCE_FLAG;
2736 : }
2737 :
2738 : {
2739 144 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
2740 216 : while (JSXML *kid = cursor.getNext()) {
2741 72 : if (pretty && indentKids) {
2742 54 : if (!sb.append('\n'))
2743 : goto out;
2744 : }
2745 :
2746 72 : JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls.array, nextIndentLevel, pretty);
2747 72 : if (!kidstr)
2748 : goto out;
2749 :
2750 72 : if (!sb.append(kidstr))
2751 : goto out;
2752 : }
2753 : }
2754 :
2755 72 : if (pretty && indentKids) {
2756 108 : if (!sb.append('\n') ||
2757 54 : !sb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG))
2758 0 : goto out;
2759 : }
2760 72 : if (!sb.append("</"))
2761 0 : goto out;
2762 :
2763 : /* Step 26. */
2764 72 : prefix = ns->getNamePrefix();
2765 72 : if (prefix && !prefix->empty()) {
2766 0 : if (!sb.append(prefix) || !sb.append(':'))
2767 0 : goto out;
2768 : }
2769 :
2770 : /* Step 27. */
2771 72 : if (!sb.append(xml->name->getQNameLocalName()) || !sb.append('>'))
2772 0 : goto out;
2773 : }
2774 :
2775 117 : str = sb.finishString();
2776 : out:
2777 117 : js_LeaveLocalRootScopeWithResult(cx, str);
2778 117 : return str;
2779 : }
2780 :
2781 : /* ECMA-357 10.2 */
2782 : static JSString *
2783 63 : ToXMLString(JSContext *cx, jsval v, uint32_t toSourceFlag)
2784 : {
2785 63 : if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
2786 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2787 : JSMSG_BAD_XML_CONVERSION,
2788 0 : JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
2789 0 : return NULL;
2790 : }
2791 :
2792 63 : if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v))
2793 0 : return ToString(cx, v);
2794 :
2795 63 : if (JSVAL_IS_STRING(v)) {
2796 0 : StringBuffer sb(cx);
2797 0 : return EscapeElementValue(cx, sb, JSVAL_TO_STRING(v), toSourceFlag);
2798 : }
2799 :
2800 63 : JSObject *obj = JSVAL_TO_OBJECT(v);
2801 63 : if (!obj->isXML()) {
2802 0 : if (!ToPrimitive(cx, JSTYPE_STRING, &v))
2803 0 : return NULL;
2804 0 : JSString *str = ToString(cx, v);
2805 0 : if (!str)
2806 0 : return NULL;
2807 0 : StringBuffer sb(cx);
2808 0 : return EscapeElementValue(cx, sb, str, toSourceFlag);
2809 : }
2810 :
2811 : JSBool pretty;
2812 63 : if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2813 0 : return NULL;
2814 :
2815 : /* Handle non-element cases in this switch, returning from each case. */
2816 126 : JS::Anchor<JSObject *> anch(obj);
2817 63 : JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate());
2818 63 : return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0, pretty);
2819 : }
2820 :
2821 : static JSObject *
2822 0 : ToAttributeName(JSContext *cx, jsval v)
2823 : {
2824 : JSLinearString *uri, *prefix;
2825 : JSObject *obj;
2826 : Class *clasp;
2827 : JSObject *qn;
2828 :
2829 : JSAtom *name;
2830 0 : if (JSVAL_IS_STRING(v)) {
2831 0 : if (!js_ValueToAtom(cx, v, &name))
2832 0 : return NULL;
2833 0 : uri = prefix = cx->runtime->emptyString;
2834 : } else {
2835 0 : if (JSVAL_IS_PRIMITIVE(v)) {
2836 : js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
2837 0 : JSDVG_IGNORE_STACK, v, NULL);
2838 0 : return NULL;
2839 : }
2840 :
2841 0 : obj = JSVAL_TO_OBJECT(v);
2842 0 : clasp = obj->getClass();
2843 0 : if (clasp == &AttributeNameClass)
2844 0 : return obj;
2845 :
2846 0 : if (clasp == &QNameClass) {
2847 0 : qn = obj;
2848 0 : uri = qn->getNameURI();
2849 0 : prefix = qn->getNamePrefix();
2850 0 : name = qn->getQNameLocalName();
2851 : } else {
2852 0 : if (clasp == &AnyNameClass) {
2853 0 : name = cx->runtime->atomState.starAtom;
2854 : } else {
2855 0 : if (!js_ValueToAtom(cx, v, &name))
2856 0 : return NULL;
2857 : }
2858 0 : uri = prefix = cx->runtime->emptyString;
2859 : }
2860 : }
2861 :
2862 0 : qn = NewXMLAttributeName(cx, uri, prefix, name);
2863 0 : if (!qn)
2864 0 : return NULL;
2865 0 : return qn;
2866 : }
2867 :
2868 : static void
2869 9 : ReportBadXMLName(JSContext *cx, const Value &idval)
2870 : {
2871 9 : js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL);
2872 9 : }
2873 :
2874 : namespace js {
2875 :
2876 : bool
2877 12186 : GetLocalNameFromFunctionQName(JSObject *qn, JSAtom **namep, JSContext *cx)
2878 : {
2879 12186 : JSAtom *atom = cx->runtime->atomState.functionNamespaceURIAtom;
2880 12186 : JSLinearString *uri = qn->getNameURI();
2881 12186 : if (uri && (uri == atom || EqualStrings(uri, atom))) {
2882 36 : *namep = qn->getQNameLocalName();
2883 36 : return true;
2884 : }
2885 12150 : return false;
2886 : }
2887 :
2888 : } /* namespace js */
2889 :
2890 : bool
2891 825 : js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx)
2892 : {
2893 825 : if (!obj->isQName())
2894 816 : return false;
2895 : JSAtom *name;
2896 9 : if (GetLocalNameFromFunctionQName(obj, &name, cx)) {
2897 9 : *funidp = ATOM_TO_JSID(name);
2898 9 : return true;
2899 : }
2900 0 : return false;
2901 : }
2902 :
2903 : static JSObject *
2904 12123 : ToXMLName(JSContext *cx, jsval v, jsid *funidp)
2905 : {
2906 : JSAtom *atomizedName;
2907 : JSString *name;
2908 : JSObject *obj;
2909 : Class *clasp;
2910 : uint32_t index;
2911 :
2912 12123 : if (JSVAL_IS_STRING(v)) {
2913 12123 : name = JSVAL_TO_STRING(v);
2914 : } else {
2915 0 : if (JSVAL_IS_PRIMITIVE(v)) {
2916 0 : ReportBadXMLName(cx, v);
2917 0 : return NULL;
2918 : }
2919 :
2920 0 : obj = JSVAL_TO_OBJECT(v);
2921 0 : clasp = obj->getClass();
2922 0 : if (clasp == &AttributeNameClass || clasp == &QNameClass)
2923 : goto out;
2924 0 : if (clasp == &AnyNameClass) {
2925 0 : name = cx->runtime->atomState.starAtom;
2926 0 : goto construct;
2927 : }
2928 0 : name = ToStringSlow(cx, v);
2929 0 : if (!name)
2930 0 : return NULL;
2931 : }
2932 :
2933 12123 : atomizedName = js_AtomizeString(cx, name);
2934 12123 : if (!atomizedName)
2935 0 : return NULL;
2936 :
2937 : /*
2938 : * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says:
2939 : *
2940 : * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
2941 : *
2942 : * First, _P_ should be _s_, to refer to the given string.
2943 : *
2944 : * Second, why does ToXMLName applied to the string type throw TypeError
2945 : * only for numeric literals without any leading or trailing whitespace?
2946 : *
2947 : * If the idea is to reject uint32_t property names, then the check needs to
2948 : * be stricter, to exclude hexadecimal and floating point literals.
2949 : */
2950 12123 : if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index))
2951 0 : goto bad;
2952 :
2953 12123 : if (*atomizedName->chars() == '@') {
2954 0 : name = js_NewDependentString(cx, name, 1, name->length() - 1);
2955 0 : if (!name)
2956 0 : return NULL;
2957 0 : *funidp = JSID_VOID;
2958 0 : return ToAttributeName(cx, STRING_TO_JSVAL(name));
2959 : }
2960 :
2961 : construct:
2962 12123 : v = STRING_TO_JSVAL(name);
2963 12123 : obj = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 1, &v);
2964 12123 : if (!obj)
2965 0 : return NULL;
2966 :
2967 : out:
2968 : JSAtom *localName;
2969 12123 : *funidp = GetLocalNameFromFunctionQName(obj, &localName, cx)
2970 0 : ? ATOM_TO_JSID(localName)
2971 12123 : : JSID_VOID;
2972 12123 : return obj;
2973 :
2974 : bad:
2975 0 : JSAutoByteString bytes;
2976 0 : if (js_ValueToPrintable(cx, StringValue(name), &bytes))
2977 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, bytes.ptr());
2978 0 : return NULL;
2979 : }
2980 :
2981 : /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */
2982 : static JSBool
2983 9 : AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns)
2984 : {
2985 : JSLinearString *prefix, *prefix2;
2986 : JSObject *match, *ns2;
2987 : uint32_t i, n, m;
2988 :
2989 9 : if (xml->xml_class != JSXML_CLASS_ELEMENT)
2990 0 : return JS_TRUE;
2991 :
2992 : /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */
2993 9 : prefix = ns->getNamePrefix();
2994 9 : if (!prefix) {
2995 0 : match = NULL;
2996 0 : for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
2997 0 : ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
2998 0 : if (ns2 && EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
2999 0 : match = ns2;
3000 0 : break;
3001 : }
3002 : }
3003 0 : if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns))
3004 0 : return JS_FALSE;
3005 : } else {
3006 9 : if (prefix->empty() && xml->name->getNameURI()->empty())
3007 9 : return JS_TRUE;
3008 0 : match = NULL;
3009 : #ifdef __GNUC__ /* suppress bogus gcc warnings */
3010 0 : m = XML_NOT_FOUND;
3011 : #endif
3012 0 : for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3013 0 : ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3014 0 : if (ns2 && (prefix2 = ns2->getNamePrefix()) &&
3015 0 : EqualStrings(prefix2, prefix)) {
3016 0 : match = ns2;
3017 0 : m = i;
3018 0 : break;
3019 : }
3020 : }
3021 0 : if (match && !EqualStrings(match->getNameURI(), ns->getNameURI())) {
3022 0 : ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE,
3023 0 : JSObject);
3024 0 : JS_ASSERT(ns2 == match);
3025 0 : match->clearNamePrefix();
3026 0 : if (!AddInScopeNamespace(cx, xml, match))
3027 0 : return JS_FALSE;
3028 : }
3029 0 : if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
3030 0 : return JS_FALSE;
3031 : }
3032 :
3033 : /* OPTION: enforce that descendants have superset namespaces. */
3034 0 : return JS_TRUE;
3035 : }
3036 :
3037 : /* ECMA-357 9.2.1.6 XMLList [[Append]]. */
3038 : static JSBool
3039 135 : Append(JSContext *cx, JSXML *list, JSXML *xml)
3040 : {
3041 135 : JS_ASSERT(list->xml_class == JSXML_CLASS_LIST);
3042 :
3043 135 : uint32_t i = list->xml_kids.length;
3044 135 : if (xml->xml_class == JSXML_CLASS_LIST) {
3045 0 : list->xml_target = xml->xml_target;
3046 0 : list->xml_targetprop = xml->xml_targetprop;
3047 0 : uint32_t n = JSXML_LENGTH(xml);
3048 0 : if (!list->xml_kids.setCapacity(cx, i + n))
3049 0 : return JS_FALSE;
3050 0 : for (uint32_t j = 0; j < n; j++) {
3051 0 : if (JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML))
3052 0 : XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid);
3053 : }
3054 0 : return JS_TRUE;
3055 : }
3056 :
3057 135 : list->xml_target = xml->parent;
3058 135 : if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
3059 0 : list->xml_targetprop = NULL;
3060 : else
3061 135 : list->xml_targetprop = xml->name;
3062 135 : if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml))
3063 0 : return JS_FALSE;
3064 135 : return JS_TRUE;
3065 : }
3066 :
3067 : /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */
3068 : static JSXML *
3069 : DeepCopyInLRS(JSContext *cx, JSXML *xml, unsigned flags);
3070 :
3071 : static JSXML *
3072 0 : DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, unsigned flags)
3073 : {
3074 : JSXML *copy;
3075 :
3076 : /* Our caller may not be protecting newborns with a local root scope. */
3077 0 : if (!js_EnterLocalRootScope(cx))
3078 0 : return NULL;
3079 0 : copy = DeepCopyInLRS(cx, xml, flags);
3080 0 : if (copy) {
3081 0 : if (obj) {
3082 : /* Caller provided the object for this copy, hook 'em up. */
3083 0 : obj->setPrivate(copy);
3084 0 : copy->object = obj;
3085 0 : } else if (!js_GetXMLObject(cx, copy)) {
3086 0 : copy = NULL;
3087 : }
3088 : }
3089 0 : js_LeaveLocalRootScopeWithResult(cx, copy);
3090 0 : return copy;
3091 : }
3092 :
3093 : /*
3094 : * (i) We must be in a local root scope (InLRS).
3095 : * (ii) parent must have a rooted object.
3096 : * (iii) from's owning object must be locked if not thread-local.
3097 : */
3098 : static JSBool
3099 4718700 : DeepCopySetInLRS(JSContext *cx, JSXMLArray<JSXML> *from, JSXMLArray<JSXML> *to, JSXML *parent,
3100 : unsigned flags)
3101 : {
3102 : uint32_t j, n;
3103 : JSXML *kid2;
3104 : JSString *str;
3105 :
3106 4718700 : n = from->length;
3107 4718700 : if (!to->setCapacity(cx, n))
3108 0 : return JS_FALSE;
3109 :
3110 9437400 : JSXMLArrayCursor<JSXML> cursor(from);
3111 4718700 : j = 0;
3112 14155596 : while (JSXML *kid = cursor.getNext()) {
3113 4718448 : if ((flags & XSF_IGNORE_COMMENTS) &&
3114 : kid->xml_class == JSXML_CLASS_COMMENT) {
3115 0 : continue;
3116 : }
3117 4718448 : if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) &&
3118 : kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
3119 0 : continue;
3120 : }
3121 4718448 : if ((flags & XSF_IGNORE_WHITESPACE) &&
3122 : (kid->xml_flags & XMLF_WHITESPACE_TEXT)) {
3123 0 : continue;
3124 : }
3125 4718448 : kid2 = DeepCopyInLRS(cx, kid, flags);
3126 4718448 : if (!kid2) {
3127 0 : to->length = j;
3128 0 : return JS_FALSE;
3129 : }
3130 :
3131 4718448 : if ((flags & XSF_IGNORE_WHITESPACE) &&
3132 : n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) {
3133 0 : str = ChompXMLWhitespace(cx, kid2->xml_value);
3134 0 : if (!str) {
3135 0 : to->length = j;
3136 0 : return JS_FALSE;
3137 : }
3138 0 : kid2->xml_value = str;
3139 : }
3140 :
3141 4718448 : XMLARRAY_SET_MEMBER(to, j, kid2);
3142 4718448 : ++j;
3143 4718448 : if (parent->xml_class != JSXML_CLASS_LIST)
3144 4718448 : kid2->parent = parent;
3145 : }
3146 :
3147 4718700 : if (j < n)
3148 0 : to->trim();
3149 4718700 : return JS_TRUE;
3150 : }
3151 :
3152 : static JSXML *
3153 4718637 : DeepCopyInLRS(JSContext *cx, JSXML *xml, unsigned flags)
3154 : {
3155 : JSXML *copy;
3156 : JSObject *qn;
3157 : JSBool ok;
3158 : uint32_t i, n;
3159 : JSObject *ns, *ns2;
3160 :
3161 4718637 : JS_CHECK_RECURSION(cx, return NULL);
3162 :
3163 4718637 : copy = js_NewXML(cx, JSXMLClass(xml->xml_class));
3164 4718637 : if (!copy)
3165 0 : return NULL;
3166 4718637 : qn = xml->name;
3167 4718637 : if (qn) {
3168 2359350 : qn = NewXMLQName(cx, qn->getNameURI(), qn->getNamePrefix(), qn->getQNameLocalName());
3169 2359350 : if (!qn) {
3170 0 : ok = JS_FALSE;
3171 0 : goto out;
3172 : }
3173 : }
3174 4718637 : copy->name = qn;
3175 4718637 : copy->xml_flags = xml->xml_flags;
3176 :
3177 4718637 : if (JSXML_HAS_VALUE(xml)) {
3178 2359287 : copy->xml_value = xml->xml_value;
3179 2359287 : ok = JS_TRUE;
3180 : } else {
3181 2359350 : ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags);
3182 2359350 : if (!ok)
3183 0 : goto out;
3184 :
3185 2359350 : if (xml->xml_class == JSXML_CLASS_LIST) {
3186 0 : copy->xml_target = xml->xml_target;
3187 0 : copy->xml_targetprop = xml->xml_targetprop;
3188 : } else {
3189 2359350 : n = xml->xml_namespaces.length;
3190 2359350 : ok = copy->xml_namespaces.setCapacity(cx, n);
3191 2359350 : if (!ok)
3192 0 : goto out;
3193 4718700 : for (i = 0; i < n; i++) {
3194 2359350 : ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3195 2359350 : if (!ns)
3196 0 : continue;
3197 : ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(),
3198 2359350 : IsDeclared(ns));
3199 2359350 : if (!ns2) {
3200 0 : copy->xml_namespaces.length = i;
3201 0 : ok = JS_FALSE;
3202 0 : goto out;
3203 : }
3204 2359350 : XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2);
3205 : }
3206 :
3207 : ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy,
3208 2359350 : 0);
3209 2359350 : if (!ok)
3210 0 : goto out;
3211 : }
3212 : }
3213 :
3214 : out:
3215 4718637 : if (!ok)
3216 0 : return NULL;
3217 4718637 : return copy;
3218 : }
3219 :
3220 : /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */
3221 : static void
3222 0 : DeleteByIndex(JSContext *cx, JSXML *xml, uint32_t index)
3223 : {
3224 : JSXML *kid;
3225 :
3226 0 : if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) {
3227 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3228 0 : if (kid)
3229 0 : kid->parent = NULL;
3230 0 : XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3231 : }
3232 0 : }
3233 :
3234 : typedef JSBool (*JSXMLNameMatcher)(JSObject *nameqn, JSXML *xml);
3235 :
3236 : static JSBool
3237 0 : MatchAttrName(JSObject *nameqn, JSXML *attr)
3238 : {
3239 0 : JSObject *attrqn = attr->name;
3240 0 : JSLinearString *localName = nameqn->getQNameLocalName();
3241 : JSLinearString *uri;
3242 :
3243 0 : return (IS_STAR(localName) ||
3244 0 : EqualStrings(attrqn->getQNameLocalName(), localName)) &&
3245 : (!(uri = nameqn->getNameURI()) ||
3246 0 : EqualStrings(attrqn->getNameURI(), uri));
3247 : }
3248 :
3249 : static JSBool
3250 1602 : MatchElemName(JSObject *nameqn, JSXML *elem)
3251 : {
3252 1602 : JSLinearString *localName = nameqn->getQNameLocalName();
3253 : JSLinearString *uri;
3254 :
3255 3168 : return (IS_STAR(localName) ||
3256 : (elem->xml_class == JSXML_CLASS_ELEMENT &&
3257 1440 : EqualStrings(elem->name->getQNameLocalName(), localName))) &&
3258 : (!(uri = nameqn->getNameURI()) ||
3259 : (elem->xml_class == JSXML_CLASS_ELEMENT &&
3260 4608 : EqualStrings(elem->name->getNameURI(), uri)));
3261 : }
3262 :
3263 : /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */
3264 : static JSBool
3265 18 : DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list)
3266 : {
3267 : uint32_t i, n;
3268 : JSXML *attr, *kid;
3269 :
3270 18 : JS_CHECK_RECURSION(cx, return JS_FALSE);
3271 :
3272 36 : if (xml->xml_class == JSXML_CLASS_ELEMENT &&
3273 18 : nameqn->getClass() == &AttributeNameClass) {
3274 0 : for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
3275 0 : attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3276 0 : if (attr && MatchAttrName(nameqn, attr)) {
3277 0 : if (!Append(cx, list, attr))
3278 0 : return JS_FALSE;
3279 : }
3280 : }
3281 : }
3282 :
3283 18 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
3284 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3285 0 : if (!kid)
3286 0 : continue;
3287 0 : if (nameqn->getClass() != &AttributeNameClass &&
3288 0 : MatchElemName(nameqn, kid)) {
3289 0 : if (!Append(cx, list, kid))
3290 0 : return JS_FALSE;
3291 : }
3292 0 : if (!DescendantsHelper(cx, kid, nameqn, list))
3293 0 : return JS_FALSE;
3294 : }
3295 18 : return JS_TRUE;
3296 : }
3297 :
3298 : static JSXML *
3299 9 : Descendants(JSContext *cx, JSXML *xml, jsval id)
3300 : {
3301 : jsid funid;
3302 : JSObject *nameqn;
3303 : JSObject *listobj;
3304 : JSXML *list, *kid;
3305 : uint32_t i, n;
3306 : JSBool ok;
3307 :
3308 9 : nameqn = ToXMLName(cx, id, &funid);
3309 9 : if (!nameqn)
3310 0 : return NULL;
3311 :
3312 9 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3313 9 : if (!listobj)
3314 0 : return NULL;
3315 9 : list = (JSXML *) listobj->getPrivate();
3316 9 : if (!JSID_IS_VOID(funid))
3317 0 : return list;
3318 :
3319 : /*
3320 : * Protect nameqn's object and strings from GC by linking list to it
3321 : * temporarily. The newborn GC root for the last allocated object
3322 : * protects listobj, which protects list. Any other object allocations
3323 : * occurring beneath DescendantsHelper use local roots.
3324 : */
3325 9 : list->name = nameqn;
3326 9 : if (!js_EnterLocalRootScope(cx))
3327 0 : return NULL;
3328 9 : if (xml->xml_class == JSXML_CLASS_LIST) {
3329 9 : ok = JS_TRUE;
3330 27 : for (i = 0, n = xml->xml_kids.length; i < n; i++) {
3331 18 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3332 18 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
3333 18 : ok = DescendantsHelper(cx, kid, nameqn, list);
3334 18 : if (!ok)
3335 0 : break;
3336 : }
3337 : }
3338 : } else {
3339 0 : ok = DescendantsHelper(cx, xml, nameqn, list);
3340 : }
3341 9 : js_LeaveLocalRootScopeWithResult(cx, list);
3342 9 : if (!ok)
3343 0 : return NULL;
3344 9 : list->name = NULL;
3345 9 : return list;
3346 : }
3347 :
3348 : /* Recursive (JSXML *) parameterized version of Equals. */
3349 : static JSBool
3350 0 : XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp)
3351 : {
3352 : JSObject *qn, *vqn;
3353 : uint32_t i, j, n;
3354 : JSXML *kid, *vkid, *attr, *vattr;
3355 : JSObject *xobj, *vobj;
3356 :
3357 : retry:
3358 0 : if (xml->xml_class != vxml->xml_class) {
3359 0 : if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) {
3360 0 : xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3361 0 : if (xml)
3362 0 : goto retry;
3363 : }
3364 0 : if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) {
3365 0 : vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML);
3366 0 : if (vxml)
3367 0 : goto retry;
3368 : }
3369 0 : *bp = JS_FALSE;
3370 0 : return JS_TRUE;
3371 : }
3372 :
3373 0 : qn = xml->name;
3374 0 : vqn = vxml->name;
3375 0 : if (qn) {
3376 : *bp = vqn &&
3377 0 : EqualStrings(qn->getQNameLocalName(), vqn->getQNameLocalName()) &&
3378 0 : EqualStrings(qn->getNameURI(), vqn->getNameURI());
3379 : } else {
3380 0 : *bp = vqn == NULL;
3381 : }
3382 0 : if (!*bp)
3383 0 : return JS_TRUE;
3384 :
3385 0 : if (JSXML_HAS_VALUE(xml)) {
3386 : bool equal;
3387 0 : if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, &equal))
3388 0 : return JS_FALSE;
3389 0 : *bp = equal;
3390 0 : } else if (xml->xml_kids.length != vxml->xml_kids.length) {
3391 0 : *bp = JS_FALSE;
3392 : } else {
3393 : {
3394 0 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
3395 0 : JSXMLArrayCursor<JSXML> vcursor(&vxml->xml_kids);
3396 0 : for (;;) {
3397 0 : kid = cursor.getNext();
3398 0 : vkid = vcursor.getNext();
3399 0 : if (!kid || !vkid) {
3400 0 : *bp = !kid && !vkid;
3401 0 : break;
3402 : }
3403 0 : xobj = js_GetXMLObject(cx, kid);
3404 0 : vobj = js_GetXMLObject(cx, vkid);
3405 0 : if (!xobj || !vobj ||
3406 0 : !js_TestXMLEquality(cx, ObjectValue(*xobj), ObjectValue(*vobj), bp))
3407 0 : return JS_FALSE;
3408 0 : if (!*bp)
3409 0 : break;
3410 : }
3411 : }
3412 :
3413 0 : if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) {
3414 0 : n = xml->xml_attrs.length;
3415 0 : if (n != vxml->xml_attrs.length)
3416 0 : *bp = JS_FALSE;
3417 0 : for (i = 0; *bp && i < n; i++) {
3418 0 : attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
3419 0 : if (!attr)
3420 0 : continue;
3421 0 : j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity);
3422 0 : if (j == XML_NOT_FOUND) {
3423 0 : *bp = JS_FALSE;
3424 0 : break;
3425 : }
3426 0 : vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML);
3427 0 : if (!vattr)
3428 0 : continue;
3429 : bool equal;
3430 0 : if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, &equal))
3431 0 : return JS_FALSE;
3432 0 : *bp = equal;
3433 : }
3434 : }
3435 : }
3436 :
3437 0 : return JS_TRUE;
3438 : }
3439 :
3440 : /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */
3441 : static JSBool
3442 9 : Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp)
3443 : {
3444 : JSObject *vobj;
3445 : JSXML *vxml;
3446 :
3447 9 : if (JSVAL_IS_PRIMITIVE(v)) {
3448 9 : *bp = JS_FALSE;
3449 9 : if (xml->xml_class == JSXML_CLASS_LIST) {
3450 9 : if (xml->xml_kids.length == 1) {
3451 9 : vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
3452 9 : if (!vxml)
3453 0 : return JS_TRUE;
3454 9 : vobj = js_GetXMLObject(cx, vxml);
3455 9 : if (!vobj)
3456 0 : return JS_FALSE;
3457 9 : return js_TestXMLEquality(cx, ObjectValue(*vobj), v, bp);
3458 : }
3459 0 : if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0)
3460 0 : *bp = JS_TRUE;
3461 : }
3462 : } else {
3463 0 : vobj = JSVAL_TO_OBJECT(v);
3464 0 : if (!vobj->isXML()) {
3465 0 : *bp = JS_FALSE;
3466 : } else {
3467 0 : vxml = (JSXML *) vobj->getPrivate();
3468 0 : if (!XMLEquals(cx, xml, vxml, bp))
3469 0 : return JS_FALSE;
3470 : }
3471 : }
3472 0 : return JS_TRUE;
3473 : }
3474 :
3475 : static JSBool
3476 198 : CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid)
3477 : {
3478 198 : JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST);
3479 :
3480 198 : do {
3481 198 : if (xml == kid) {
3482 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3483 0 : JSMSG_CYCLIC_VALUE, js_XML_str);
3484 0 : return JS_FALSE;
3485 : }
3486 198 : } while ((xml = xml->parent) != NULL);
3487 :
3488 198 : return JS_TRUE;
3489 : }
3490 :
3491 : /* ECMA-357 9.1.1.11 XML [[Insert]]. */
3492 : static JSBool
3493 0 : Insert(JSContext *cx, JSXML *xml, uint32_t i, jsval v)
3494 : {
3495 : uint32_t j, n;
3496 : JSXML *vxml, *kid;
3497 : JSObject *vobj;
3498 : JSString *str;
3499 :
3500 0 : if (!JSXML_HAS_KIDS(xml))
3501 0 : return JS_TRUE;
3502 :
3503 0 : n = 1;
3504 0 : vxml = NULL;
3505 0 : if (!JSVAL_IS_PRIMITIVE(v)) {
3506 0 : vobj = JSVAL_TO_OBJECT(v);
3507 0 : if (vobj->isXML()) {
3508 0 : vxml = (JSXML *) vobj->getPrivate();
3509 0 : if (vxml->xml_class == JSXML_CLASS_LIST) {
3510 0 : n = vxml->xml_kids.length;
3511 0 : if (n == 0)
3512 0 : return JS_TRUE;
3513 0 : for (j = 0; j < n; j++) {
3514 0 : kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3515 0 : if (!kid)
3516 0 : continue;
3517 0 : if (!CheckCycle(cx, xml, kid))
3518 0 : return JS_FALSE;
3519 : }
3520 0 : } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) {
3521 : /* OPTION: enforce that descendants have superset namespaces. */
3522 0 : if (!CheckCycle(cx, xml, vxml))
3523 0 : return JS_FALSE;
3524 : }
3525 : }
3526 : }
3527 0 : if (!vxml) {
3528 0 : str = ToString(cx, v);
3529 0 : if (!str)
3530 0 : return JS_FALSE;
3531 :
3532 0 : vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3533 0 : if (!vxml)
3534 0 : return JS_FALSE;
3535 0 : vxml->xml_value = str;
3536 : }
3537 :
3538 0 : if (i > xml->xml_kids.length)
3539 0 : i = xml->xml_kids.length;
3540 :
3541 0 : if (!XMLArrayInsert(cx, &xml->xml_kids, i, n))
3542 0 : return JS_FALSE;
3543 :
3544 0 : if (vxml->xml_class == JSXML_CLASS_LIST) {
3545 0 : for (j = 0; j < n; j++) {
3546 0 : kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML);
3547 0 : if (!kid)
3548 0 : continue;
3549 0 : kid->parent = xml;
3550 0 : XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid);
3551 :
3552 : /* OPTION: enforce that descendants have superset namespaces. */
3553 : }
3554 : } else {
3555 0 : vxml->parent = xml;
3556 0 : XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
3557 : }
3558 0 : return JS_TRUE;
3559 : }
3560 :
3561 : /* ECMA-357 9.1.1.12 XML [[Replace]]. */
3562 : static JSBool
3563 216 : Replace(JSContext *cx, JSXML *xml, uint32_t i, jsval v)
3564 : {
3565 : uint32_t n;
3566 : JSXML *vxml, *kid;
3567 : JSObject *vobj;
3568 : JSString *str;
3569 :
3570 216 : if (!JSXML_HAS_KIDS(xml))
3571 0 : return JS_TRUE;
3572 :
3573 : /*
3574 : * 9.1.1.12
3575 : * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_.
3576 : * It should therefore constrain callers to pass in _i <= x.[[Length]]_.
3577 : */
3578 216 : n = xml->xml_kids.length;
3579 216 : if (i > n)
3580 0 : i = n;
3581 :
3582 216 : vxml = NULL;
3583 216 : if (!JSVAL_IS_PRIMITIVE(v)) {
3584 198 : vobj = JSVAL_TO_OBJECT(v);
3585 198 : if (vobj->isXML())
3586 198 : vxml = (JSXML *) vobj->getPrivate();
3587 : }
3588 :
3589 216 : switch (vxml ? JSXMLClass(vxml->xml_class) : JSXML_CLASS_LIMIT) {
3590 : case JSXML_CLASS_ELEMENT:
3591 : /* OPTION: enforce that descendants have superset namespaces. */
3592 198 : if (!CheckCycle(cx, xml, vxml))
3593 0 : return JS_FALSE;
3594 : case JSXML_CLASS_COMMENT:
3595 : case JSXML_CLASS_PROCESSING_INSTRUCTION:
3596 : case JSXML_CLASS_TEXT:
3597 198 : goto do_replace;
3598 :
3599 : case JSXML_CLASS_LIST:
3600 0 : if (i < n)
3601 0 : DeleteByIndex(cx, xml, i);
3602 0 : if (!Insert(cx, xml, i, v))
3603 0 : return JS_FALSE;
3604 0 : break;
3605 :
3606 : default:
3607 18 : str = ToString(cx, v);
3608 18 : if (!str)
3609 0 : return JS_FALSE;
3610 :
3611 18 : vxml = js_NewXML(cx, JSXML_CLASS_TEXT);
3612 18 : if (!vxml)
3613 0 : return JS_FALSE;
3614 18 : vxml->xml_value = str;
3615 :
3616 : do_replace:
3617 216 : vxml->parent = xml;
3618 216 : if (i < n) {
3619 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
3620 0 : if (kid)
3621 0 : kid->parent = NULL;
3622 : }
3623 216 : if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml))
3624 0 : return JS_FALSE;
3625 216 : break;
3626 : }
3627 :
3628 216 : return JS_TRUE;
3629 : }
3630 :
3631 : /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */
3632 : static void
3633 36 : DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn,
3634 : JSBool attributes)
3635 : {
3636 : JSXMLArray<JSXML> *array;
3637 : uint32_t index, deleteCount;
3638 : JSXML *kid;
3639 : JSXMLNameMatcher matcher;
3640 :
3641 36 : if (xml->xml_class == JSXML_CLASS_LIST) {
3642 0 : array = &xml->xml_kids;
3643 0 : for (index = 0; index < array->length; index++) {
3644 0 : kid = XMLARRAY_MEMBER(array, index, JSXML);
3645 0 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT)
3646 0 : DeleteNamedProperty(cx, kid, nameqn, attributes);
3647 : }
3648 36 : } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3649 36 : if (attributes) {
3650 0 : array = &xml->xml_attrs;
3651 0 : matcher = MatchAttrName;
3652 : } else {
3653 36 : array = &xml->xml_kids;
3654 36 : matcher = MatchElemName;
3655 : }
3656 36 : deleteCount = 0;
3657 36 : for (index = 0; index < array->length; index++) {
3658 0 : kid = XMLARRAY_MEMBER(array, index, JSXML);
3659 0 : if (kid && matcher(nameqn, kid)) {
3660 0 : kid->parent = NULL;
3661 0 : XMLArrayDelete(cx, array, index, JS_FALSE);
3662 0 : ++deleteCount;
3663 0 : } else if (deleteCount != 0) {
3664 0 : XMLARRAY_SET_MEMBER(array,
3665 : index - deleteCount,
3666 : array->vector[index]);
3667 : }
3668 : }
3669 36 : array->length -= deleteCount;
3670 : }
3671 36 : }
3672 :
3673 : /* ECMA-357 9.2.1.3 index case. */
3674 : static void
3675 0 : DeleteListElement(JSContext *cx, JSXML *xml, uint32_t index)
3676 : {
3677 : JSXML *kid, *parent;
3678 : uint32_t kidIndex;
3679 :
3680 0 : JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST);
3681 :
3682 0 : if (index < xml->xml_kids.length) {
3683 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3684 0 : if (kid) {
3685 0 : parent = kid->parent;
3686 0 : if (parent) {
3687 0 : JS_ASSERT(parent != xml);
3688 0 : JS_ASSERT(JSXML_HAS_KIDS(parent));
3689 :
3690 0 : if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
3691 0 : DeleteNamedProperty(cx, parent, kid->name, JS_TRUE);
3692 : } else {
3693 0 : kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid,
3694 0 : pointer_match);
3695 0 : JS_ASSERT(kidIndex != XML_NOT_FOUND);
3696 0 : DeleteByIndex(cx, parent, kidIndex);
3697 : }
3698 : }
3699 0 : XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE);
3700 : }
3701 : }
3702 0 : }
3703 :
3704 : static JSBool
3705 9 : SyncInScopeNamespaces(JSContext *cx, JSXML *xml)
3706 : {
3707 : JSXMLArray<JSObject> *nsarray;
3708 : uint32_t i, n;
3709 : JSObject *ns;
3710 :
3711 9 : nsarray = &xml->xml_namespaces;
3712 27 : while ((xml = xml->parent) != NULL) {
3713 18 : for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
3714 9 : ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
3715 9 : if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) {
3716 9 : if (!XMLARRAY_APPEND(cx, nsarray, ns))
3717 0 : return JS_FALSE;
3718 : }
3719 : }
3720 : }
3721 9 : return JS_TRUE;
3722 : }
3723 :
3724 : static JSBool
3725 6053 : GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list)
3726 : {
3727 : JSXMLArray<JSXML> *array;
3728 : JSXMLNameMatcher matcher;
3729 : JSBool attrs;
3730 :
3731 6053 : if (xml->xml_class == JSXML_CLASS_LIST) {
3732 7192 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
3733 3596 : while (JSXML *kid = cursor.getNext()) {
3734 0 : if (kid->xml_class == JSXML_CLASS_ELEMENT &&
3735 0 : !GetNamedProperty(cx, kid, nameqn, list)) {
3736 0 : return JS_FALSE;
3737 : }
3738 : }
3739 2457 : } else if (xml->xml_class == JSXML_CLASS_ELEMENT) {
3740 2457 : attrs = (nameqn->getClass() == &AttributeNameClass);
3741 2457 : if (attrs) {
3742 0 : array = &xml->xml_attrs;
3743 0 : matcher = MatchAttrName;
3744 : } else {
3745 2457 : array = &xml->xml_kids;
3746 2457 : matcher = MatchElemName;
3747 : }
3748 :
3749 4914 : JSXMLArrayCursor<JSXML> cursor(array);
3750 2475 : while (JSXML *kid = cursor.getNext()) {
3751 9 : if (matcher(nameqn, kid)) {
3752 18 : if (!attrs &&
3753 : kid->xml_class == JSXML_CLASS_ELEMENT &&
3754 9 : !SyncInScopeNamespaces(cx, kid)) {
3755 0 : return JS_FALSE;
3756 : }
3757 9 : if (!Append(cx, list, kid))
3758 0 : return JS_FALSE;
3759 : }
3760 : }
3761 : }
3762 :
3763 6053 : return JS_TRUE;
3764 : }
3765 :
3766 : /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */
3767 : static JSBool
3768 6053 : GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3769 : {
3770 : JSXML *xml, *list, *kid;
3771 : uint32_t index;
3772 : JSObject *kidobj, *listobj;
3773 : JSObject *nameqn;
3774 : jsid funid;
3775 :
3776 6053 : if (!obj->isXML())
3777 0 : return true;
3778 6053 : xml = (JSXML *) obj->getPrivate();
3779 6053 : if (!xml)
3780 0 : return true;
3781 :
3782 6053 : if (js_IdIsIndex(id, &index)) {
3783 0 : if (!JSXML_HAS_KIDS(xml)) {
3784 0 : *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID;
3785 : } else {
3786 : /*
3787 : * ECMA-357 9.2.1.1 starts here.
3788 : *
3789 : * Erratum: 9.2 is not completely clear that indexed properties
3790 : * correspond to kids, but that's what it seems to say, and it's
3791 : * what any sane user would want.
3792 : */
3793 0 : if (index < xml->xml_kids.length) {
3794 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3795 0 : if (!kid) {
3796 0 : *vp = JSVAL_VOID;
3797 0 : return true;
3798 : }
3799 0 : kidobj = js_GetXMLObject(cx, kid);
3800 0 : if (!kidobj)
3801 0 : return false;
3802 :
3803 0 : *vp = OBJECT_TO_JSVAL(kidobj);
3804 : } else {
3805 0 : *vp = JSVAL_VOID;
3806 : }
3807 : }
3808 0 : return true;
3809 : }
3810 :
3811 : /*
3812 : * ECMA-357 9.2.1.1/9.1.1.1 qname case.
3813 : */
3814 6053 : nameqn = ToXMLName(cx, IdToJsval(id), &funid);
3815 6053 : if (!nameqn)
3816 0 : return false;
3817 6053 : if (!JSID_IS_VOID(funid))
3818 0 : return GetXMLFunction(cx, obj, funid, vp);
3819 :
3820 6053 : jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL };
3821 12106 : AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
3822 :
3823 6053 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
3824 6053 : if (!listobj)
3825 0 : return false;
3826 :
3827 6053 : roots[1] = OBJECT_TO_JSVAL(listobj);
3828 :
3829 6053 : list = (JSXML *) listobj->getPrivate();
3830 6053 : if (!GetNamedProperty(cx, xml, nameqn, list))
3831 0 : return false;
3832 :
3833 : /*
3834 : * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
3835 : * given list's [[TargetProperty]] to the property that is being
3836 : * appended. This means that any use of the internal [[Get]]
3837 : * property returns a list which, when used by e.g. [[Insert]]
3838 : * duplicates the last element matched by id. See bug 336921.
3839 : */
3840 6053 : list->xml_target = xml;
3841 6053 : list->xml_targetprop = nameqn;
3842 6053 : *vp = OBJECT_TO_JSVAL(listobj);
3843 6053 : return true;
3844 : }
3845 :
3846 : static JSXML *
3847 0 : CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj)
3848 : {
3849 0 : JS_ASSERT(xml->object != obj);
3850 :
3851 0 : xml = DeepCopy(cx, xml, obj, 0);
3852 0 : if (!xml)
3853 0 : return NULL;
3854 :
3855 0 : JS_ASSERT(xml->object == obj);
3856 0 : return xml;
3857 : }
3858 :
3859 : #define CHECK_COPY_ON_WRITE(cx,xml,obj) \
3860 : (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj))
3861 :
3862 : static JSString *
3863 0 : KidToString(JSContext *cx, JSXML *xml, uint32_t index)
3864 : {
3865 : JSXML *kid;
3866 : JSObject *kidobj;
3867 :
3868 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
3869 0 : if (!kid)
3870 0 : return cx->runtime->emptyString;
3871 0 : kidobj = js_GetXMLObject(cx, kid);
3872 0 : if (!kidobj)
3873 0 : return NULL;
3874 0 : return ToString(cx, ObjectValue(*kidobj));
3875 : }
3876 :
3877 : /* Forward declared -- its implementation uses other statics that call it. */
3878 : static JSBool
3879 : ResolveValue(JSContext *cx, JSXML *list, JSXML **result);
3880 :
3881 : /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */
3882 : static JSBool
3883 207 : PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
3884 : {
3885 : JSBool ok, primitiveAssign;
3886 : enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
3887 : JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
3888 : JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
3889 : JSObject *targetprop, *nameqn, *attrqn;
3890 : uint32_t index, i, j, k, n, q, matchIndex;
3891 : jsval attrval, nsval;
3892 : jsid funid;
3893 : JSObject *ns;
3894 :
3895 207 : if (!obj->isXML())
3896 0 : return JS_TRUE;
3897 207 : xml = (JSXML *) obj->getPrivate();
3898 207 : if (!xml)
3899 0 : return JS_TRUE;
3900 :
3901 207 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
3902 207 : if (!xml)
3903 0 : return JS_FALSE;
3904 :
3905 : /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */
3906 207 : vxml = NULL;
3907 207 : if (!JSVAL_IS_PRIMITIVE(*vp)) {
3908 189 : vobj = JSVAL_TO_OBJECT(*vp);
3909 189 : if (vobj->isXML())
3910 189 : vxml = (JSXML *) vobj->getPrivate();
3911 : }
3912 :
3913 207 : ok = js_EnterLocalRootScope(cx);
3914 207 : if (!ok)
3915 0 : return JS_FALSE;
3916 :
3917 : MUST_FLOW_THROUGH("out");
3918 : jsval roots[3];
3919 207 : roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
3920 207 : roots[ID_ROOT] = IdToJsval(id);
3921 207 : roots[VAL_ROOT] = *vp;
3922 414 : AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
3923 :
3924 207 : if (js_IdIsIndex(id, &index)) {
3925 0 : if (xml->xml_class != JSXML_CLASS_LIST) {
3926 : /* See NOTE in spec: this variation is reserved for future use. */
3927 0 : ReportBadXMLName(cx, IdToValue(id));
3928 0 : goto bad;
3929 : }
3930 :
3931 : /*
3932 : * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index.
3933 : */
3934 0 : i = index;
3935 :
3936 : /* 2(a-b). */
3937 0 : if (xml->xml_target) {
3938 0 : ok = ResolveValue(cx, xml->xml_target, &rxml);
3939 0 : if (!ok)
3940 0 : goto out;
3941 0 : if (!rxml)
3942 0 : goto out;
3943 0 : JS_ASSERT(rxml->object);
3944 : } else {
3945 0 : rxml = NULL;
3946 : }
3947 :
3948 : /* 2(c). */
3949 0 : if (index >= xml->xml_kids.length) {
3950 : /* 2(c)(i). */
3951 0 : if (rxml) {
3952 0 : if (rxml->xml_class == JSXML_CLASS_LIST) {
3953 0 : if (rxml->xml_kids.length != 1)
3954 0 : goto out;
3955 0 : rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML);
3956 0 : if (!rxml)
3957 0 : goto out;
3958 0 : ok = js_GetXMLObject(cx, rxml) != NULL;
3959 0 : if (!ok)
3960 0 : goto out;
3961 : }
3962 :
3963 : /*
3964 : * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets
3965 : * _y.[[Parent]] = r_ where _r_ is the result of
3966 : * [[ResolveValue]] called on _x.[[TargetObject]] in
3967 : * 2(a)(i). This can result in text parenting text:
3968 : *
3969 : * var MYXML = new XML();
3970 : * MYXML.appendChild(new XML("<TEAM>Giants</TEAM>"));
3971 : *
3972 : * (testcase from Werner Sharp <wsharp@macromedia.com>).
3973 : *
3974 : * To match insertChildAfter, insertChildBefore,
3975 : * prependChild, and setChildren, we should silently
3976 : * do nothing in this case.
3977 : */
3978 0 : if (!JSXML_HAS_KIDS(rxml))
3979 0 : goto out;
3980 : }
3981 :
3982 : /* 2(c)(ii) is distributed below as several js_NewXML calls. */
3983 0 : targetprop = xml->xml_targetprop;
3984 0 : if (!targetprop || IS_STAR(targetprop->getQNameLocalName())) {
3985 : /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */
3986 0 : kid = js_NewXML(cx, JSXML_CLASS_TEXT);
3987 0 : if (!kid)
3988 0 : goto bad;
3989 : } else {
3990 0 : nameobj = targetprop;
3991 0 : if (nameobj->getClass() == &AttributeNameClass) {
3992 : /*
3993 : * 2(c)(iii)(1-3).
3994 : * Note that rxml can't be null here, because target
3995 : * and targetprop are non-null.
3996 : */
3997 0 : ok = GetProperty(cx, rxml->object, id, &attrval);
3998 0 : if (!ok)
3999 0 : goto out;
4000 0 : if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */
4001 0 : goto out;
4002 0 : attrobj = JSVAL_TO_OBJECT(attrval);
4003 0 : attr = (JSXML *) attrobj->getPrivate();
4004 0 : if (JSXML_LENGTH(attr) != 0)
4005 0 : goto out;
4006 :
4007 0 : kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4008 : } else {
4009 : /* 2(c)(v). */
4010 0 : kid = js_NewXML(cx, JSXML_CLASS_ELEMENT);
4011 : }
4012 0 : if (!kid)
4013 0 : goto bad;
4014 :
4015 : /* An important bit of 2(c)(ii). */
4016 0 : kid->name = targetprop;
4017 : }
4018 :
4019 : /* Final important bit of 2(c)(ii). */
4020 0 : kid->parent = rxml;
4021 :
4022 : /* 2(c)(vi-vii). */
4023 0 : i = xml->xml_kids.length;
4024 0 : if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) {
4025 : /*
4026 : * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null.
4027 : * y.[[Parent]] is here called kid->parent, which we know
4028 : * from 2(c)(ii) is _r_, here called rxml. So let's just
4029 : * test that! Erratum, the spec should be simpler here.
4030 : */
4031 0 : if (rxml) {
4032 0 : JS_ASSERT(JSXML_HAS_KIDS(rxml));
4033 0 : n = rxml->xml_kids.length;
4034 0 : j = n - 1;
4035 0 : if (n != 0 && i != 0) {
4036 0 : for (n = j, j = 0; j < n; j++) {
4037 0 : if (rxml->xml_kids.vector[j] ==
4038 0 : xml->xml_kids.vector[i-1]) {
4039 0 : break;
4040 : }
4041 : }
4042 : }
4043 :
4044 0 : kidobj = js_GetXMLObject(cx, kid);
4045 0 : if (!kidobj)
4046 0 : goto bad;
4047 0 : ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj));
4048 0 : if (!ok)
4049 0 : goto out;
4050 : }
4051 :
4052 : /*
4053 : * 2(c)(vii)(2-3).
4054 : * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a
4055 : * typo for [[TargetProperty]].
4056 : */
4057 0 : if (vxml) {
4058 : kid->name = (vxml->xml_class == JSXML_CLASS_LIST)
4059 : ? vxml->xml_targetprop
4060 0 : : vxml->name;
4061 : }
4062 : }
4063 :
4064 : /* 2(c)(viii). */
4065 0 : ok = Append(cx, xml, kid);
4066 0 : if (!ok)
4067 0 : goto out;
4068 : }
4069 :
4070 : /* 2(d). */
4071 0 : if (!vxml ||
4072 : vxml->xml_class == JSXML_CLASS_TEXT ||
4073 : vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4074 0 : ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4075 0 : if (!ok)
4076 0 : goto out;
4077 0 : roots[VAL_ROOT] = *vp;
4078 : }
4079 :
4080 : /* 2(e). */
4081 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
4082 0 : if (!kid)
4083 0 : goto out;
4084 0 : parent = kid->parent;
4085 0 : if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
4086 0 : nameobj = kid->name;
4087 0 : if (nameobj->getClass() != &AttributeNameClass) {
4088 : nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(),
4089 0 : nameobj->getQNameLocalName());
4090 0 : if (!nameobj)
4091 0 : goto bad;
4092 : }
4093 0 : id = OBJECT_TO_JSID(nameobj);
4094 :
4095 0 : if (parent) {
4096 : /* 2(e)(i). */
4097 0 : parentobj = js_GetXMLObject(cx, parent);
4098 0 : if (!parentobj)
4099 0 : goto bad;
4100 0 : ok = PutProperty(cx, parentobj, id, strict, vp);
4101 0 : if (!ok)
4102 0 : goto out;
4103 :
4104 : /* 2(e)(ii). */
4105 0 : ok = GetProperty(cx, parentobj, id, vp);
4106 0 : if (!ok)
4107 0 : goto out;
4108 0 : attr = (JSXML *) JSVAL_TO_OBJECT(*vp)->getPrivate();
4109 :
4110 : /* 2(e)(iii) - the length check comes from the bug 375406. */
4111 0 : if (attr->xml_kids.length != 0)
4112 0 : xml->xml_kids.vector[i] = attr->xml_kids.vector[0];
4113 : }
4114 : }
4115 :
4116 : /* 2(f). */
4117 0 : else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4118 : /*
4119 : * 2(f)(i)
4120 : *
4121 : * Erratum: the spec says to create a shallow copy _c_ of _V_, but
4122 : * if we do that we never change the parent of each child in the
4123 : * list. Since [[Put]] when called on an XML object deeply copies
4124 : * the provided list _V_, we also do so here. Perhaps the shallow
4125 : * copy was a misguided optimization?
4126 : */
4127 0 : copy = DeepCopyInLRS(cx, vxml, 0);
4128 0 : if (!copy)
4129 0 : goto bad;
4130 0 : copyobj = js_GetXMLObject(cx, copy);
4131 0 : if (!copyobj)
4132 0 : goto bad;
4133 :
4134 0 : JS_ASSERT(parent != xml);
4135 0 : if (parent) {
4136 0 : q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, pointer_match);
4137 0 : JS_ASSERT(q != XML_NOT_FOUND);
4138 0 : ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj));
4139 0 : if (!ok)
4140 0 : goto out;
4141 :
4142 : #ifdef DEBUG
4143 : /* Erratum: this loop in the spec is useless. */
4144 0 : for (j = 0, n = copy->xml_kids.length; j < n; j++) {
4145 0 : kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML);
4146 0 : JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML)
4147 0 : == kid2);
4148 : }
4149 : #endif
4150 : }
4151 :
4152 : /*
4153 : * 2(f)(iv-vi).
4154 : * Erratum: notice the unhandled zero-length V basis case and
4155 : * the off-by-one errors for the n != 0 cases in the spec.
4156 : */
4157 0 : n = copy->xml_kids.length;
4158 0 : if (n == 0) {
4159 0 : XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE);
4160 : } else {
4161 0 : ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1);
4162 0 : if (!ok)
4163 0 : goto out;
4164 :
4165 0 : for (j = 0; j < n; j++)
4166 0 : xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j];
4167 : }
4168 : }
4169 :
4170 : /* 2(g). */
4171 0 : else if (vxml || JSXML_HAS_VALUE(kid)) {
4172 0 : if (parent) {
4173 0 : q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, pointer_match);
4174 0 : JS_ASSERT(q != XML_NOT_FOUND);
4175 0 : ok = Replace(cx, parent, q, *vp);
4176 0 : if (!ok)
4177 0 : goto out;
4178 :
4179 0 : vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML);
4180 0 : if (!vxml)
4181 0 : goto out;
4182 0 : roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object);
4183 : }
4184 :
4185 : /*
4186 : * 2(g)(iii).
4187 : * Erratum: _V_ may not be of type XML, but all index-named
4188 : * properties _x[i]_ in an XMLList _x_ must be of type XML,
4189 : * according to 9.2.1.1 Overview and other places in the spec.
4190 : *
4191 : * Thanks to 2(d), we know _V_ (*vp here) is either a string
4192 : * or an XML/XMLList object. If *vp is a string, call ToXML
4193 : * on it to satisfy the constraint.
4194 : */
4195 0 : if (!vxml) {
4196 0 : JS_ASSERT(JSVAL_IS_STRING(*vp));
4197 0 : vobj = ToXML(cx, *vp);
4198 0 : if (!vobj)
4199 0 : goto bad;
4200 0 : roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj);
4201 0 : vxml = (JSXML *) vobj->getPrivate();
4202 : }
4203 0 : XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml);
4204 : }
4205 :
4206 : /* 2(h). */
4207 : else {
4208 0 : kidobj = js_GetXMLObject(cx, kid);
4209 0 : if (!kidobj)
4210 0 : goto bad;
4211 0 : id = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
4212 0 : ok = PutProperty(cx, kidobj, id, strict, vp);
4213 0 : if (!ok)
4214 0 : goto out;
4215 : }
4216 : } else {
4217 : /*
4218 : * ECMA-357 9.2.1.2/9.1.1.2 qname case.
4219 : */
4220 207 : nameqn = ToXMLName(cx, IdToJsval(id), &funid);
4221 207 : if (!nameqn)
4222 0 : goto bad;
4223 207 : if (!JSID_IS_VOID(funid)) {
4224 0 : ok = js_SetPropertyHelper(cx, obj, funid, 0, vp, false);
4225 0 : goto out;
4226 : }
4227 207 : nameobj = nameqn;
4228 207 : roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj);
4229 :
4230 207 : if (xml->xml_class == JSXML_CLASS_LIST) {
4231 : /*
4232 : * Step 3 of 9.2.1.2.
4233 : * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null
4234 : * or an r with r.[[Length]] != 1, throw TypeError.
4235 : */
4236 0 : n = JSXML_LENGTH(xml);
4237 0 : if (n > 1)
4238 0 : goto type_error;
4239 0 : if (n == 0) {
4240 0 : ok = ResolveValue(cx, xml, &rxml);
4241 0 : if (!ok)
4242 0 : goto out;
4243 0 : if (!rxml || JSXML_LENGTH(rxml) != 1)
4244 : goto type_error;
4245 0 : ok = Append(cx, xml, rxml);
4246 0 : if (!ok)
4247 0 : goto out;
4248 : }
4249 0 : JS_ASSERT(JSXML_LENGTH(xml) == 1);
4250 0 : xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
4251 0 : if (!xml)
4252 0 : goto out;
4253 0 : JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
4254 0 : obj = js_GetXMLObject(cx, xml);
4255 0 : if (!obj)
4256 0 : goto bad;
4257 0 : roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
4258 :
4259 : /* FALL THROUGH to non-list case */
4260 : }
4261 :
4262 : /*
4263 : * ECMA-357 9.1.1.2.
4264 : * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted
4265 : * effort in ToString or [[DeepCopy]].
4266 : */
4267 :
4268 207 : if (JSXML_HAS_VALUE(xml))
4269 0 : goto out;
4270 :
4271 207 : if (!vxml ||
4272 : vxml->xml_class == JSXML_CLASS_TEXT ||
4273 : vxml->xml_class == JSXML_CLASS_ATTRIBUTE) {
4274 18 : ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4275 36 : if (!ok)
4276 0 : goto out;
4277 : } else {
4278 189 : rxml = DeepCopyInLRS(cx, vxml, 0);
4279 189 : if (!rxml || !js_GetXMLObject(cx, rxml))
4280 0 : goto bad;
4281 189 : vxml = rxml;
4282 189 : *vp = OBJECT_TO_JSVAL(vxml->object);
4283 : }
4284 207 : roots[VAL_ROOT] = *vp;
4285 :
4286 : /*
4287 : * 6.
4288 : * Erratum: why is this done here, so early? use is way later....
4289 : */
4290 207 : ok = js_GetDefaultXMLNamespace(cx, &nsval);
4291 207 : if (!ok)
4292 0 : goto out;
4293 :
4294 207 : if (nameobj->getClass() == &AttributeNameClass) {
4295 : /* 7(a). */
4296 0 : if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)))
4297 0 : goto out;
4298 :
4299 : /* 7(b-c). */
4300 0 : if (vxml && vxml->xml_class == JSXML_CLASS_LIST) {
4301 0 : n = vxml->xml_kids.length;
4302 0 : if (n == 0) {
4303 0 : *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
4304 : } else {
4305 0 : JSString *left = KidToString(cx, vxml, 0);
4306 0 : if (!left)
4307 0 : goto bad;
4308 :
4309 0 : JSString *space = cx->runtime->atomState.spaceAtom;
4310 0 : for (i = 1; i < n; i++) {
4311 0 : left = js_ConcatStrings(cx, left, space);
4312 0 : if (!left)
4313 0 : goto bad;
4314 0 : JSString *right = KidToString(cx, vxml, i);
4315 0 : if (!right)
4316 0 : goto bad;
4317 0 : left = js_ConcatStrings(cx, left, right);
4318 0 : if (!left)
4319 0 : goto bad;
4320 : }
4321 :
4322 0 : roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left);
4323 0 : }
4324 : } else {
4325 0 : ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4326 0 : if (!ok)
4327 0 : goto out;
4328 0 : roots[VAL_ROOT] = *vp;
4329 : }
4330 :
4331 : /* 7(d-e). */
4332 0 : match = NULL;
4333 0 : for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
4334 0 : attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
4335 0 : if (!attr)
4336 0 : continue;
4337 0 : attrqn = attr->name;
4338 0 : if (EqualStrings(attrqn->getQNameLocalName(), nameqn->getQNameLocalName())) {
4339 0 : JSLinearString *uri = nameqn->getNameURI();
4340 0 : if (!uri || EqualStrings(attrqn->getNameURI(), uri)) {
4341 0 : if (!match) {
4342 0 : match = attr;
4343 : } else {
4344 0 : DeleteNamedProperty(cx, xml, attrqn, JS_TRUE);
4345 0 : --i;
4346 : }
4347 : }
4348 : }
4349 : }
4350 :
4351 : /* 7(f). */
4352 0 : attr = match;
4353 0 : if (!attr) {
4354 : /* 7(f)(i-ii). */
4355 0 : JSLinearString *uri = nameqn->getNameURI();
4356 : JSLinearString *left, *right;
4357 0 : if (!uri) {
4358 0 : left = right = cx->runtime->emptyString;
4359 : } else {
4360 0 : left = uri;
4361 0 : right = nameqn->getNamePrefix();
4362 : }
4363 0 : nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4364 0 : if (!nameqn)
4365 0 : goto bad;
4366 :
4367 : /* 7(f)(iii). */
4368 0 : attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
4369 0 : if (!attr)
4370 0 : goto bad;
4371 0 : attr->parent = xml;
4372 0 : attr->name = nameqn;
4373 :
4374 : /* 7(f)(iv). */
4375 0 : ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr);
4376 0 : if (!ok)
4377 0 : goto out;
4378 :
4379 : /* 7(f)(v-vi). */
4380 0 : ns = GetNamespace(cx, nameqn, NULL);
4381 0 : if (!ns)
4382 0 : goto bad;
4383 0 : ok = AddInScopeNamespace(cx, xml, ns);
4384 0 : if (!ok)
4385 0 : goto out;
4386 : }
4387 :
4388 : /* 7(g). */
4389 0 : attr->xml_value = JSVAL_TO_STRING(*vp);
4390 0 : goto out;
4391 : }
4392 :
4393 : /* 8-9. */
4394 207 : if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) &&
4395 0 : !IS_STAR(nameqn->getQNameLocalName())) {
4396 0 : goto out;
4397 : }
4398 :
4399 : /* 10-11. */
4400 207 : id = JSID_VOID;
4401 207 : primitiveAssign = !vxml && !IS_STAR(nameqn->getQNameLocalName());
4402 :
4403 : /* 12. */
4404 207 : k = n = xml->xml_kids.length;
4405 207 : matchIndex = XML_NOT_FOUND;
4406 207 : kid2 = NULL;
4407 1989 : while (k != 0) {
4408 1575 : --k;
4409 1575 : kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML);
4410 1575 : if (kid && MatchElemName(nameqn, kid)) {
4411 9 : if (matchIndex != XML_NOT_FOUND)
4412 0 : DeleteByIndex(cx, xml, matchIndex);
4413 9 : matchIndex = k;
4414 9 : kid2 = kid;
4415 : }
4416 : }
4417 :
4418 : /*
4419 : * Erratum: ECMA-357 specified child insertion inconsistently:
4420 : * insertChildBefore and insertChildAfter insert an arbitrary XML
4421 : * instance, and therefore can create cycles, but appendChild as
4422 : * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on
4423 : * its argument. But the "Semantics" in 13.4.4.3 do not include
4424 : * any [[DeepCopy]] call.
4425 : *
4426 : * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692)
4427 : * required adding cycle detection, and allowing duplicate kids to
4428 : * be created (see comment 6 in the bug). Allowing duplicate kid
4429 : * references means the loop above will delete all but the lowest
4430 : * indexed reference, and each [[DeleteByIndex]] nulls the kid's
4431 : * parent. Thus the need to restore parent here. This is covered
4432 : * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564.
4433 : */
4434 207 : if (kid2) {
4435 9 : JS_ASSERT(kid2->parent == xml || !kid2->parent);
4436 9 : if (!kid2->parent)
4437 0 : kid2->parent = xml;
4438 : }
4439 :
4440 : /* 13. */
4441 207 : if (matchIndex == XML_NOT_FOUND) {
4442 : /* 13(a). */
4443 198 : matchIndex = n;
4444 :
4445 : /* 13(b). */
4446 198 : if (primitiveAssign) {
4447 9 : JSLinearString *uri = nameqn->getNameURI();
4448 : JSLinearString *left, *right;
4449 9 : if (!uri) {
4450 0 : ns = JSVAL_TO_OBJECT(nsval);
4451 0 : left = ns->getNameURI();
4452 0 : right = ns->getNamePrefix();
4453 : } else {
4454 9 : left = uri;
4455 9 : right = nameqn->getNamePrefix();
4456 : }
4457 9 : nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName());
4458 9 : if (!nameqn)
4459 0 : goto bad;
4460 :
4461 : /* 13(b)(iii). */
4462 9 : vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT);
4463 9 : if (!vobj)
4464 0 : goto bad;
4465 9 : vxml = (JSXML *) vobj->getPrivate();
4466 9 : vxml->parent = xml;
4467 9 : vxml->name = nameqn;
4468 :
4469 : /* 13(b)(iv-vi). */
4470 9 : ns = GetNamespace(cx, nameqn, NULL);
4471 9 : if (!ns)
4472 0 : goto bad;
4473 9 : ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj));
4474 9 : if (!ok)
4475 0 : goto out;
4476 9 : ok = AddInScopeNamespace(cx, vxml, ns);
4477 9 : if (!ok)
4478 0 : goto out;
4479 : }
4480 : }
4481 :
4482 : /* 14. */
4483 207 : if (primitiveAssign) {
4484 36 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
4485 18 : cursor.index = matchIndex;
4486 18 : kid = cursor.getCurrent();
4487 18 : if (JSXML_HAS_KIDS(kid)) {
4488 18 : kid->xml_kids.finish(cx->runtime->defaultFreeOp());
4489 18 : kid->xml_kids.init();
4490 18 : ok = kid->xml_kids.setCapacity(cx, 1);
4491 : }
4492 :
4493 : /* 14(b-c). */
4494 : /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
4495 18 : if (ok) {
4496 18 : ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
4497 18 : if (ok && !JSVAL_TO_STRING(*vp)->empty()) {
4498 18 : roots[VAL_ROOT] = *vp;
4499 18 : if (cursor.getCurrent() == kid)
4500 18 : ok = Replace(cx, kid, 0, *vp);
4501 : }
4502 : }
4503 : } else {
4504 : /* 15(a). */
4505 189 : ok = Replace(cx, xml, matchIndex, *vp);
4506 : }
4507 : }
4508 :
4509 : out:
4510 207 : js_LeaveLocalRootScope(cx);
4511 207 : return ok;
4512 :
4513 : type_error:
4514 : {
4515 0 : JSAutoByteString bytes;
4516 0 : if (js_ValueToPrintable(cx, IdToValue(id), &bytes))
4517 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XMLLIST_PUT, bytes.ptr());
4518 : }
4519 : bad:
4520 0 : ok = JS_FALSE;
4521 0 : goto out;
4522 : }
4523 :
4524 : /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */
4525 : static JSBool
4526 0 : ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
4527 : {
4528 : JSXML *target, *base;
4529 : JSObject *targetprop;
4530 : jsid id;
4531 : jsval tv;
4532 :
4533 0 : if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
4534 0 : if (!js_GetXMLObject(cx, list))
4535 0 : return JS_FALSE;
4536 0 : *result = list;
4537 0 : return JS_TRUE;
4538 : }
4539 :
4540 0 : target = list->xml_target;
4541 0 : targetprop = list->xml_targetprop;
4542 0 : if (!target || !targetprop || IS_STAR(targetprop->getQNameLocalName())) {
4543 0 : *result = NULL;
4544 0 : return JS_TRUE;
4545 : }
4546 :
4547 0 : if (targetprop->getClass() == &AttributeNameClass) {
4548 0 : *result = NULL;
4549 0 : return JS_TRUE;
4550 : }
4551 :
4552 0 : if (!ResolveValue(cx, target, &base))
4553 0 : return JS_FALSE;
4554 0 : if (!base) {
4555 0 : *result = NULL;
4556 0 : return JS_TRUE;
4557 : }
4558 0 : if (!js_GetXMLObject(cx, base))
4559 0 : return JS_FALSE;
4560 :
4561 0 : id = OBJECT_TO_JSID(targetprop);
4562 0 : if (!GetProperty(cx, base->object, id, &tv))
4563 0 : return JS_FALSE;
4564 0 : target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4565 :
4566 0 : if (JSXML_LENGTH(target) == 0) {
4567 0 : if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) {
4568 0 : *result = NULL;
4569 0 : return JS_TRUE;
4570 : }
4571 0 : tv = STRING_TO_JSVAL(cx->runtime->emptyString);
4572 0 : if (!PutProperty(cx, base->object, id, false, &tv))
4573 0 : return JS_FALSE;
4574 0 : if (!GetProperty(cx, base->object, id, &tv))
4575 0 : return JS_FALSE;
4576 0 : target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate();
4577 : }
4578 :
4579 0 : *result = target;
4580 0 : return JS_TRUE;
4581 : }
4582 :
4583 : static JSBool
4584 5197 : HasNamedProperty(JSXML *xml, JSObject *nameqn)
4585 : {
4586 : JSBool found;
4587 : JSXMLArray<JSXML> *array;
4588 : JSXMLNameMatcher matcher;
4589 : uint32_t i, n;
4590 :
4591 5197 : if (xml->xml_class == JSXML_CLASS_LIST) {
4592 0 : found = JS_FALSE;
4593 0 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
4594 0 : while (JSXML *kid = cursor.getNext()) {
4595 0 : found = HasNamedProperty(kid, nameqn);
4596 0 : if (found)
4597 0 : break;
4598 : }
4599 0 : return found;
4600 : }
4601 :
4602 5197 : if (xml->xml_class == JSXML_CLASS_ELEMENT) {
4603 661 : if (nameqn->getClass() == &AttributeNameClass) {
4604 0 : array = &xml->xml_attrs;
4605 0 : matcher = MatchAttrName;
4606 : } else {
4607 661 : array = &xml->xml_kids;
4608 661 : matcher = MatchElemName;
4609 : }
4610 661 : for (i = 0, n = array->length; i < n; i++) {
4611 18 : JSXML *kid = XMLARRAY_MEMBER(array, i, JSXML);
4612 18 : if (kid && matcher(nameqn, kid))
4613 18 : return JS_TRUE;
4614 : }
4615 : }
4616 :
4617 5179 : return JS_FALSE;
4618 : }
4619 :
4620 : static JSBool
4621 0 : HasIndexedProperty(JSXML *xml, uint32_t i)
4622 : {
4623 0 : if (xml->xml_class == JSXML_CLASS_LIST)
4624 0 : return i < JSXML_LENGTH(xml);
4625 :
4626 0 : if (xml->xml_class == JSXML_CLASS_ELEMENT)
4627 0 : return i == 0;
4628 :
4629 0 : return JS_FALSE;
4630 : }
4631 :
4632 : static JSBool
4633 : HasSimpleContent(JSXML *xml);
4634 :
4635 : static JSBool
4636 0 : HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
4637 : {
4638 : JSObject *pobj;
4639 : JSProperty *prop;
4640 : JSXML *xml;
4641 :
4642 0 : JS_ASSERT(obj->getClass() == &XMLClass);
4643 :
4644 0 : if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
4645 0 : return false;
4646 0 : if (!prop) {
4647 0 : xml = (JSXML *) obj->getPrivate();
4648 0 : if (HasSimpleContent(xml)) {
4649 : /*
4650 : * Search in String.prototype to set found whenever
4651 : * GetXMLFunction returns existing function.
4652 : */
4653 0 : JSObject *proto = obj->global().getOrCreateStringPrototype(cx);
4654 0 : if (!proto)
4655 0 : return false;
4656 :
4657 0 : if (!js_LookupProperty(cx, proto, funid, &pobj, &prop))
4658 0 : return false;
4659 : }
4660 : }
4661 0 : *found = (prop != NULL);
4662 0 : return true;
4663 : }
4664 :
4665 : static bool
4666 0 : IdValIsIndex(JSContext *cx, jsval id, uint32_t *indexp, bool *isIndex)
4667 : {
4668 0 : if (JSVAL_IS_INT(id)) {
4669 0 : int32_t i = JSVAL_TO_INT(id);
4670 0 : if (i < 0) {
4671 0 : *isIndex = false;
4672 0 : return true;
4673 : }
4674 0 : *indexp = (uint32_t)i;
4675 0 : *isIndex = true;
4676 0 : return true;
4677 : }
4678 :
4679 0 : if (!JSVAL_IS_STRING(id)) {
4680 0 : *isIndex = false;
4681 0 : return true;
4682 : }
4683 :
4684 0 : JSLinearString *str = JSVAL_TO_STRING(id)->ensureLinear(cx);
4685 0 : if (!str)
4686 0 : return false;
4687 :
4688 0 : *isIndex = StringIsArrayIndex(str, indexp);
4689 0 : return true;
4690 : }
4691 :
4692 : /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
4693 : static JSBool
4694 0 : HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found)
4695 : {
4696 : JSXML *xml;
4697 : bool isIndex;
4698 : uint32_t i;
4699 : JSObject *qn;
4700 : jsid funid;
4701 :
4702 0 : xml = (JSXML *) obj->getPrivate();
4703 0 : if (!IdValIsIndex(cx, id, &i, &isIndex))
4704 0 : return JS_FALSE;
4705 :
4706 0 : if (isIndex) {
4707 0 : *found = HasIndexedProperty(xml, i);
4708 : } else {
4709 0 : qn = ToXMLName(cx, id, &funid);
4710 0 : if (!qn)
4711 0 : return JS_FALSE;
4712 0 : if (!JSID_IS_VOID(funid)) {
4713 0 : if (!HasFunctionProperty(cx, obj, funid, found))
4714 0 : return JS_FALSE;
4715 : } else {
4716 0 : *found = HasNamedProperty(xml, qn);
4717 : }
4718 : }
4719 0 : return JS_TRUE;
4720 : }
4721 :
4722 : /*
4723 : * XML objects are native. Thus xml_lookupGeneric must return a valid
4724 : * Shape pointer parameter via *propp to signify "property found". Since the
4725 : * only call to xml_lookupGeneric is via JSObject::lookupGeneric, and then
4726 : * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
4727 : * JSOP_IN case in the interpreter, the only time we add a Shape here is when
4728 : * an unqualified name is being accessed or when "name in xml" is called.
4729 : *
4730 : * This scope property keeps the JSOP_NAME code in js_Interpret happy by
4731 : * giving it an shape with (getter, setter) == (GetProperty, PutProperty).
4732 : *
4733 : * NB: xml_deleteProperty must take care to remove any property added here.
4734 : *
4735 : * FIXME This clashes with the function namespace implementation which also
4736 : * uses native properties. Effectively after xml_lookupGeneric any property
4737 : * stored previously using assignments to xml.function::name will be removed.
4738 : * We partially workaround the problem in GetXMLFunction. There we take
4739 : * advantage of the fact that typically function:: is used to access the
4740 : * functions from XML.prototype. So when js_GetProperty returns a non-function
4741 : * property, we assume that it represents the result of GetProperty setter
4742 : * hiding the function and use an extra prototype chain lookup to recover it.
4743 : * For a proper solution see bug 355257.
4744 : */
4745 : static JSBool
4746 5197 : xml_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp)
4747 : {
4748 : JSBool found;
4749 : JSXML *xml;
4750 : uint32_t i;
4751 : JSObject *qn;
4752 : jsid funid;
4753 :
4754 5197 : xml = (JSXML *) obj->getPrivate();
4755 5197 : if (js_IdIsIndex(id, &i)) {
4756 0 : found = HasIndexedProperty(xml, i);
4757 : } else {
4758 5197 : qn = ToXMLName(cx, IdToJsval(id), &funid);
4759 5197 : if (!qn)
4760 0 : return JS_FALSE;
4761 5197 : if (!JSID_IS_VOID(funid))
4762 0 : return js_LookupProperty(cx, obj, funid, objp, propp);
4763 5197 : found = HasNamedProperty(xml, qn);
4764 : }
4765 5197 : if (!found) {
4766 5179 : *objp = NULL;
4767 5179 : *propp = NULL;
4768 : } else {
4769 : const Shape *shape =
4770 : js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
4771 : SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
4772 18 : 0, 0);
4773 18 : if (!shape)
4774 0 : return JS_FALSE;
4775 :
4776 18 : *objp = obj;
4777 18 : *propp = (JSProperty *) shape;
4778 : }
4779 5197 : return JS_TRUE;
4780 : }
4781 :
4782 : static JSBool
4783 0 : xml_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp,
4784 : JSProperty **propp)
4785 : {
4786 0 : return xml_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
4787 : }
4788 :
4789 : static JSBool
4790 0 : xml_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
4791 : JSProperty **propp)
4792 : {
4793 0 : JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate());
4794 0 : if (!HasIndexedProperty(xml, index)) {
4795 0 : *objp = NULL;
4796 0 : *propp = NULL;
4797 0 : return true;
4798 : }
4799 :
4800 : jsid id;
4801 0 : if (!IndexToId(cx, index, &id))
4802 0 : return false;
4803 :
4804 : const Shape *shape =
4805 : js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
4806 : SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
4807 0 : 0, 0);
4808 0 : if (!shape)
4809 0 : return false;
4810 :
4811 0 : *objp = obj;
4812 0 : *propp = (JSProperty *) shape;
4813 0 : return true;
4814 : }
4815 :
4816 : static JSBool
4817 0 : xml_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp)
4818 : {
4819 0 : return xml_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
4820 : }
4821 :
4822 : static JSBool
4823 31857 : xml_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v,
4824 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4825 : {
4826 31857 : if (IsFunctionObject(*v) || getter || setter ||
4827 : (attrs & JSPROP_ENUMERATE) == 0 ||
4828 : (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
4829 31857 : return js_DefineProperty(cx, obj, id, v, getter, setter, attrs);
4830 : }
4831 :
4832 0 : jsval tmp = *v;
4833 0 : return PutProperty(cx, obj, id, false, &tmp);
4834 : }
4835 :
4836 : static JSBool
4837 0 : xml_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v,
4838 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4839 : {
4840 0 : return xml_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs);
4841 : }
4842 :
4843 : static JSBool
4844 0 : xml_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v,
4845 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4846 : {
4847 : jsid id;
4848 0 : if (!IndexToId(cx, index, &id))
4849 0 : return false;
4850 0 : return xml_defineGeneric(cx, obj, id, v, getter, setter, attrs);
4851 : }
4852 :
4853 : static JSBool
4854 0 : xml_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v,
4855 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4856 : {
4857 0 : return xml_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs);
4858 : }
4859 :
4860 : static JSBool
4861 6044 : xml_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
4862 : {
4863 6044 : if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) {
4864 0 : vp->setUndefined();
4865 0 : return JS_TRUE;
4866 : }
4867 :
4868 6044 : return GetProperty(cx, obj, id, vp);
4869 : }
4870 :
4871 : static JSBool
4872 0 : xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
4873 : {
4874 0 : return xml_getGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp);
4875 : }
4876 :
4877 : static JSBool
4878 0 : xml_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
4879 : {
4880 : jsid id;
4881 0 : if (!IndexToId(cx, index, &id))
4882 0 : return false;
4883 0 : return xml_getGeneric(cx, obj, receiver, id, vp);
4884 : }
4885 :
4886 : static JSBool
4887 0 : xml_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
4888 : {
4889 0 : return xml_getGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
4890 : }
4891 :
4892 : static JSBool
4893 207 : xml_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
4894 : {
4895 207 : return PutProperty(cx, obj, id, strict, vp);
4896 : }
4897 :
4898 : static JSBool
4899 0 : xml_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
4900 : {
4901 0 : return xml_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
4902 : }
4903 :
4904 : static JSBool
4905 0 : xml_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
4906 : {
4907 : jsid id;
4908 0 : if (!IndexToId(cx, index, &id))
4909 0 : return false;
4910 0 : return xml_setGeneric(cx, obj, id, vp, strict);
4911 : }
4912 :
4913 : static JSBool
4914 0 : xml_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
4915 : {
4916 0 : return xml_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
4917 : }
4918 :
4919 : static JSBool
4920 0 : xml_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
4921 : {
4922 : JSBool found;
4923 0 : if (!HasProperty(cx, obj, IdToJsval(id), &found))
4924 0 : return false;
4925 :
4926 0 : *attrsp = found ? JSPROP_ENUMERATE : 0;
4927 0 : return JS_TRUE;
4928 : }
4929 :
4930 : static JSBool
4931 0 : xml_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
4932 : {
4933 0 : return xml_getGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
4934 : }
4935 :
4936 : static JSBool
4937 0 : xml_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
4938 : {
4939 : jsid id;
4940 0 : if (!IndexToId(cx, index, &id))
4941 0 : return false;
4942 0 : return xml_getGenericAttributes(cx, obj, id, attrsp);
4943 : }
4944 :
4945 : static JSBool
4946 0 : xml_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
4947 : {
4948 0 : return xml_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
4949 : }
4950 :
4951 : static JSBool
4952 0 : xml_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
4953 : {
4954 : JSBool found;
4955 0 : if (!HasProperty(cx, obj, IdToJsval(id), &found))
4956 0 : return false;
4957 :
4958 0 : if (found) {
4959 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4960 0 : JSMSG_CANT_SET_XML_ATTRS);
4961 0 : return false;
4962 : }
4963 0 : return true;
4964 : }
4965 :
4966 : static JSBool
4967 0 : xml_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
4968 : {
4969 0 : return xml_setGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
4970 : }
4971 :
4972 : static JSBool
4973 0 : xml_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
4974 : {
4975 : jsid id;
4976 0 : if (!IndexToId(cx, index, &id))
4977 0 : return false;
4978 0 : return xml_setGenericAttributes(cx, obj, id, attrsp);
4979 : }
4980 :
4981 : static JSBool
4982 0 : xml_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
4983 : {
4984 0 : return xml_setGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
4985 : }
4986 :
4987 : static JSBool
4988 36 : xml_deleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
4989 : {
4990 : uint32_t index;
4991 : JSObject *nameqn;
4992 : jsid funid;
4993 :
4994 36 : Value idval = IdToValue(id);
4995 36 : JSXML *xml = (JSXML *) obj->getPrivate();
4996 36 : if (js_IdIsIndex(id, &index)) {
4997 0 : if (xml->xml_class != JSXML_CLASS_LIST) {
4998 : /* See NOTE in spec: this variation is reserved for future use. */
4999 0 : ReportBadXMLName(cx, IdToValue(id));
5000 0 : return false;
5001 : }
5002 :
5003 : /* ECMA-357 9.2.1.3. */
5004 0 : DeleteListElement(cx, xml, index);
5005 : } else {
5006 36 : nameqn = ToXMLName(cx, idval, &funid);
5007 36 : if (!nameqn)
5008 0 : return false;
5009 36 : if (!JSID_IS_VOID(funid))
5010 0 : return js_DeleteGeneric(cx, obj, funid, rval, false);
5011 :
5012 : DeleteNamedProperty(cx, xml, nameqn,
5013 36 : nameqn->getClass() == &AttributeNameClass);
5014 : }
5015 :
5016 : /*
5017 : * If this object has its own (mutable) scope, then we may have added a
5018 : * property to the scope in xml_lookupGeneric for it to return to mean
5019 : * "found" and to provide a handle for access operations to call the
5020 : * property's getter or setter. But now it's time to remove any such
5021 : * property, to purge the property cache and remove the scope entry.
5022 : */
5023 36 : if (!obj->nativeEmpty() && !js_DeleteGeneric(cx, obj, id, rval, false))
5024 0 : return false;
5025 :
5026 36 : rval->setBoolean(true);
5027 36 : return true;
5028 : }
5029 :
5030 : static JSBool
5031 36 : xml_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
5032 : {
5033 36 : return xml_deleteGeneric(cx, obj, ATOM_TO_JSID(name), rval, strict);
5034 : }
5035 :
5036 : static JSBool
5037 9 : xml_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
5038 : {
5039 9 : JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate());
5040 9 : if (xml->xml_class != JSXML_CLASS_LIST) {
5041 : /* See NOTE in spec: this variation is reserved for future use. */
5042 9 : ReportBadXMLName(cx, DoubleValue(index));
5043 9 : return false;
5044 : }
5045 :
5046 : /* ECMA-357 9.2.1.3. */
5047 0 : DeleteListElement(cx, xml, index);
5048 :
5049 : /*
5050 : * If this object has its own (mutable) scope, then we may have added a
5051 : * property to the scope in xml_lookupGeneric for it to return to mean
5052 : * "found" and to provide a handle for access operations to call the
5053 : * property's getter or setter. But now it's time to remove any such
5054 : * property, to purge the property cache and remove the scope entry.
5055 : */
5056 0 : if (!obj->nativeEmpty() && !js_DeleteElement(cx, obj, index, rval, false))
5057 0 : return false;
5058 :
5059 0 : rval->setBoolean(true);
5060 0 : return true;
5061 : }
5062 :
5063 : static JSBool
5064 0 : xml_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
5065 : {
5066 0 : return xml_deleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict);
5067 : }
5068 :
5069 : static JSString *
5070 : xml_toString_helper(JSContext *cx, JSXML *xml);
5071 :
5072 : JSBool
5073 162 : xml_convert(JSContext *cx, JSObject *obj, JSType hint, Value *rval)
5074 : {
5075 162 : JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
5076 162 : JS_ASSERT(obj->isXML());
5077 :
5078 324 : JS::Anchor<JSObject *> anch(obj);
5079 162 : JSString *str = xml_toString_helper(cx, reinterpret_cast<JSXML *>(obj->getPrivate()));
5080 162 : if (!str)
5081 0 : return false;
5082 162 : *rval = StringValue(str);
5083 162 : return true;
5084 : }
5085 :
5086 : static JSBool
5087 342 : xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5088 : {
5089 : JSXML *xml;
5090 : uint32_t length, index;
5091 : JSXMLArrayCursor<JSXML> *cursor;
5092 :
5093 342 : xml = (JSXML *)obj->getPrivate();
5094 342 : length = JSXML_LENGTH(xml);
5095 :
5096 342 : switch (enum_op) {
5097 : case JSENUMERATE_INIT:
5098 : case JSENUMERATE_INIT_ALL:
5099 126 : if (length == 0) {
5100 36 : statep->setInt32(0);
5101 : } else {
5102 90 : cursor = cx->new_< JSXMLArrayCursor<JSXML> >(&xml->xml_kids);
5103 90 : if (!cursor)
5104 0 : return JS_FALSE;
5105 90 : statep->setPrivate(cursor);
5106 : }
5107 126 : if (idp)
5108 0 : *idp = INT_TO_JSID(length);
5109 126 : break;
5110 :
5111 : case JSENUMERATE_NEXT:
5112 216 : if (statep->isInt32(0)) {
5113 36 : statep->setNull();
5114 36 : break;
5115 : }
5116 180 : cursor = (JSXMLArrayCursor<JSXML> *) statep->toPrivate();
5117 180 : if (cursor && cursor->array && (index = cursor->index) < length) {
5118 90 : *idp = INT_TO_JSID(index);
5119 90 : cursor->index = index + 1;
5120 90 : break;
5121 : }
5122 : /* FALL THROUGH */
5123 :
5124 : case JSENUMERATE_DESTROY:
5125 90 : if (!statep->isInt32(0)) {
5126 90 : cursor = (JSXMLArrayCursor<JSXML> *) statep->toPrivate();
5127 90 : if (cursor)
5128 90 : cx->delete_(cursor);
5129 : }
5130 90 : statep->setNull();
5131 90 : break;
5132 : }
5133 342 : return JS_TRUE;
5134 : }
5135 :
5136 : static JSType
5137 0 : xml_typeOf(JSContext *cx, JSObject *obj)
5138 : {
5139 0 : return JSTYPE_XML;
5140 : }
5141 :
5142 : static JSBool
5143 0 : xml_hasInstance(JSContext *cx, JSObject *obj, const Value *, JSBool *bp)
5144 : {
5145 0 : return JS_TRUE;
5146 : }
5147 :
5148 : static void
5149 178 : xml_trace(JSTracer *trc, JSObject *obj)
5150 : {
5151 178 : JSXML *xml = (JSXML *) obj->getPrivate();
5152 : /*
5153 : * This is safe to leave Unbarriered for incremental GC, but we'll need
5154 : * to fix somehow for generational.
5155 : */
5156 178 : if (xml) {
5157 178 : MarkXMLUnbarriered(trc, &xml, "private");
5158 178 : JS_ASSERT(xml == obj->getPrivate());
5159 : }
5160 178 : }
5161 :
5162 : static JSBool
5163 0 : xml_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
5164 : {
5165 0 : JS_ASSERT(obj->isExtensible());
5166 0 : *success = false;
5167 0 : return true;
5168 : }
5169 :
5170 : static void
5171 0 : xml_clear(JSContext *cx, JSObject *obj)
5172 : {
5173 0 : }
5174 :
5175 : static JSBool
5176 279 : HasSimpleContent(JSXML *xml)
5177 : {
5178 : JSXML *kid;
5179 : JSBool simple;
5180 : uint32_t i, n;
5181 :
5182 : again:
5183 279 : switch (xml->xml_class) {
5184 : case JSXML_CLASS_COMMENT:
5185 : case JSXML_CLASS_PROCESSING_INSTRUCTION:
5186 0 : return JS_FALSE;
5187 : case JSXML_CLASS_LIST:
5188 108 : if (xml->xml_kids.length == 0)
5189 90 : return JS_TRUE;
5190 18 : if (xml->xml_kids.length == 1) {
5191 18 : kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5192 18 : if (kid) {
5193 18 : xml = kid;
5194 18 : goto again;
5195 : }
5196 : }
5197 : /* FALL THROUGH */
5198 : default:
5199 171 : simple = JS_TRUE;
5200 279 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5201 162 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5202 162 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5203 54 : simple = JS_FALSE;
5204 54 : break;
5205 : }
5206 : }
5207 171 : return simple;
5208 : }
5209 : }
5210 :
5211 : /*
5212 : * 11.2.2.1 Step 3(d) onward.
5213 : */
5214 : JSBool
5215 720 : js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5216 : {
5217 720 : JS_ASSERT(obj->isXML());
5218 :
5219 720 : if (JSID_IS_OBJECT(id))
5220 0 : js_GetLocalNameFromFunctionQName(JSID_TO_OBJECT(id), &id, cx);
5221 :
5222 : /*
5223 : * As our callers have a bad habit of passing a pointer to an unrooted
5224 : * local value as vp, we use a proper root here.
5225 : */
5226 1440 : AutoValueRooter tvr(cx);
5227 720 : JSBool ok = GetXMLFunction(cx, obj, id, tvr.addr());
5228 720 : *vp = tvr.value();
5229 720 : return ok;
5230 : }
5231 :
5232 : JSBool
5233 18 : js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp)
5234 : {
5235 : JSXML *xml, *vxml;
5236 : JSObject *vobj;
5237 : JSBool ok;
5238 : JSString *str, *vstr;
5239 : double d, d2;
5240 :
5241 : JSObject *obj;
5242 : jsval v;
5243 18 : if (v1.isObject() && v1.toObject().isXML()) {
5244 18 : obj = &v1.toObject();
5245 18 : v = v2;
5246 : } else {
5247 0 : v = v1;
5248 0 : obj = &v2.toObject();
5249 : }
5250 :
5251 18 : JS_ASSERT(obj->isXML());
5252 :
5253 18 : xml = (JSXML *) obj->getPrivate();
5254 18 : vxml = NULL;
5255 18 : if (!JSVAL_IS_PRIMITIVE(v)) {
5256 0 : vobj = JSVAL_TO_OBJECT(v);
5257 0 : if (vobj->isXML())
5258 0 : vxml = (JSXML *) vobj->getPrivate();
5259 : }
5260 :
5261 18 : if (xml->xml_class == JSXML_CLASS_LIST) {
5262 9 : ok = Equals(cx, xml, v, bp);
5263 9 : } else if (vxml) {
5264 0 : if (vxml->xml_class == JSXML_CLASS_LIST) {
5265 0 : ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp);
5266 : } else {
5267 0 : if (((xml->xml_class == JSXML_CLASS_TEXT ||
5268 : xml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5269 0 : HasSimpleContent(vxml)) ||
5270 : ((vxml->xml_class == JSXML_CLASS_TEXT ||
5271 : vxml->xml_class == JSXML_CLASS_ATTRIBUTE) &&
5272 0 : HasSimpleContent(xml))) {
5273 0 : ok = js_EnterLocalRootScope(cx);
5274 0 : if (ok) {
5275 0 : ok = (str = ToStringSlow(cx, ObjectValue(*obj))) &&
5276 0 : (vstr = ToString(cx, v));
5277 0 : if (ok) {
5278 : bool equal;
5279 0 : ok = EqualStrings(cx, str, vstr, &equal);
5280 0 : *bp = equal;
5281 : }
5282 0 : js_LeaveLocalRootScope(cx);
5283 : }
5284 : } else {
5285 0 : ok = XMLEquals(cx, xml, vxml, bp);
5286 : }
5287 : }
5288 : } else {
5289 9 : ok = js_EnterLocalRootScope(cx);
5290 9 : if (ok) {
5291 9 : if (HasSimpleContent(xml)) {
5292 9 : ok = (str = ToString(cx, ObjectValue(*obj))) &&
5293 9 : (vstr = ToString(cx, v));
5294 9 : if (ok) {
5295 : bool equal;
5296 9 : ok = EqualStrings(cx, str, vstr, &equal);
5297 9 : *bp = equal;
5298 : }
5299 0 : } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) {
5300 0 : str = ToString(cx, ObjectValue(*obj));
5301 0 : if (!str) {
5302 0 : ok = JS_FALSE;
5303 0 : } else if (JSVAL_IS_STRING(v)) {
5304 : bool equal;
5305 0 : ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), &equal);
5306 0 : if (ok)
5307 0 : *bp = equal;
5308 : } else {
5309 0 : ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
5310 0 : if (ok) {
5311 0 : d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
5312 0 : : JSVAL_TO_DOUBLE(v);
5313 0 : *bp = (d == d2);
5314 : }
5315 : }
5316 : } else {
5317 0 : *bp = JS_FALSE;
5318 : }
5319 9 : js_LeaveLocalRootScope(cx);
5320 : }
5321 : }
5322 18 : return ok;
5323 : }
5324 :
5325 : JSBool
5326 0 : js_ConcatenateXML(JSContext *cx, JSObject *obj, JSObject *robj, Value *vp)
5327 : {
5328 : JSBool ok;
5329 : JSObject *listobj;
5330 : JSXML *list, *lxml, *rxml;
5331 :
5332 0 : JS_ASSERT(obj->isXML());
5333 0 : ok = js_EnterLocalRootScope(cx);
5334 0 : if (!ok)
5335 0 : return JS_FALSE;
5336 :
5337 0 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5338 0 : if (!listobj) {
5339 0 : ok = JS_FALSE;
5340 0 : goto out;
5341 : }
5342 :
5343 0 : list = (JSXML *) listobj->getPrivate();
5344 0 : lxml = (JSXML *) obj->getPrivate();
5345 0 : ok = Append(cx, list, lxml);
5346 0 : if (!ok)
5347 0 : goto out;
5348 :
5349 0 : JS_ASSERT(robj->isXML());
5350 0 : rxml = (JSXML *) robj->getPrivate();
5351 0 : ok = Append(cx, list, rxml);
5352 0 : if (!ok)
5353 0 : goto out;
5354 :
5355 0 : vp->setObject(*listobj);
5356 : out:
5357 0 : js_LeaveLocalRootScopeWithResult(cx, *vp);
5358 0 : return ok;
5359 : }
5360 :
5361 : JS_FRIEND_DATA(Class) js::XMLClass = {
5362 : js_XML_str,
5363 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
5364 : JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
5365 : JS_PropertyStub, /* addProperty */
5366 : JS_PropertyStub, /* delProperty */
5367 : JS_PropertyStub, /* getProperty */
5368 : JS_StrictPropertyStub, /* setProperty */
5369 : JS_EnumerateStub,
5370 : JS_ResolveStub,
5371 : xml_convert,
5372 : NULL, /* finalize */
5373 : NULL, /* checkAccess */
5374 : NULL, /* call */
5375 : NULL, /* construct */
5376 : xml_hasInstance,
5377 : xml_trace,
5378 : JS_NULL_CLASS_EXT,
5379 : {
5380 : xml_lookupGeneric,
5381 : xml_lookupProperty,
5382 : xml_lookupElement,
5383 : xml_lookupSpecial,
5384 : xml_defineGeneric,
5385 : xml_defineProperty,
5386 : xml_defineElement,
5387 : xml_defineSpecial,
5388 : xml_getGeneric,
5389 : xml_getProperty,
5390 : xml_getElement,
5391 : NULL, /* getElementIfPresent */
5392 : xml_getSpecial,
5393 : xml_setGeneric,
5394 : xml_setProperty,
5395 : xml_setElement,
5396 : xml_setSpecial,
5397 : xml_getGenericAttributes,
5398 : xml_getPropertyAttributes,
5399 : xml_getElementAttributes,
5400 : xml_getSpecialAttributes,
5401 : xml_setGenericAttributes,
5402 : xml_setPropertyAttributes,
5403 : xml_setElementAttributes,
5404 : xml_setSpecialAttributes,
5405 : xml_deleteProperty,
5406 : xml_deleteElement,
5407 : xml_deleteSpecial,
5408 : xml_enumerate,
5409 : xml_typeOf,
5410 : xml_fix,
5411 : NULL, /* thisObject */
5412 : xml_clear
5413 : }
5414 : };
5415 :
5416 : static JSXML *
5417 0 : StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
5418 : {
5419 : JSXML *xml;
5420 : JSFunction *fun;
5421 : char numBuf[12];
5422 :
5423 0 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5424 0 : JS_ASSERT(JSVAL_TO_OBJECT(*vp)->isFunction());
5425 :
5426 0 : *objp = ToObject(cx, &vp[1]);
5427 0 : if (!*objp)
5428 0 : return NULL;
5429 0 : if (!(*objp)->isXML()) {
5430 0 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass);
5431 0 : return NULL;
5432 : }
5433 0 : xml = (JSXML *) (*objp)->getPrivate();
5434 0 : if (!xml || xml->xml_class != JSXML_CLASS_LIST)
5435 0 : return xml;
5436 :
5437 0 : if (xml->xml_kids.length == 1) {
5438 0 : xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5439 0 : if (xml) {
5440 0 : *objp = js_GetXMLObject(cx, xml);
5441 0 : if (!*objp)
5442 0 : return NULL;
5443 0 : vp[1] = OBJECT_TO_JSVAL(*objp);
5444 0 : return xml;
5445 : }
5446 : }
5447 :
5448 0 : fun = JSVAL_TO_OBJECT(*vp)->toFunction();
5449 0 : JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length);
5450 0 : JSAutoByteString funNameBytes;
5451 0 : if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
5452 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_LIST_XML_METHOD,
5453 0 : funName, numBuf);
5454 : }
5455 0 : return NULL;
5456 : }
5457 :
5458 : /* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */
5459 : #define XML_METHOD_PROLOG \
5460 : JSObject *obj = ToObject(cx, &vp[1]); \
5461 : if (!obj) \
5462 : return JS_FALSE; \
5463 : if (!obj->isXML()) { \
5464 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass); \
5465 : return JS_FALSE; \
5466 : } \
5467 : JSXML *xml = (JSXML *)obj->getPrivate(); \
5468 : if (!xml) \
5469 : return JS_FALSE
5470 :
5471 : #define NON_LIST_XML_METHOD_PROLOG \
5472 : JSObject *obj; \
5473 : JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \
5474 : if (!xml) \
5475 : return JS_FALSE; \
5476 : JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
5477 :
5478 : static JSBool
5479 0 : xml_addNamespace(JSContext *cx, unsigned argc, jsval *vp)
5480 : {
5481 : JSObject *ns;
5482 :
5483 0 : NON_LIST_XML_METHOD_PROLOG;
5484 0 : if (xml->xml_class != JSXML_CLASS_ELEMENT)
5485 0 : goto done;
5486 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5487 0 : if (!xml)
5488 0 : return JS_FALSE;
5489 :
5490 0 : if (!NamespaceHelper(cx, argc == 0 ? -1 : 1, vp + 2, vp))
5491 0 : return JS_FALSE;
5492 0 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
5493 :
5494 0 : ns = JSVAL_TO_OBJECT(*vp);
5495 0 : if (!AddInScopeNamespace(cx, xml, ns))
5496 0 : return JS_FALSE;
5497 0 : ns->setNamespaceDeclared(JSVAL_TRUE);
5498 :
5499 : done:
5500 0 : *vp = OBJECT_TO_JSVAL(obj);
5501 0 : return JS_TRUE;
5502 : }
5503 :
5504 : static JSBool
5505 0 : xml_appendChild(JSContext *cx, unsigned argc, jsval *vp)
5506 : {
5507 : jsval v;
5508 : JSObject *vobj;
5509 : JSXML *vxml;
5510 :
5511 0 : NON_LIST_XML_METHOD_PROLOG;
5512 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
5513 0 : if (!xml)
5514 0 : return JS_FALSE;
5515 :
5516 : jsid name;
5517 0 : if (!js_GetAnyName(cx, &name))
5518 0 : return JS_FALSE;
5519 :
5520 0 : if (!GetProperty(cx, obj, name, &v))
5521 0 : return JS_FALSE;
5522 :
5523 0 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5524 0 : vobj = JSVAL_TO_OBJECT(v);
5525 0 : JS_ASSERT(vobj->isXML());
5526 0 : vxml = (JSXML *) vobj->getPrivate();
5527 0 : JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST);
5528 :
5529 0 : if (!IndexToId(cx, vxml->xml_kids.length, &name))
5530 0 : return JS_FALSE;
5531 0 : *vp = (argc != 0) ? vp[2] : JSVAL_VOID;
5532 :
5533 0 : if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, false, vp))
5534 0 : return JS_FALSE;
5535 :
5536 0 : *vp = OBJECT_TO_JSVAL(obj);
5537 0 : return JS_TRUE;
5538 : }
5539 :
5540 : /* XML and XMLList */
5541 : static JSBool
5542 0 : xml_attribute(JSContext *cx, unsigned argc, jsval *vp)
5543 : {
5544 : JSObject *qn;
5545 :
5546 0 : if (argc == 0) {
5547 0 : js_ReportMissingArg(cx, *vp, 0);
5548 0 : return JS_FALSE;
5549 : }
5550 :
5551 0 : qn = ToAttributeName(cx, vp[2]);
5552 0 : if (!qn)
5553 0 : return JS_FALSE;
5554 0 : vp[2] = OBJECT_TO_JSVAL(qn); /* local root */
5555 :
5556 0 : jsid id = OBJECT_TO_JSID(qn);
5557 0 : JSObject *obj = ToObject(cx, &vp[1]);
5558 0 : if (!obj)
5559 0 : return JS_FALSE;
5560 0 : return GetProperty(cx, obj, id, vp);
5561 : }
5562 :
5563 : /* XML and XMLList */
5564 : static JSBool
5565 0 : xml_attributes(JSContext *cx, unsigned argc, jsval *vp)
5566 : {
5567 0 : jsval name = STRING_TO_JSVAL(cx->runtime->atomState.starAtom);
5568 0 : JSObject *qn = ToAttributeName(cx, name);
5569 0 : if (!qn)
5570 0 : return JS_FALSE;
5571 :
5572 0 : jsid id = OBJECT_TO_JSID(qn);
5573 0 : JSObject *obj = ToObject(cx, &vp[1]);
5574 0 : if (!obj)
5575 0 : return JS_FALSE;
5576 0 : return GetProperty(cx, obj, id, vp);
5577 : }
5578 :
5579 : static JSXML *
5580 621 : xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval)
5581 : {
5582 : JSObject *listobj;
5583 : JSXML *list;
5584 :
5585 621 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
5586 621 : if (!listobj)
5587 0 : return NULL;
5588 :
5589 621 : *rval = OBJECT_TO_JSVAL(listobj);
5590 621 : list = (JSXML *) listobj->getPrivate();
5591 621 : list->xml_target = xml;
5592 621 : return list;
5593 : }
5594 :
5595 : static JSBool
5596 0 : ValueToId(JSContext *cx, jsval v, AutoIdRooter *idr)
5597 : {
5598 0 : if (JSVAL_IS_INT(v)) {
5599 0 : int32_t i = JSVAL_TO_INT(v);
5600 0 : if (INT_FITS_IN_JSID(i))
5601 0 : *idr->addr() = INT_TO_JSID(i);
5602 0 : else if (!js_ValueToStringId(cx, v, idr->addr()))
5603 0 : return JS_FALSE;
5604 0 : } else if (JSVAL_IS_STRING(v)) {
5605 0 : JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v));
5606 0 : if (!atom)
5607 0 : return JS_FALSE;
5608 0 : *idr->addr() = ATOM_TO_JSID(atom);
5609 0 : } else if (!JSVAL_IS_PRIMITIVE(v)) {
5610 0 : *idr->addr() = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v));
5611 : } else {
5612 0 : ReportBadXMLName(cx, v);
5613 0 : return JS_FALSE;
5614 : }
5615 0 : return JS_TRUE;
5616 : }
5617 :
5618 : static JSBool
5619 0 : xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name,
5620 : jsval *rval)
5621 : {
5622 : bool isIndex;
5623 : uint32_t index;
5624 : JSXML *kid;
5625 : JSObject *kidobj;
5626 :
5627 : /* ECMA-357 13.4.4.6 */
5628 0 : JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
5629 :
5630 0 : if (!IdValIsIndex(cx, name, &index, &isIndex))
5631 0 : return JS_FALSE;
5632 :
5633 0 : if (isIndex) {
5634 0 : if (index >= JSXML_LENGTH(xml)) {
5635 0 : *rval = JSVAL_VOID;
5636 : } else {
5637 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
5638 0 : if (!kid) {
5639 0 : *rval = JSVAL_VOID;
5640 : } else {
5641 0 : kidobj = js_GetXMLObject(cx, kid);
5642 0 : if (!kidobj)
5643 0 : return JS_FALSE;
5644 0 : *rval = OBJECT_TO_JSVAL(kidobj);
5645 : }
5646 : }
5647 0 : return JS_TRUE;
5648 : }
5649 :
5650 0 : AutoIdRooter idr(cx);
5651 0 : if (!ValueToId(cx, name, &idr))
5652 0 : return JS_FALSE;
5653 :
5654 0 : return GetProperty(cx, obj, idr.id(), rval);
5655 : }
5656 :
5657 : /* XML and XMLList */
5658 : static JSBool
5659 0 : xml_child(JSContext *cx, unsigned argc, jsval *vp)
5660 : {
5661 : jsval v;
5662 : JSXML *list, *vxml;
5663 : JSObject *kidobj;
5664 :
5665 0 : XML_METHOD_PROLOG;
5666 0 : jsval name = argc != 0 ? vp[2] : JSVAL_VOID;
5667 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
5668 : /* ECMA-357 13.5.4.4 */
5669 0 : list = xml_list_helper(cx, xml, vp);
5670 0 : if (!list)
5671 0 : return JS_FALSE;
5672 :
5673 0 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
5674 0 : while (JSXML *kid = cursor.getNext()) {
5675 0 : kidobj = js_GetXMLObject(cx, kid);
5676 0 : if (!kidobj)
5677 0 : return JS_FALSE;
5678 0 : if (!xml_child_helper(cx, kidobj, kid, name, &v))
5679 0 : return JS_FALSE;
5680 0 : if (JSVAL_IS_VOID(v)) {
5681 : /* The property didn't exist in this kid. */
5682 0 : continue;
5683 : }
5684 :
5685 0 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
5686 0 : vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5687 0 : if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) &&
5688 0 : !Append(cx, list, vxml)) {
5689 0 : return JS_FALSE;
5690 : }
5691 : }
5692 0 : return JS_TRUE;
5693 : }
5694 :
5695 : /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */
5696 0 : if (!xml_child_helper(cx, obj, xml, name, vp))
5697 0 : return JS_FALSE;
5698 0 : if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp))
5699 0 : return JS_FALSE;
5700 0 : return JS_TRUE;
5701 : }
5702 :
5703 : static JSBool
5704 0 : xml_childIndex(JSContext *cx, unsigned argc, jsval *vp)
5705 : {
5706 : JSXML *parent;
5707 : uint32_t i, n;
5708 :
5709 0 : NON_LIST_XML_METHOD_PROLOG;
5710 0 : parent = xml->parent;
5711 0 : if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) {
5712 0 : *vp = DOUBLE_TO_JSVAL(js_NaN);
5713 0 : return JS_TRUE;
5714 : }
5715 0 : for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) {
5716 0 : if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml)
5717 0 : break;
5718 : }
5719 0 : JS_ASSERT(i < n);
5720 0 : if (i <= JSVAL_INT_MAX)
5721 0 : *vp = INT_TO_JSVAL(i);
5722 : else
5723 0 : *vp = DOUBLE_TO_JSVAL(i);
5724 0 : return JS_TRUE;
5725 : }
5726 :
5727 : /* XML and XMLList */
5728 : static JSBool
5729 0 : xml_children(JSContext *cx, unsigned argc, jsval *vp)
5730 : {
5731 0 : JSObject *obj = ToObject(cx, &vp[1]);
5732 0 : if (!obj)
5733 0 : return false;
5734 0 : jsid name = ATOM_TO_JSID(cx->runtime->atomState.starAtom);
5735 0 : return GetProperty(cx, obj, name, vp);
5736 : }
5737 :
5738 : /* XML and XMLList */
5739 : static JSBool
5740 0 : xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
5741 : {
5742 : JSXML *list, *kid, *vxml;
5743 : JSBool ok;
5744 : uint32_t i, n;
5745 : JSObject *kidobj;
5746 : jsval v;
5747 :
5748 0 : list = xml_list_helper(cx, xml, vp);
5749 0 : if (!list)
5750 0 : return JS_FALSE;
5751 :
5752 0 : ok = JS_TRUE;
5753 :
5754 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
5755 : /* 13.5.4.6 Step 2. */
5756 0 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5757 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5758 0 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
5759 0 : ok = js_EnterLocalRootScope(cx);
5760 0 : if (!ok)
5761 0 : break;
5762 0 : kidobj = js_GetXMLObject(cx, kid);
5763 0 : if (kidobj) {
5764 0 : ok = xml_comments_helper(cx, kidobj, kid, &v);
5765 : } else {
5766 0 : ok = JS_FALSE;
5767 0 : v = JSVAL_NULL;
5768 : }
5769 0 : js_LeaveLocalRootScopeWithResult(cx, v);
5770 0 : if (!ok)
5771 0 : break;
5772 0 : vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5773 0 : if (JSXML_LENGTH(vxml) != 0) {
5774 0 : ok = Append(cx, list, vxml);
5775 0 : if (!ok)
5776 0 : break;
5777 : }
5778 : }
5779 : }
5780 : } else {
5781 : /* 13.4.4.9 Step 2. */
5782 0 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5783 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5784 0 : if (kid && kid->xml_class == JSXML_CLASS_COMMENT) {
5785 0 : ok = Append(cx, list, kid);
5786 0 : if (!ok)
5787 0 : break;
5788 : }
5789 : }
5790 : }
5791 :
5792 0 : return ok;
5793 : }
5794 :
5795 : static JSBool
5796 0 : xml_comments(JSContext *cx, unsigned argc, jsval *vp)
5797 : {
5798 0 : XML_METHOD_PROLOG;
5799 0 : return xml_comments_helper(cx, obj, xml, vp);
5800 : }
5801 :
5802 : /* XML and XMLList */
5803 : static JSBool
5804 0 : xml_contains(JSContext *cx, unsigned argc, jsval *vp)
5805 : {
5806 : jsval value;
5807 : JSBool eq;
5808 : JSObject *kidobj;
5809 :
5810 0 : XML_METHOD_PROLOG;
5811 0 : value = argc != 0 ? vp[2] : JSVAL_VOID;
5812 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
5813 0 : eq = JS_FALSE;
5814 0 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
5815 0 : while (JSXML *kid = cursor.getNext()) {
5816 0 : kidobj = js_GetXMLObject(cx, kid);
5817 0 : if (!kidobj || !js_TestXMLEquality(cx, ObjectValue(*kidobj), value, &eq))
5818 0 : return JS_FALSE;
5819 0 : if (eq)
5820 0 : break;
5821 : }
5822 : } else {
5823 0 : if (!js_TestXMLEquality(cx, ObjectValue(*obj), value, &eq))
5824 0 : return JS_FALSE;
5825 : }
5826 0 : *vp = BOOLEAN_TO_JSVAL(eq);
5827 0 : return JS_TRUE;
5828 : }
5829 :
5830 : /* XML and XMLList */
5831 : static JSBool
5832 0 : xml_copy(JSContext *cx, unsigned argc, jsval *vp)
5833 : {
5834 : JSXML *copy;
5835 :
5836 0 : XML_METHOD_PROLOG;
5837 0 : copy = DeepCopy(cx, xml, NULL, 0);
5838 0 : if (!copy)
5839 0 : return JS_FALSE;
5840 0 : *vp = OBJECT_TO_JSVAL(copy->object);
5841 0 : return JS_TRUE;
5842 : }
5843 :
5844 : /* XML and XMLList */
5845 : static JSBool
5846 0 : xml_descendants(JSContext *cx, unsigned argc, jsval *vp)
5847 : {
5848 : jsval name;
5849 : JSXML *list;
5850 :
5851 0 : XML_METHOD_PROLOG;
5852 0 : name = argc == 0 ? STRING_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5853 0 : list = Descendants(cx, xml, name);
5854 0 : if (!list)
5855 0 : return JS_FALSE;
5856 0 : *vp = OBJECT_TO_JSVAL(list->object);
5857 0 : return JS_TRUE;
5858 : }
5859 :
5860 : /* XML and XMLList */
5861 : static JSBool
5862 621 : xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml,
5863 : JSObject *nameqn, jsval *vp)
5864 : {
5865 : JSXML *list, *vxml;
5866 : jsval v;
5867 : JSBool ok;
5868 : JSObject *kidobj;
5869 : uint32_t i, n;
5870 :
5871 621 : list = xml_list_helper(cx, xml, vp);
5872 621 : if (!list)
5873 0 : return JS_FALSE;
5874 :
5875 621 : list->xml_targetprop = nameqn;
5876 621 : ok = JS_TRUE;
5877 :
5878 621 : if (xml->xml_class == JSXML_CLASS_LIST) {
5879 : /* 13.5.4.6 */
5880 0 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
5881 0 : while (JSXML *kid = cursor.getNext()) {
5882 0 : if (kid->xml_class == JSXML_CLASS_ELEMENT) {
5883 0 : ok = js_EnterLocalRootScope(cx);
5884 0 : if (!ok)
5885 0 : break;
5886 0 : kidobj = js_GetXMLObject(cx, kid);
5887 0 : if (kidobj) {
5888 0 : ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v);
5889 : } else {
5890 0 : ok = JS_FALSE;
5891 0 : v = JSVAL_NULL;
5892 : }
5893 0 : js_LeaveLocalRootScopeWithResult(cx, v);
5894 0 : if (!ok)
5895 0 : break;
5896 0 : vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
5897 0 : if (JSXML_LENGTH(vxml) != 0) {
5898 0 : ok = Append(cx, list, vxml);
5899 0 : if (!ok)
5900 0 : break;
5901 : }
5902 : }
5903 : }
5904 : } else {
5905 621 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
5906 0 : JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
5907 0 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT &&
5908 0 : MatchElemName(nameqn, kid)) {
5909 0 : ok = Append(cx, list, kid);
5910 0 : if (!ok)
5911 0 : break;
5912 : }
5913 : }
5914 : }
5915 :
5916 621 : return ok;
5917 : }
5918 :
5919 : static JSBool
5920 621 : xml_elements(JSContext *cx, unsigned argc, jsval *vp)
5921 : {
5922 : jsval name;
5923 : JSObject *nameqn;
5924 : jsid funid;
5925 :
5926 621 : XML_METHOD_PROLOG;
5927 :
5928 621 : name = (argc == 0) ? STRING_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
5929 621 : nameqn = ToXMLName(cx, name, &funid);
5930 621 : if (!nameqn)
5931 0 : return JS_FALSE;
5932 :
5933 621 : if (!JSID_IS_VOID(funid))
5934 0 : return xml_list_helper(cx, xml, vp) != NULL;
5935 :
5936 621 : return xml_elements_helper(cx, obj, xml, nameqn, vp);
5937 : }
5938 :
5939 : /* XML and XMLList */
5940 : static JSBool
5941 0 : xml_hasOwnProperty(JSContext *cx, unsigned argc, jsval *vp)
5942 : {
5943 : jsval name;
5944 : JSBool found;
5945 :
5946 0 : JSObject *obj = ToObject(cx, &vp[1]);
5947 0 : if (!obj)
5948 0 : return JS_FALSE;
5949 0 : if (!obj->isXML()) {
5950 0 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &XMLClass);
5951 0 : return JS_FALSE;
5952 : }
5953 :
5954 0 : name = argc != 0 ? vp[2] : JSVAL_VOID;
5955 0 : if (!HasProperty(cx, obj, name, &found))
5956 0 : return JS_FALSE;
5957 0 : if (found) {
5958 0 : *vp = JSVAL_TRUE;
5959 0 : return JS_TRUE;
5960 : }
5961 0 : return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, vp);
5962 : }
5963 :
5964 : /* XML and XMLList */
5965 : static JSBool
5966 0 : xml_hasComplexContent(JSContext *cx, unsigned argc, jsval *vp)
5967 : {
5968 : JSXML *kid;
5969 : JSObject *kidobj;
5970 : uint32_t i, n;
5971 :
5972 0 : XML_METHOD_PROLOG;
5973 : again:
5974 0 : switch (xml->xml_class) {
5975 : case JSXML_CLASS_ATTRIBUTE:
5976 : case JSXML_CLASS_COMMENT:
5977 : case JSXML_CLASS_PROCESSING_INSTRUCTION:
5978 : case JSXML_CLASS_TEXT:
5979 0 : *vp = JSVAL_FALSE;
5980 0 : break;
5981 : case JSXML_CLASS_LIST:
5982 0 : if (xml->xml_kids.length == 0) {
5983 0 : *vp = JSVAL_TRUE;
5984 0 : } else if (xml->xml_kids.length == 1) {
5985 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
5986 0 : if (kid) {
5987 0 : kidobj = js_GetXMLObject(cx, kid);
5988 0 : if (!kidobj)
5989 0 : return JS_FALSE;
5990 0 : obj = kidobj;
5991 0 : xml = (JSXML *) obj->getPrivate();
5992 0 : goto again;
5993 : }
5994 : }
5995 : /* FALL THROUGH */
5996 : default:
5997 0 : *vp = JSVAL_FALSE;
5998 0 : for (i = 0, n = xml->xml_kids.length; i < n; i++) {
5999 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6000 0 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6001 0 : *vp = JSVAL_TRUE;
6002 0 : break;
6003 : }
6004 : }
6005 0 : break;
6006 : }
6007 0 : return JS_TRUE;
6008 : }
6009 :
6010 : /* XML and XMLList */
6011 : static JSBool
6012 0 : xml_hasSimpleContent(JSContext *cx, unsigned argc, jsval *vp)
6013 : {
6014 0 : XML_METHOD_PROLOG;
6015 0 : *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml));
6016 0 : return JS_TRUE;
6017 : }
6018 :
6019 : static JSBool
6020 0 : FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray<JSObject> *nsarray)
6021 : {
6022 : uint32_t length, i, j, n;
6023 : JSObject *ns, *ns2;
6024 : JSLinearString *prefix, *prefix2;
6025 :
6026 0 : length = nsarray->length;
6027 0 : do {
6028 0 : if (xml->xml_class != JSXML_CLASS_ELEMENT)
6029 0 : continue;
6030 0 : for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6031 0 : ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
6032 0 : if (!ns)
6033 0 : continue;
6034 :
6035 0 : prefix = ns->getNamePrefix();
6036 0 : for (j = 0; j < length; j++) {
6037 0 : ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject);
6038 0 : if (ns2) {
6039 0 : prefix2 = ns2->getNamePrefix();
6040 0 : if ((prefix2 && prefix)
6041 : ? EqualStrings(prefix2, prefix)
6042 0 : : EqualStrings(ns2->getNameURI(), ns->getNameURI())) {
6043 0 : break;
6044 : }
6045 : }
6046 : }
6047 :
6048 0 : if (j == length) {
6049 0 : if (!XMLARRAY_APPEND(cx, nsarray, ns))
6050 0 : return JS_FALSE;
6051 0 : ++length;
6052 : }
6053 : }
6054 0 : } while ((xml = xml->parent) != NULL);
6055 0 : JS_ASSERT(length == nsarray->length);
6056 :
6057 0 : return JS_TRUE;
6058 : }
6059 :
6060 : /*
6061 : * Populate a new JS array with elements of array and place the result into
6062 : * rval. rval must point to a rooted location.
6063 : */
6064 : static bool
6065 0 : NamespacesToJSArray(JSContext *cx, JSXMLArray<JSObject> *array, jsval *rval)
6066 : {
6067 0 : JSObject *arrayobj = NewDenseEmptyArray(cx);
6068 0 : if (!arrayobj)
6069 0 : return false;
6070 0 : *rval = OBJECT_TO_JSVAL(arrayobj);
6071 :
6072 0 : AutoValueRooter tvr(cx);
6073 0 : for (uint32_t i = 0, n = array->length; i < n; i++) {
6074 0 : JSObject *ns = XMLARRAY_MEMBER(array, i, JSObject);
6075 0 : if (!ns)
6076 0 : continue;
6077 0 : tvr.set(ObjectValue(*ns));
6078 0 : if (!arrayobj->setElement(cx, i, tvr.addr(), false))
6079 0 : return false;
6080 : }
6081 0 : return true;
6082 : }
6083 :
6084 : static JSBool
6085 0 : xml_inScopeNamespaces(JSContext *cx, unsigned argc, jsval *vp)
6086 : {
6087 0 : NON_LIST_XML_METHOD_PROLOG;
6088 :
6089 0 : AutoNamespaceArray namespaces(cx);
6090 0 : return FindInScopeNamespaces(cx, xml, &namespaces.array) &&
6091 0 : NamespacesToJSArray(cx, &namespaces.array, vp);
6092 : }
6093 :
6094 : static JSBool
6095 0 : xml_insertChildAfter(JSContext *cx, unsigned argc, jsval *vp)
6096 : {
6097 : jsval arg;
6098 : JSXML *kid;
6099 : uint32_t i;
6100 :
6101 0 : NON_LIST_XML_METHOD_PROLOG;
6102 0 : *vp = OBJECT_TO_JSVAL(obj);
6103 0 : if (!JSXML_HAS_KIDS(xml) || argc == 0)
6104 0 : return JS_TRUE;
6105 :
6106 0 : arg = vp[2];
6107 0 : if (JSVAL_IS_NULL(arg)) {
6108 0 : kid = NULL;
6109 0 : i = 0;
6110 : } else {
6111 0 : if (!VALUE_IS_XML(arg))
6112 0 : return JS_TRUE;
6113 0 : kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
6114 0 : i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, pointer_match);
6115 0 : if (i == XML_NOT_FOUND)
6116 0 : return JS_TRUE;
6117 0 : ++i;
6118 : }
6119 :
6120 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6121 0 : if (!xml)
6122 0 : return JS_FALSE;
6123 0 : return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
6124 : }
6125 :
6126 : static JSBool
6127 0 : xml_insertChildBefore(JSContext *cx, unsigned argc, jsval *vp)
6128 : {
6129 : jsval arg;
6130 : JSXML *kid;
6131 : uint32_t i;
6132 :
6133 0 : NON_LIST_XML_METHOD_PROLOG;
6134 0 : *vp = OBJECT_TO_JSVAL(obj);
6135 0 : if (!JSXML_HAS_KIDS(xml) || argc == 0)
6136 0 : return JS_TRUE;
6137 :
6138 0 : arg = vp[2];
6139 0 : if (JSVAL_IS_NULL(arg)) {
6140 0 : kid = NULL;
6141 0 : i = xml->xml_kids.length;
6142 : } else {
6143 0 : if (!VALUE_IS_XML(arg))
6144 0 : return JS_TRUE;
6145 0 : kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate();
6146 0 : i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, pointer_match);
6147 0 : if (i == XML_NOT_FOUND)
6148 0 : return JS_TRUE;
6149 : }
6150 :
6151 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6152 0 : if (!xml)
6153 0 : return JS_FALSE;
6154 0 : return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID);
6155 : }
6156 :
6157 : /* XML and XMLList */
6158 : static JSBool
6159 0 : xml_length(JSContext *cx, unsigned argc, jsval *vp)
6160 : {
6161 0 : XML_METHOD_PROLOG;
6162 0 : if (xml->xml_class != JSXML_CLASS_LIST) {
6163 0 : *vp = JSVAL_ONE;
6164 : } else {
6165 0 : uint32_t l = xml->xml_kids.length;
6166 0 : if (l <= JSVAL_INT_MAX)
6167 0 : *vp = INT_TO_JSVAL(l);
6168 : else
6169 0 : *vp = DOUBLE_TO_JSVAL(l);
6170 : }
6171 0 : return JS_TRUE;
6172 : }
6173 :
6174 : static JSBool
6175 0 : xml_localName(JSContext *cx, unsigned argc, jsval *vp)
6176 : {
6177 0 : NON_LIST_XML_METHOD_PROLOG;
6178 0 : *vp = xml->name ? xml->name->getQNameLocalNameVal() : JSVAL_NULL;
6179 0 : return JS_TRUE;
6180 : }
6181 :
6182 : static JSBool
6183 0 : xml_name(JSContext *cx, unsigned argc, jsval *vp)
6184 : {
6185 0 : NON_LIST_XML_METHOD_PROLOG;
6186 0 : *vp = OBJECT_TO_JSVAL(xml->name);
6187 0 : return JS_TRUE;
6188 : }
6189 :
6190 : static JSBool
6191 0 : xml_namespace(JSContext *cx, unsigned argc, jsval *vp)
6192 : {
6193 : JSLinearString *prefix, *nsprefix;
6194 : uint32_t i, length;
6195 : JSObject *ns;
6196 :
6197 0 : NON_LIST_XML_METHOD_PROLOG;
6198 0 : if (argc == 0 && !JSXML_HAS_NAME(xml)) {
6199 0 : *vp = JSVAL_NULL;
6200 0 : return true;
6201 : }
6202 :
6203 0 : if (argc == 0) {
6204 0 : prefix = NULL;
6205 : } else {
6206 0 : JSString *str = ToString(cx, vp[2]);
6207 0 : if (!str)
6208 0 : return false;
6209 0 : prefix = str->ensureLinear(cx);
6210 0 : if (!prefix)
6211 0 : return false;
6212 0 : vp[2] = STRING_TO_JSVAL(prefix); /* local root */
6213 : }
6214 :
6215 0 : AutoNamespaceArray inScopeNSes(cx);
6216 0 : if (!FindInScopeNamespaces(cx, xml, &inScopeNSes.array))
6217 0 : return false;
6218 :
6219 0 : if (!prefix) {
6220 0 : ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
6221 0 : if (!ns)
6222 0 : return false;
6223 : } else {
6224 0 : ns = NULL;
6225 0 : for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
6226 0 : ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject);
6227 0 : if (ns) {
6228 0 : nsprefix = ns->getNamePrefix();
6229 0 : if (nsprefix && EqualStrings(nsprefix, prefix))
6230 0 : break;
6231 0 : ns = NULL;
6232 : }
6233 : }
6234 : }
6235 :
6236 0 : *vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns);
6237 0 : return true;
6238 : }
6239 :
6240 : static JSBool
6241 0 : xml_namespaceDeclarations(JSContext *cx, unsigned argc, jsval *vp)
6242 : {
6243 0 : NON_LIST_XML_METHOD_PROLOG;
6244 0 : if (JSXML_HAS_VALUE(xml))
6245 0 : return true;
6246 :
6247 0 : AutoNamespaceArray ancestors(cx);
6248 0 : AutoNamespaceArray declared(cx);
6249 :
6250 0 : JSXML *yml = xml;
6251 0 : while ((yml = yml->parent) != NULL) {
6252 0 : JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
6253 0 : for (uint32_t i = 0, n = yml->xml_namespaces.length; i < n; i++) {
6254 0 : JSObject *ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
6255 0 : if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6256 0 : if (!XMLARRAY_APPEND(cx, &ancestors.array, ns))
6257 0 : return false;
6258 : }
6259 : }
6260 : }
6261 :
6262 0 : for (uint32_t i = 0, n = xml->xml_namespaces.length; i < n; i++) {
6263 0 : JSObject *ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
6264 0 : if (!ns)
6265 0 : continue;
6266 0 : if (!IsDeclared(ns))
6267 0 : continue;
6268 0 : if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
6269 0 : if (!XMLARRAY_APPEND(cx, &declared.array, ns))
6270 0 : return false;
6271 : }
6272 : }
6273 :
6274 0 : return NamespacesToJSArray(cx, &declared.array, vp);
6275 : }
6276 :
6277 : static const char js_attribute_str[] = "attribute";
6278 : static const char js_text_str[] = "text";
6279 :
6280 : /* Exported to jsgc.c #ifdef DEBUG. */
6281 : const char *js_xml_class_str[] = {
6282 : "list",
6283 : "element",
6284 : js_attribute_str,
6285 : "processing-instruction",
6286 : js_text_str,
6287 : "comment"
6288 : };
6289 :
6290 : static JSBool
6291 0 : xml_nodeKind(JSContext *cx, unsigned argc, jsval *vp)
6292 : {
6293 : JSString *str;
6294 :
6295 0 : NON_LIST_XML_METHOD_PROLOG;
6296 0 : str = JS_InternString(cx, js_xml_class_str[xml->xml_class]);
6297 0 : if (!str)
6298 0 : return JS_FALSE;
6299 0 : *vp = STRING_TO_JSVAL(str);
6300 0 : return JS_TRUE;
6301 : }
6302 :
6303 : static void
6304 0 : NormalizingDelete(JSContext *cx, JSXML *xml, uint32_t index)
6305 : {
6306 0 : if (xml->xml_class == JSXML_CLASS_LIST)
6307 0 : DeleteListElement(cx, xml, index);
6308 : else
6309 0 : DeleteByIndex(cx, xml, index);
6310 0 : }
6311 :
6312 : /* XML and XMLList */
6313 : static JSBool
6314 0 : xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml)
6315 : {
6316 : JSXML *kid, *kid2;
6317 : uint32_t i, n;
6318 : JSObject *kidobj;
6319 : JSString *str;
6320 :
6321 0 : if (!JSXML_HAS_KIDS(xml))
6322 0 : return JS_TRUE;
6323 :
6324 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6325 0 : if (!xml)
6326 0 : return JS_FALSE;
6327 :
6328 0 : for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6329 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6330 0 : if (!kid)
6331 0 : continue;
6332 0 : if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6333 0 : kidobj = js_GetXMLObject(cx, kid);
6334 0 : if (!kidobj || !xml_normalize_helper(cx, kidobj, kid))
6335 0 : return JS_FALSE;
6336 0 : } else if (kid->xml_class == JSXML_CLASS_TEXT) {
6337 0 : while (i + 1 < n &&
6338 0 : (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) &&
6339 : kid2->xml_class == JSXML_CLASS_TEXT) {
6340 0 : str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value);
6341 0 : if (!str)
6342 0 : return JS_FALSE;
6343 0 : NormalizingDelete(cx, xml, i + 1);
6344 0 : n = xml->xml_kids.length;
6345 0 : kid->xml_value = str;
6346 : }
6347 0 : if (kid->xml_value->empty()) {
6348 0 : NormalizingDelete(cx, xml, i);
6349 0 : n = xml->xml_kids.length;
6350 0 : --i;
6351 : }
6352 : }
6353 : }
6354 :
6355 0 : return JS_TRUE;
6356 : }
6357 :
6358 : static JSBool
6359 0 : xml_normalize(JSContext *cx, unsigned argc, jsval *vp)
6360 : {
6361 0 : XML_METHOD_PROLOG;
6362 0 : *vp = OBJECT_TO_JSVAL(obj);
6363 0 : return xml_normalize_helper(cx, obj, xml);
6364 : }
6365 :
6366 : /* XML and XMLList */
6367 : static JSBool
6368 0 : xml_parent(JSContext *cx, unsigned argc, jsval *vp)
6369 : {
6370 : JSXML *parent, *kid;
6371 : uint32_t i, n;
6372 : JSObject *parentobj;
6373 :
6374 0 : XML_METHOD_PROLOG;
6375 0 : parent = xml->parent;
6376 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
6377 0 : *vp = JSVAL_VOID;
6378 0 : n = xml->xml_kids.length;
6379 0 : if (n == 0)
6380 0 : return JS_TRUE;
6381 :
6382 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
6383 0 : if (!kid)
6384 0 : return JS_TRUE;
6385 0 : parent = kid->parent;
6386 0 : for (i = 1; i < n; i++) {
6387 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6388 0 : if (kid && kid->parent != parent)
6389 0 : return JS_TRUE;
6390 : }
6391 : }
6392 :
6393 0 : if (!parent) {
6394 0 : *vp = JSVAL_NULL;
6395 0 : return JS_TRUE;
6396 : }
6397 :
6398 0 : parentobj = js_GetXMLObject(cx, parent);
6399 0 : if (!parentobj)
6400 0 : return JS_FALSE;
6401 0 : *vp = OBJECT_TO_JSVAL(parentobj);
6402 0 : return JS_TRUE;
6403 : }
6404 :
6405 : /* XML and XMLList */
6406 : static JSBool
6407 0 : xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml,
6408 : JSObject *nameqn, jsval *vp)
6409 : {
6410 : JSXML *list, *vxml;
6411 : JSBool ok;
6412 : JSObject *kidobj;
6413 : jsval v;
6414 : uint32_t i, n;
6415 :
6416 0 : list = xml_list_helper(cx, xml, vp);
6417 0 : if (!list)
6418 0 : return JS_FALSE;
6419 :
6420 0 : list->xml_targetprop = nameqn;
6421 0 : ok = JS_TRUE;
6422 :
6423 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
6424 : /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */
6425 0 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
6426 0 : while (JSXML *kid = cursor.getNext()) {
6427 0 : if (kid->xml_class == JSXML_CLASS_ELEMENT) {
6428 0 : ok = js_EnterLocalRootScope(cx);
6429 0 : if (!ok)
6430 0 : break;
6431 0 : kidobj = js_GetXMLObject(cx, kid);
6432 0 : if (kidobj) {
6433 : ok = xml_processingInstructions_helper(cx, kidobj, kid,
6434 0 : nameqn, &v);
6435 : } else {
6436 0 : ok = JS_FALSE;
6437 0 : v = JSVAL_NULL;
6438 : }
6439 0 : js_LeaveLocalRootScopeWithResult(cx, v);
6440 0 : if (!ok)
6441 0 : break;
6442 0 : vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6443 0 : if (JSXML_LENGTH(vxml) != 0) {
6444 0 : ok = Append(cx, list, vxml);
6445 0 : if (!ok)
6446 0 : break;
6447 : }
6448 : }
6449 : }
6450 : } else {
6451 : /* 13.4.4.28 Step 4. */
6452 0 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6453 0 : JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6454 0 : if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) {
6455 0 : JSLinearString *localName = nameqn->getQNameLocalName();
6456 0 : if (IS_STAR(localName) ||
6457 0 : EqualStrings(localName, kid->name->getQNameLocalName())) {
6458 0 : ok = Append(cx, list, kid);
6459 0 : if (!ok)
6460 0 : break;
6461 : }
6462 : }
6463 : }
6464 : }
6465 :
6466 0 : return ok;
6467 : }
6468 :
6469 : static JSBool
6470 0 : xml_processingInstructions(JSContext *cx, unsigned argc, jsval *vp)
6471 : {
6472 : jsval name;
6473 : JSObject *nameqn;
6474 : jsid funid;
6475 :
6476 0 : XML_METHOD_PROLOG;
6477 :
6478 0 : name = (argc == 0) ? STRING_TO_JSVAL(cx->runtime->atomState.starAtom) : vp[2];
6479 0 : nameqn = ToXMLName(cx, name, &funid);
6480 0 : if (!nameqn)
6481 0 : return JS_FALSE;
6482 0 : vp[2] = OBJECT_TO_JSVAL(nameqn);
6483 :
6484 0 : if (!JSID_IS_VOID(funid))
6485 0 : return xml_list_helper(cx, xml, vp) != NULL;
6486 :
6487 0 : return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp);
6488 : }
6489 :
6490 : static JSBool
6491 0 : xml_prependChild(JSContext *cx, unsigned argc, jsval *vp)
6492 : {
6493 0 : NON_LIST_XML_METHOD_PROLOG;
6494 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6495 0 : if (!xml)
6496 0 : return JS_FALSE;
6497 0 : *vp = OBJECT_TO_JSVAL(obj);
6498 0 : return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID);
6499 : }
6500 :
6501 : /* XML and XMLList */
6502 : static JSBool
6503 0 : xml_propertyIsEnumerable(JSContext *cx, unsigned argc, jsval *vp)
6504 : {
6505 : bool isIndex;
6506 : uint32_t index;
6507 :
6508 0 : XML_METHOD_PROLOG;
6509 0 : *vp = JSVAL_FALSE;
6510 0 : if (argc != 0) {
6511 0 : if (!IdValIsIndex(cx, vp[2], &index, &isIndex))
6512 0 : return JS_FALSE;
6513 :
6514 0 : if (isIndex) {
6515 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
6516 : /* 13.5.4.18. */
6517 0 : *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length);
6518 : } else {
6519 : /* 13.4.4.30. */
6520 0 : *vp = BOOLEAN_TO_JSVAL(index == 0);
6521 : }
6522 : }
6523 : }
6524 0 : return JS_TRUE;
6525 : }
6526 :
6527 : static JSBool
6528 0 : namespace_full_match(const JSObject *nsa, const JSObject *nsb)
6529 : {
6530 0 : JSLinearString *prefixa = nsa->getNamePrefix();
6531 : JSLinearString *prefixb;
6532 :
6533 0 : if (prefixa) {
6534 0 : prefixb = nsb->getNamePrefix();
6535 0 : if (prefixb && !EqualStrings(prefixa, prefixb))
6536 0 : return JS_FALSE;
6537 : }
6538 0 : return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
6539 : }
6540 :
6541 : static JSBool
6542 0 : xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns)
6543 : {
6544 : JSObject *thisns, *attrns;
6545 : uint32_t i, n;
6546 : JSXML *attr, *kid;
6547 :
6548 0 : thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces);
6549 0 : JS_ASSERT(thisns);
6550 0 : if (thisns == ns)
6551 0 : return JS_TRUE;
6552 :
6553 0 : for (i = 0, n = xml->xml_attrs.length; i < n; i++) {
6554 0 : attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML);
6555 0 : if (!attr)
6556 0 : continue;
6557 0 : attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces);
6558 0 : JS_ASSERT(attrns);
6559 0 : if (attrns == ns)
6560 0 : return JS_TRUE;
6561 : }
6562 :
6563 0 : i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match);
6564 0 : if (i != XML_NOT_FOUND)
6565 0 : XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE);
6566 :
6567 0 : for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6568 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6569 0 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6570 0 : if (!xml_removeNamespace_helper(cx, kid, ns))
6571 0 : return JS_FALSE;
6572 : }
6573 : }
6574 0 : return JS_TRUE;
6575 : }
6576 :
6577 : static JSBool
6578 0 : xml_removeNamespace(JSContext *cx, unsigned argc, jsval *vp)
6579 : {
6580 : JSObject *ns;
6581 :
6582 0 : NON_LIST_XML_METHOD_PROLOG;
6583 0 : if (xml->xml_class != JSXML_CLASS_ELEMENT)
6584 0 : goto done;
6585 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6586 0 : if (!xml)
6587 0 : return JS_FALSE;
6588 :
6589 0 : if (!NamespaceHelper(cx, argc == 0 ? -1 : 1, vp + 2, vp))
6590 0 : return JS_FALSE;
6591 0 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6592 0 : ns = JSVAL_TO_OBJECT(*vp);
6593 :
6594 : /* NOTE: remove ns from each ancestor if not used by that ancestor. */
6595 0 : if (!xml_removeNamespace_helper(cx, xml, ns))
6596 0 : return JS_FALSE;
6597 : done:
6598 0 : *vp = OBJECT_TO_JSVAL(obj);
6599 0 : return JS_TRUE;
6600 : }
6601 :
6602 : static JSBool
6603 0 : xml_replace(JSContext *cx, unsigned argc, jsval *vp)
6604 : {
6605 : jsval value;
6606 : JSXML *vxml, *kid;
6607 : uint32_t index, i;
6608 : JSObject *nameqn;
6609 :
6610 0 : NON_LIST_XML_METHOD_PROLOG;
6611 0 : if (xml->xml_class != JSXML_CLASS_ELEMENT)
6612 0 : goto done;
6613 :
6614 0 : if (argc <= 1) {
6615 0 : value = STRING_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6616 : } else {
6617 0 : value = vp[3];
6618 0 : vxml = VALUE_IS_XML(value)
6619 0 : ? (JSXML *) JSVAL_TO_OBJECT(value)->getPrivate()
6620 0 : : NULL;
6621 0 : if (!vxml) {
6622 0 : if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
6623 0 : return JS_FALSE;
6624 0 : value = vp[3];
6625 : } else {
6626 0 : vxml = DeepCopy(cx, vxml, NULL, 0);
6627 0 : if (!vxml)
6628 0 : return JS_FALSE;
6629 0 : value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
6630 : }
6631 : }
6632 :
6633 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6634 0 : if (!xml)
6635 0 : return JS_FALSE;
6636 :
6637 : bool haveIndex;
6638 0 : if (argc == 0) {
6639 0 : haveIndex = false;
6640 : } else {
6641 0 : if (!IdValIsIndex(cx, vp[2], &index, &haveIndex))
6642 0 : return JS_FALSE;
6643 : }
6644 :
6645 0 : if (!haveIndex) {
6646 : /*
6647 : * Call function QName per spec, not ToXMLName, to avoid attribute
6648 : * names.
6649 : */
6650 0 : if (!QNameHelper(cx, argc == 0 ? -1 : 1, vp + 2, vp))
6651 0 : return JS_FALSE;
6652 0 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
6653 0 : nameqn = JSVAL_TO_OBJECT(*vp);
6654 :
6655 0 : i = xml->xml_kids.length;
6656 0 : index = XML_NOT_FOUND;
6657 0 : while (i != 0) {
6658 0 : --i;
6659 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6660 0 : if (kid && MatchElemName(nameqn, kid)) {
6661 0 : if (i != XML_NOT_FOUND)
6662 0 : DeleteByIndex(cx, xml, i);
6663 0 : index = i;
6664 : }
6665 : }
6666 :
6667 0 : if (index == XML_NOT_FOUND)
6668 0 : goto done;
6669 : }
6670 :
6671 0 : if (!Replace(cx, xml, index, value))
6672 0 : return JS_FALSE;
6673 :
6674 : done:
6675 0 : *vp = OBJECT_TO_JSVAL(obj);
6676 0 : return JS_TRUE;
6677 : }
6678 :
6679 : static JSBool
6680 0 : xml_setChildren(JSContext *cx, unsigned argc, jsval *vp)
6681 : {
6682 : JSObject *obj;
6683 :
6684 0 : if (!StartNonListXMLMethod(cx, vp, &obj))
6685 0 : return JS_FALSE;
6686 :
6687 0 : *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */
6688 0 : if (!PutProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.starAtom), false, vp))
6689 0 : return JS_FALSE;
6690 :
6691 0 : *vp = OBJECT_TO_JSVAL(obj);
6692 0 : return JS_TRUE;
6693 : }
6694 :
6695 : static JSBool
6696 0 : xml_setLocalName(JSContext *cx, unsigned argc, jsval *vp)
6697 : {
6698 0 : NON_LIST_XML_METHOD_PROLOG;
6699 0 : if (!JSXML_HAS_NAME(xml)) {
6700 0 : vp[0] = JSVAL_VOID;
6701 0 : return JS_TRUE;
6702 : }
6703 :
6704 : JSAtom *namestr;
6705 0 : if (argc == 0) {
6706 0 : namestr = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
6707 : } else {
6708 0 : jsval name = vp[2];
6709 0 : if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->isQName()) {
6710 0 : namestr = JSVAL_TO_OBJECT(name)->getQNameLocalName();
6711 : } else {
6712 0 : if (!js_ValueToAtom(cx, name, &namestr))
6713 0 : return false;
6714 : }
6715 : }
6716 :
6717 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6718 0 : if (!xml)
6719 0 : return JS_FALSE;
6720 0 : if (namestr)
6721 0 : xml->name->setQNameLocalName(namestr);
6722 0 : vp[0] = JSVAL_VOID;
6723 0 : return JS_TRUE;
6724 : }
6725 :
6726 : static JSBool
6727 0 : xml_setName(JSContext *cx, unsigned argc, jsval *vp)
6728 : {
6729 : jsval name;
6730 : JSObject *nameqn;
6731 : JSXML *nsowner;
6732 : JSXMLArray<JSObject> *nsarray;
6733 : uint32_t i, n;
6734 : JSObject *ns;
6735 :
6736 0 : NON_LIST_XML_METHOD_PROLOG;
6737 0 : if (!JSXML_HAS_NAME(xml))
6738 0 : return JS_TRUE;
6739 :
6740 0 : if (argc == 0) {
6741 0 : name = STRING_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
6742 : } else {
6743 0 : name = vp[2];
6744 0 : if (!JSVAL_IS_PRIMITIVE(name) &&
6745 0 : JSVAL_TO_OBJECT(name)->getClass() == &QNameClass &&
6746 0 : !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) {
6747 0 : name = vp[2] = nameqn->getQNameLocalNameVal();
6748 : }
6749 : }
6750 :
6751 0 : nameqn = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 1, &name);
6752 0 : if (!nameqn)
6753 0 : return JS_FALSE;
6754 :
6755 : /* ECMA-357 13.4.4.35 Step 4. */
6756 0 : if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
6757 0 : nameqn->setNameURI(cx->runtime->emptyString);
6758 :
6759 0 : xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
6760 0 : if (!xml)
6761 0 : return JS_FALSE;
6762 0 : xml->name = nameqn;
6763 :
6764 : /*
6765 : * Erratum: nothing in 13.4.4.35 talks about making the name match the
6766 : * in-scope namespaces, either by finding an in-scope namespace with a
6767 : * matching uri and setting the new name's prefix to that namespace's
6768 : * prefix, or by extending the in-scope namespaces for xml (which are in
6769 : * xml->parent if xml is an attribute or a PI).
6770 : */
6771 0 : if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6772 0 : nsowner = xml;
6773 : } else {
6774 0 : if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6775 0 : return JS_TRUE;
6776 0 : nsowner = xml->parent;
6777 : }
6778 :
6779 0 : if (nameqn->getNamePrefix()) {
6780 : /*
6781 : * The name being set has a prefix, which originally came from some
6782 : * namespace object (which may be the null namespace, where both the
6783 : * prefix and uri are the empty string). We must go through a full
6784 : * GetNamespace in case that namespace is in-scope in nsowner.
6785 : *
6786 : * If we find such an in-scope namespace, we return true right away,
6787 : * in this block. Otherwise, we fall through to the final return of
6788 : * AddInScopeNamespace(cx, nsowner, ns).
6789 : */
6790 0 : ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces);
6791 0 : if (!ns)
6792 0 : return JS_FALSE;
6793 :
6794 : /* XXXbe have to test membership to see whether GetNamespace added */
6795 0 : if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, pointer_match)) {
6796 0 : vp[0] = JSVAL_VOID;
6797 0 : return JS_TRUE;
6798 : }
6799 : } else {
6800 : /*
6801 : * At this point, we know prefix of nameqn is null, so its uri can't
6802 : * be the empty string (the null namespace always uses the empty string
6803 : * for both prefix and uri).
6804 : *
6805 : * This means we must inline GetNamespace and specialize it to match
6806 : * uri only, never prefix. If we find a namespace with nameqn's uri
6807 : * already in nsowner->xml_namespaces, then all that we need do is set
6808 : * prefix of nameqn to that namespace's prefix.
6809 : *
6810 : * If no such namespace exists, we can create one without going through
6811 : * the constructor, because we know uri of nameqn is non-empty (so
6812 : * prefix does not need to be converted from null to empty by QName).
6813 : */
6814 0 : JS_ASSERT(!nameqn->getNameURI()->empty());
6815 :
6816 0 : nsarray = &nsowner->xml_namespaces;
6817 0 : for (i = 0, n = nsarray->length; i < n; i++) {
6818 0 : ns = XMLARRAY_MEMBER(nsarray, i, JSObject);
6819 0 : if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) {
6820 0 : nameqn->setNamePrefix(ns->getNamePrefix());
6821 0 : vp[0] = JSVAL_VOID;
6822 0 : return JS_TRUE;
6823 : }
6824 : }
6825 :
6826 0 : ns = NewXMLNamespace(cx, NULL, nameqn->getNameURI(), JS_TRUE);
6827 0 : if (!ns)
6828 0 : return JS_FALSE;
6829 : }
6830 :
6831 0 : if (!AddInScopeNamespace(cx, nsowner, ns))
6832 0 : return JS_FALSE;
6833 0 : vp[0] = JSVAL_VOID;
6834 0 : return JS_TRUE;
6835 : }
6836 :
6837 : /* Utility function used within xml_setNamespace */
6838 0 : static JSBool qn_match(const JSXML *xml, const JSObject *qn)
6839 : {
6840 0 : return qname_identity(xml->name, qn);
6841 : }
6842 :
6843 : /* ECMA-357 13.4.4.36 */
6844 : static JSBool
6845 0 : xml_setNamespace(JSContext *cx, unsigned argc, jsval *vp)
6846 : {
6847 : JSObject *qn;
6848 : JSObject *ns;
6849 : jsval qnargv[2];
6850 : JSXML *nsowner;
6851 :
6852 0 : NON_LIST_XML_METHOD_PROLOG;
6853 0 : if (!JSXML_HAS_NAME(xml))
6854 0 : return JS_TRUE;
6855 :
6856 : ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL,
6857 0 : argc == 0 ? 0 : 1, vp + 2);
6858 0 : if (!ns)
6859 0 : return JS_FALSE;
6860 0 : vp[0] = OBJECT_TO_JSVAL(ns);
6861 0 : ns->setNamespaceDeclared(JSVAL_TRUE);
6862 :
6863 0 : qnargv[0] = OBJECT_TO_JSVAL(ns);
6864 0 : qnargv[1] = OBJECT_TO_JSVAL(xml->name);
6865 0 : qn = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 2, qnargv);
6866 0 : if (!qn)
6867 0 : return JS_FALSE;
6868 :
6869 : /*
6870 : * Erratum: setting the namespace of an attribute may cause it to duplicate
6871 : * an already-existing attribute. To preserve the invariant that there are
6872 : * not multiple attributes with the same name, we delete the existing
6873 : * attribute so that the mutated attribute will not be a duplicate.
6874 : */
6875 0 : if (xml->xml_class == JSXML_CLASS_ATTRIBUTE &&
6876 0 : xml->parent && xml->parent->xml_class == JSXML_CLASS_ELEMENT &&
6877 0 : !qn_match(xml, qn))
6878 : {
6879 0 : JSXMLArray<JSXML> *array = &xml->parent->xml_attrs;
6880 0 : uint32_t i = XMLArrayFindMember(array, qn, qn_match);
6881 0 : if (i != XML_NOT_FOUND)
6882 0 : XMLArrayDelete(cx, array, i, JS_TRUE);
6883 : }
6884 :
6885 0 : xml->name = qn;
6886 :
6887 : /*
6888 : * Erratum: the spec fails to update the governing in-scope namespaces.
6889 : * See the erratum noted in xml_setName, above.
6890 : */
6891 0 : if (xml->xml_class == JSXML_CLASS_ELEMENT) {
6892 0 : nsowner = xml;
6893 : } else {
6894 0 : if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT)
6895 0 : return JS_TRUE;
6896 0 : nsowner = xml->parent;
6897 : }
6898 0 : if (!AddInScopeNamespace(cx, nsowner, ns))
6899 0 : return JS_FALSE;
6900 0 : vp[0] = JSVAL_VOID;
6901 0 : return JS_TRUE;
6902 : }
6903 :
6904 : /* XML and XMLList */
6905 : static JSBool
6906 0 : xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp)
6907 : {
6908 : JSXML *list, *kid, *vxml;
6909 : uint32_t i, n;
6910 : JSObject *kidobj;
6911 : jsval v;
6912 :
6913 0 : list = xml_list_helper(cx, xml, vp);
6914 0 : if (!list)
6915 0 : return JS_FALSE;
6916 :
6917 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
6918 0 : for (i = 0, n = xml->xml_kids.length; i < n; i++) {
6919 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6920 0 : if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) {
6921 0 : JSBool ok = js_EnterLocalRootScope(cx);
6922 0 : if (!ok)
6923 0 : break;
6924 0 : kidobj = js_GetXMLObject(cx, kid);
6925 0 : if (kidobj) {
6926 0 : ok = xml_text_helper(cx, kidobj, kid, &v);
6927 : } else {
6928 0 : ok = JS_FALSE;
6929 0 : v = JSVAL_NULL;
6930 : }
6931 0 : js_LeaveLocalRootScopeWithResult(cx, v);
6932 0 : if (!ok)
6933 0 : return JS_FALSE;
6934 0 : vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate();
6935 0 : if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml))
6936 0 : return JS_FALSE;
6937 : }
6938 : }
6939 : } else {
6940 0 : for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) {
6941 0 : kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
6942 0 : if (kid && kid->xml_class == JSXML_CLASS_TEXT) {
6943 0 : if (!Append(cx, list, kid))
6944 0 : return JS_FALSE;
6945 : }
6946 : }
6947 : }
6948 0 : return JS_TRUE;
6949 : }
6950 :
6951 : static JSBool
6952 0 : xml_text(JSContext *cx, unsigned argc, jsval *vp)
6953 : {
6954 0 : XML_METHOD_PROLOG;
6955 0 : return xml_text_helper(cx, obj, xml, vp);
6956 : }
6957 :
6958 : /* XML and XMLList */
6959 : static JSString *
6960 171 : xml_toString_helper(JSContext *cx, JSXML *xml)
6961 : {
6962 : JSString *str, *kidstr;
6963 :
6964 171 : if (xml->xml_class == JSXML_CLASS_ATTRIBUTE ||
6965 : xml->xml_class == JSXML_CLASS_TEXT) {
6966 9 : return xml->xml_value;
6967 : }
6968 :
6969 162 : if (!HasSimpleContent(xml))
6970 54 : return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0);
6971 :
6972 108 : str = cx->runtime->emptyString;
6973 108 : if (!js_EnterLocalRootScope(cx))
6974 0 : return NULL;
6975 216 : JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
6976 126 : while (JSXML *kid = cursor.getNext()) {
6977 9 : if (kid->xml_class != JSXML_CLASS_COMMENT &&
6978 : kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) {
6979 9 : kidstr = xml_toString_helper(cx, kid);
6980 9 : if (!kidstr) {
6981 0 : str = NULL;
6982 0 : break;
6983 : }
6984 9 : str = js_ConcatStrings(cx, str, kidstr);
6985 9 : if (!str)
6986 0 : break;
6987 : }
6988 : }
6989 108 : js_LeaveLocalRootScopeWithResult(cx, str);
6990 108 : return str;
6991 : }
6992 :
6993 : static JSBool
6994 9 : xml_toSource(JSContext *cx, unsigned argc, jsval *vp)
6995 : {
6996 9 : JSObject *obj = ToObject(cx, &vp[1]);
6997 9 : if (!obj)
6998 0 : return JS_FALSE;
6999 9 : JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), TO_SOURCE_FLAG);
7000 9 : if (!str)
7001 0 : return JS_FALSE;
7002 9 : *vp = STRING_TO_JSVAL(str);
7003 9 : return JS_TRUE;
7004 : }
7005 :
7006 : static JSBool
7007 0 : xml_toString(JSContext *cx, unsigned argc, jsval *vp)
7008 : {
7009 : JSString *str;
7010 :
7011 0 : XML_METHOD_PROLOG;
7012 0 : str = xml_toString_helper(cx, xml);
7013 0 : if (!str)
7014 0 : return JS_FALSE;
7015 0 : *vp = STRING_TO_JSVAL(str);
7016 0 : return JS_TRUE;
7017 : }
7018 :
7019 : /* XML and XMLList */
7020 : static JSBool
7021 0 : xml_toXMLString(JSContext *cx, unsigned argc, jsval *vp)
7022 : {
7023 0 : JSObject *obj = ToObject(cx, &vp[1]);
7024 0 : if (!obj)
7025 0 : return JS_FALSE;
7026 0 : JSString *str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), 0);
7027 0 : if (!str)
7028 0 : return JS_FALSE;
7029 0 : *vp = STRING_TO_JSVAL(str);
7030 0 : return JS_TRUE;
7031 : }
7032 :
7033 : /* XML and XMLList */
7034 : static JSBool
7035 0 : xml_valueOf(JSContext *cx, unsigned argc, jsval *vp)
7036 : {
7037 0 : JSObject *obj = ToObject(cx, &vp[1]);
7038 0 : if (!obj)
7039 0 : return false;
7040 0 : *vp = OBJECT_TO_JSVAL(obj);
7041 0 : return true;
7042 : }
7043 :
7044 : static JSFunctionSpec xml_methods[] = {
7045 : JS_FN("addNamespace", xml_addNamespace, 1,0),
7046 : JS_FN("appendChild", xml_appendChild, 1,0),
7047 : JS_FN(js_attribute_str, xml_attribute, 1,0),
7048 : JS_FN("attributes", xml_attributes, 0,0),
7049 : JS_FN("child", xml_child, 1,0),
7050 : JS_FN("childIndex", xml_childIndex, 0,0),
7051 : JS_FN("children", xml_children, 0,0),
7052 : JS_FN("comments", xml_comments, 0,0),
7053 : JS_FN("contains", xml_contains, 1,0),
7054 : JS_FN("copy", xml_copy, 0,0),
7055 : JS_FN("descendants", xml_descendants, 1,0),
7056 : JS_FN("elements", xml_elements, 1,0),
7057 : JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0),
7058 : JS_FN("hasComplexContent", xml_hasComplexContent, 1,0),
7059 : JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0),
7060 : JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0),
7061 : JS_FN("insertChildAfter", xml_insertChildAfter, 2,0),
7062 : JS_FN("insertChildBefore", xml_insertChildBefore, 2,0),
7063 : JS_FN(js_length_str, xml_length, 0,0),
7064 : JS_FN(js_localName_str, xml_localName, 0,0),
7065 : JS_FN(js_name_str, xml_name, 0,0),
7066 : JS_FN(js_namespace_str, xml_namespace, 1,0),
7067 : JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0),
7068 : JS_FN("nodeKind", xml_nodeKind, 0,0),
7069 : JS_FN("normalize", xml_normalize, 0,0),
7070 : JS_FN(js_xml_parent_str, xml_parent, 0,0),
7071 : JS_FN("processingInstructions",xml_processingInstructions,1,0),
7072 : JS_FN("prependChild", xml_prependChild, 1,0),
7073 : JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0),
7074 : JS_FN("removeNamespace", xml_removeNamespace, 1,0),
7075 : JS_FN("replace", xml_replace, 2,0),
7076 : JS_FN("setChildren", xml_setChildren, 1,0),
7077 : JS_FN("setLocalName", xml_setLocalName, 1,0),
7078 : JS_FN("setName", xml_setName, 1,0),
7079 : JS_FN("setNamespace", xml_setNamespace, 1,0),
7080 : JS_FN(js_text_str, xml_text, 0,0),
7081 : JS_FN(js_toSource_str, xml_toSource, 0,0),
7082 : JS_FN(js_toString_str, xml_toString, 0,0),
7083 : JS_FN(js_toXMLString_str, xml_toXMLString, 0,0),
7084 : JS_FN(js_valueOf_str, xml_valueOf, 0,0),
7085 : JS_FS_END
7086 : };
7087 :
7088 : static JSBool
7089 0 : CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to)
7090 : {
7091 : int i;
7092 : const char *name;
7093 : jsval v;
7094 :
7095 : /* Note: PRETTY_INDENT is not a boolean setting. */
7096 0 : for (i = 0; xml_static_props[i].name; i++) {
7097 0 : name = xml_static_props[i].name;
7098 0 : if (!JS_GetProperty(cx, from, name, &v))
7099 0 : return false;
7100 0 : if (name == js_prettyIndent_str) {
7101 0 : if (!JSVAL_IS_NUMBER(v))
7102 0 : continue;
7103 : } else {
7104 0 : if (!JSVAL_IS_BOOLEAN(v))
7105 0 : continue;
7106 : }
7107 0 : if (!JS_SetProperty(cx, to, name, &v))
7108 0 : return false;
7109 : }
7110 :
7111 0 : return true;
7112 : }
7113 :
7114 : static JSBool
7115 777 : SetDefaultXMLSettings(JSContext *cx, JSObject *obj)
7116 : {
7117 : int i;
7118 : jsval v;
7119 :
7120 : /* Note: PRETTY_INDENT is not a boolean setting. */
7121 4662 : for (i = 0; xml_static_props[i].name; i++) {
7122 : v = (xml_static_props[i].name != js_prettyIndent_str)
7123 3885 : ? JSVAL_TRUE : INT_TO_JSVAL(2);
7124 3885 : if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v))
7125 0 : return JS_FALSE;
7126 : }
7127 777 : return true;
7128 : }
7129 :
7130 : static JSBool
7131 0 : xml_settings(JSContext *cx, unsigned argc, jsval *vp)
7132 : {
7133 0 : JSObject *settings = JS_NewObject(cx, NULL, NULL, NULL);
7134 0 : if (!settings)
7135 0 : return false;
7136 0 : *vp = OBJECT_TO_JSVAL(settings);
7137 0 : JSObject *obj = ToObject(cx, &vp[1]);
7138 0 : if (!obj)
7139 0 : return false;
7140 0 : return CopyXMLSettings(cx, obj, settings);
7141 : }
7142 :
7143 : static JSBool
7144 0 : xml_setSettings(JSContext *cx, unsigned argc, jsval *vp)
7145 : {
7146 : JSObject *settings;
7147 : jsval v;
7148 : JSBool ok;
7149 :
7150 0 : JSObject *obj = ToObject(cx, &vp[1]);
7151 0 : if (!obj)
7152 0 : return JS_FALSE;
7153 0 : v = (argc == 0) ? JSVAL_VOID : vp[2];
7154 0 : if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
7155 0 : ok = SetDefaultXMLSettings(cx, obj);
7156 : } else {
7157 0 : if (JSVAL_IS_PRIMITIVE(v)) {
7158 0 : vp[0] = JSVAL_VOID;
7159 0 : return JS_TRUE;
7160 : }
7161 0 : settings = JSVAL_TO_OBJECT(v);
7162 0 : ok = CopyXMLSettings(cx, settings, obj);
7163 : }
7164 0 : vp[0] = JSVAL_VOID;
7165 0 : return ok;
7166 : }
7167 :
7168 : static JSBool
7169 0 : xml_defaultSettings(JSContext *cx, unsigned argc, jsval *vp)
7170 : {
7171 : JSObject *settings;
7172 :
7173 0 : settings = JS_NewObject(cx, NULL, NULL, NULL);
7174 0 : if (!settings)
7175 0 : return JS_FALSE;
7176 0 : *vp = OBJECT_TO_JSVAL(settings);
7177 0 : return SetDefaultXMLSettings(cx, settings);
7178 : }
7179 :
7180 : static JSFunctionSpec xml_static_methods[] = {
7181 : JS_FN("settings", xml_settings, 0,0),
7182 : JS_FN("setSettings", xml_setSettings, 1,0),
7183 : JS_FN("defaultSettings", xml_defaultSettings, 0,0),
7184 : JS_FS_END
7185 : };
7186 :
7187 : static JSBool
7188 756 : XML(JSContext *cx, unsigned argc, Value *vp)
7189 : {
7190 : JSXML *xml, *copy;
7191 : JSObject *xobj, *vobj;
7192 : Class *clasp;
7193 :
7194 756 : jsval v = argc ? vp[2] : JSVAL_VOID;
7195 :
7196 756 : if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7197 0 : v = STRING_TO_JSVAL(cx->runtime->emptyString);
7198 :
7199 756 : xobj = ToXML(cx, v);
7200 756 : if (!xobj)
7201 0 : return JS_FALSE;
7202 756 : xml = (JSXML *) xobj->getPrivate();
7203 :
7204 756 : if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) {
7205 0 : vobj = JSVAL_TO_OBJECT(v);
7206 0 : clasp = vobj->getClass();
7207 0 : if (clasp == &XMLClass ||
7208 : (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) {
7209 0 : copy = DeepCopy(cx, xml, NULL, 0);
7210 0 : if (!copy)
7211 0 : return JS_FALSE;
7212 0 : vp->setObject(*copy->object);
7213 0 : return JS_TRUE;
7214 : }
7215 : }
7216 :
7217 756 : vp->setObject(*xobj);
7218 756 : return JS_TRUE;
7219 : }
7220 :
7221 : static JSBool
7222 45 : XMLList(JSContext *cx, unsigned argc, jsval *vp)
7223 : {
7224 : JSObject *vobj, *listobj;
7225 : JSXML *xml, *list;
7226 :
7227 45 : jsval v = argc ? vp[2] : JSVAL_VOID;
7228 :
7229 45 : if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
7230 36 : v = STRING_TO_JSVAL(cx->runtime->emptyString);
7231 :
7232 45 : if (IsConstructing(vp) && !JSVAL_IS_PRIMITIVE(v)) {
7233 0 : vobj = JSVAL_TO_OBJECT(v);
7234 0 : if (vobj->isXML()) {
7235 0 : xml = (JSXML *) vobj->getPrivate();
7236 0 : if (xml->xml_class == JSXML_CLASS_LIST) {
7237 0 : listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7238 0 : if (!listobj)
7239 0 : return JS_FALSE;
7240 0 : *vp = OBJECT_TO_JSVAL(listobj);
7241 :
7242 0 : list = (JSXML *) listobj->getPrivate();
7243 0 : if (!Append(cx, list, xml))
7244 0 : return JS_FALSE;
7245 0 : return JS_TRUE;
7246 : }
7247 : }
7248 : }
7249 :
7250 : /* Toggle on XML support since the script has explicitly requested it. */
7251 45 : listobj = ToXMLList(cx, v);
7252 45 : if (!listobj)
7253 0 : return JS_FALSE;
7254 :
7255 45 : *vp = OBJECT_TO_JSVAL(listobj);
7256 45 : return JS_TRUE;
7257 : }
7258 :
7259 : #ifdef DEBUG_notme
7260 : JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks);
7261 : uint32_t xml_serial;
7262 : #endif
7263 :
7264 : JSXML *
7265 4729195 : js_NewXML(JSContext *cx, JSXMLClass xml_class)
7266 : {
7267 4729195 : JSXML *xml = js_NewGCXML(cx);
7268 4729195 : if (!xml)
7269 0 : return NULL;
7270 :
7271 4729195 : xml->object.init(NULL);
7272 4729195 : xml->domnode = NULL;
7273 4729195 : xml->parent.init(NULL);
7274 4729195 : xml->name.init(NULL);
7275 4729195 : xml->xml_class = xml_class;
7276 4729195 : xml->xml_flags = 0;
7277 4729195 : if (JSXML_CLASS_HAS_VALUE(xml_class)) {
7278 2360964 : xml->xml_value.init(cx->runtime->emptyString);
7279 : } else {
7280 2368231 : xml->xml_value.init(NULL);
7281 2368231 : xml->xml_kids.init();
7282 2368231 : if (xml_class == JSXML_CLASS_LIST) {
7283 6845 : xml->xml_target.init(NULL);
7284 6845 : xml->xml_targetprop.init(NULL);
7285 : } else {
7286 2361386 : xml->xml_namespaces.init();
7287 2361386 : xml->xml_attrs.init();
7288 : }
7289 : }
7290 :
7291 : #ifdef DEBUG_notme
7292 : JS_APPEND_LINK(&xml->links, &xml_leaks);
7293 : xml->serial = xml_serial++;
7294 : #endif
7295 4729195 : return xml;
7296 : }
7297 :
7298 : void
7299 18898217 : JSXML::writeBarrierPre(JSXML *xml)
7300 : {
7301 : #ifdef JSGC_INCREMENTAL
7302 18898217 : if (!xml)
7303 11818474 : return;
7304 :
7305 7079743 : JSCompartment *comp = xml->compartment();
7306 7079743 : if (comp->needsBarrier()) {
7307 0 : JSXML *tmp = xml;
7308 0 : MarkXMLUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
7309 0 : JS_ASSERT(tmp == xml);
7310 : }
7311 : #endif
7312 : }
7313 :
7314 : void
7315 23629348 : JSXML::writeBarrierPost(JSXML *xml, void *addr)
7316 : {
7317 23629348 : }
7318 :
7319 : void
7320 194 : js_TraceXML(JSTracer *trc, JSXML *xml)
7321 : {
7322 194 : if (xml->object)
7323 178 : MarkObject(trc, &xml->object, "object");
7324 194 : if (xml->name)
7325 25 : MarkObject(trc, &xml->name, "name");
7326 194 : if (xml->parent)
7327 7 : MarkXML(trc, &xml->parent, "xml_parent");
7328 :
7329 194 : if (JSXML_HAS_VALUE(xml)) {
7330 169 : if (xml->xml_value)
7331 169 : MarkString(trc, &xml->xml_value, "value");
7332 169 : return;
7333 : }
7334 :
7335 25 : MarkXMLRange(trc, xml->xml_kids.length, xml->xml_kids.vector, "xml_kids");
7336 25 : js_XMLArrayCursorTrace(trc, xml->xml_kids.cursors);
7337 :
7338 25 : if (xml->xml_class == JSXML_CLASS_LIST) {
7339 0 : if (xml->xml_target)
7340 0 : MarkXML(trc, &xml->xml_target, "target");
7341 0 : if (xml->xml_targetprop)
7342 0 : MarkObject(trc, &xml->xml_targetprop, "targetprop");
7343 : } else {
7344 : MarkObjectRange(trc, xml->xml_namespaces.length,
7345 : xml->xml_namespaces.vector,
7346 25 : "xml_namespaces");
7347 25 : js_XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
7348 :
7349 25 : MarkXMLRange(trc, xml->xml_attrs.length, xml->xml_attrs.vector, "xml_attrs");
7350 25 : js_XMLArrayCursorTrace(trc, xml->xml_attrs.cursors);
7351 : }
7352 : }
7353 :
7354 : JSObject *
7355 6854 : js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
7356 : {
7357 6854 : JSXML *xml = js_NewXML(cx, xml_class);
7358 6854 : if (!xml)
7359 0 : return NULL;
7360 :
7361 13708 : AutoXMLRooter root(cx, xml);
7362 6854 : return js_GetXMLObject(cx, xml);
7363 : }
7364 :
7365 : static JSObject *
7366 8421 : NewXMLObject(JSContext *cx, JSXML *xml)
7367 : {
7368 : JSObject *obj;
7369 :
7370 8421 : JSObject *parent = GetGlobalForScopeChain(cx);
7371 8421 : obj = NewObjectWithClassProto(cx, &XMLClass, NULL, parent);
7372 8421 : if (!obj)
7373 0 : return NULL;
7374 8421 : obj->setPrivate(xml);
7375 8421 : return obj;
7376 : }
7377 :
7378 : JSObject *
7379 8466 : js_GetXMLObject(JSContext *cx, JSXML *xml)
7380 : {
7381 : JSObject *obj;
7382 :
7383 8466 : obj = xml->object;
7384 8466 : if (obj) {
7385 45 : JS_ASSERT(obj->getPrivate() == xml);
7386 45 : return obj;
7387 : }
7388 :
7389 8421 : obj = NewXMLObject(cx, xml);
7390 8421 : if (!obj)
7391 0 : return NULL;
7392 8421 : xml->object = obj;
7393 8421 : return obj;
7394 : }
7395 :
7396 : JSObject *
7397 813 : js_InitNamespaceClass(JSContext *cx, JSObject *obj)
7398 : {
7399 813 : JS_ASSERT(obj->isNative());
7400 :
7401 813 : GlobalObject *global = &obj->asGlobal();
7402 :
7403 813 : JSObject *namespaceProto = global->createBlankPrototype(cx, &NamespaceClass);
7404 813 : if (!namespaceProto)
7405 0 : return NULL;
7406 813 : JSFlatString *empty = cx->runtime->emptyString;
7407 813 : namespaceProto->setNamePrefix(empty);
7408 813 : namespaceProto->setNameURI(empty);
7409 :
7410 813 : const unsigned NAMESPACE_CTOR_LENGTH = 2;
7411 813 : JSFunction *ctor = global->createConstructor(cx, Namespace, CLASS_ATOM(cx, Namespace),
7412 813 : NAMESPACE_CTOR_LENGTH);
7413 813 : if (!ctor)
7414 0 : return NULL;
7415 :
7416 813 : if (!LinkConstructorAndPrototype(cx, ctor, namespaceProto))
7417 0 : return NULL;
7418 :
7419 813 : if (!DefinePropertiesAndBrand(cx, namespaceProto, namespace_props, namespace_methods))
7420 0 : return NULL;
7421 :
7422 813 : if (!DefineConstructorAndPrototype(cx, global, JSProto_Namespace, ctor, namespaceProto))
7423 0 : return NULL;
7424 :
7425 813 : return namespaceProto;
7426 : }
7427 :
7428 : JSObject *
7429 822 : js_InitQNameClass(JSContext *cx, JSObject *obj)
7430 : {
7431 822 : JS_ASSERT(obj->isNative());
7432 :
7433 822 : GlobalObject *global = &obj->asGlobal();
7434 :
7435 822 : JSObject *qnameProto = global->createBlankPrototype(cx, &QNameClass);
7436 822 : if (!qnameProto)
7437 0 : return NULL;
7438 822 : JSAtom *empty = cx->runtime->emptyString;
7439 822 : if (!InitXMLQName(cx, qnameProto, empty, empty, empty))
7440 0 : return NULL;
7441 :
7442 822 : const unsigned QNAME_CTOR_LENGTH = 2;
7443 822 : JSFunction *ctor = global->createConstructor(cx, QName, CLASS_ATOM(cx, QName),
7444 822 : QNAME_CTOR_LENGTH);
7445 822 : if (!ctor)
7446 0 : return NULL;
7447 :
7448 822 : if (!LinkConstructorAndPrototype(cx, ctor, qnameProto))
7449 0 : return NULL;
7450 :
7451 822 : if (!DefinePropertiesAndBrand(cx, qnameProto, NULL, qname_methods))
7452 0 : return NULL;
7453 :
7454 822 : if (!DefineConstructorAndPrototype(cx, global, JSProto_QName, ctor, qnameProto))
7455 0 : return NULL;
7456 :
7457 822 : return qnameProto;
7458 : }
7459 :
7460 : JSObject *
7461 777 : js_InitXMLClass(JSContext *cx, JSObject *obj)
7462 : {
7463 777 : JS_ASSERT(obj->isNative());
7464 :
7465 777 : GlobalObject *global = &obj->asGlobal();
7466 :
7467 777 : JSObject *xmlProto = global->createBlankPrototype(cx, &XMLClass);
7468 777 : if (!xmlProto)
7469 0 : return NULL;
7470 777 : JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT);
7471 777 : if (!xml)
7472 0 : return NULL;
7473 777 : xmlProto->setPrivate(xml);
7474 777 : xml->object = xmlProto;
7475 :
7476 : /* Don't count this as a real content-created XML object. */
7477 777 : if (!cx->runningWithTrustedPrincipals()) {
7478 677 : JS_ASSERT(sE4XObjectsCreated > 0);
7479 677 : --sE4XObjectsCreated;
7480 : }
7481 :
7482 777 : const unsigned XML_CTOR_LENGTH = 1;
7483 777 : JSFunction *ctor = global->createConstructor(cx, XML, CLASS_ATOM(cx, XML), XML_CTOR_LENGTH);
7484 777 : if (!ctor)
7485 0 : return NULL;
7486 :
7487 777 : if (!LinkConstructorAndPrototype(cx, ctor, xmlProto))
7488 0 : return NULL;
7489 :
7490 1554 : if (!DefinePropertiesAndBrand(cx, xmlProto, NULL, xml_methods) ||
7491 777 : !DefinePropertiesAndBrand(cx, ctor, xml_static_props, xml_static_methods))
7492 : {
7493 0 : return NULL;
7494 : }
7495 :
7496 777 : if (!SetDefaultXMLSettings(cx, ctor))
7497 0 : return NULL;
7498 :
7499 : /* Define the XMLList function, and give it the same .prototype as XML. */
7500 : JSFunction *xmllist =
7501 777 : JS_DefineFunction(cx, global, js_XMLList_str, XMLList, 1, JSFUN_CONSTRUCTOR);
7502 777 : if (!xmllist)
7503 0 : return NULL;
7504 777 : if (!xmllist->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom,
7505 : ObjectValue(*xmlProto), JS_PropertyStub, JS_StrictPropertyStub,
7506 777 : JSPROP_PERMANENT | JSPROP_READONLY))
7507 : {
7508 0 : return NULL;
7509 : }
7510 :
7511 777 : if (!DefineConstructorAndPrototype(cx, global, JSProto_XML, ctor, xmlProto))
7512 0 : return NULL;
7513 :
7514 : /* Define the isXMLName function. */
7515 777 : if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0))
7516 0 : return NULL;
7517 :
7518 777 : return xmlProto;
7519 : }
7520 :
7521 : JSObject *
7522 264 : js_InitXMLClasses(JSContext *cx, JSObject *obj)
7523 : {
7524 264 : if (!js_InitNamespaceClass(cx, obj))
7525 0 : return NULL;
7526 264 : if (!js_InitQNameClass(cx, obj))
7527 0 : return NULL;
7528 264 : return js_InitXMLClass(cx, obj);
7529 : }
7530 :
7531 : namespace js {
7532 :
7533 : bool
7534 36 : GlobalObject::getFunctionNamespace(JSContext *cx, Value *vp)
7535 : {
7536 36 : HeapSlot &v = getSlotRef(FUNCTION_NS);
7537 36 : if (v.isUndefined()) {
7538 36 : JSRuntime *rt = cx->runtime;
7539 36 : JSLinearString *prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION];
7540 36 : JSLinearString *uri = rt->atomState.functionNamespaceURIAtom;
7541 36 : JSObject *obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE);
7542 36 : if (!obj)
7543 0 : return false;
7544 :
7545 : /*
7546 : * Avoid entraining any in-scope Object.prototype. The loss of
7547 : * Namespace.prototype is not detectable, as there is no way to
7548 : * refer to this instance in scripts. When used to qualify method
7549 : * names, its prefix and uri references are copied to the QName.
7550 : * The parent remains set and links back to global.
7551 : */
7552 36 : if (!obj->clearType(cx))
7553 0 : return false;
7554 :
7555 36 : v.set(this, FUNCTION_NS, ObjectValue(*obj));
7556 : }
7557 :
7558 36 : *vp = v;
7559 36 : return true;
7560 : }
7561 :
7562 : } // namespace js
7563 :
7564 : /*
7565 : * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML-
7566 : * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID,
7567 : * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no
7568 : * requirement that fp->varobj lie directly on fp->scopeChain, although
7569 : * it should be reachable using the prototype chain from a scope object (cf.
7570 : * JSOPTION_VAROBJFIX in jsapi.h).
7571 : *
7572 : * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it
7573 : * creates a default namespace via 'new Namespace()'. In contrast, Set uses
7574 : * its v argument as the uri of a new Namespace, with "" as the prefix. See
7575 : * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n,
7576 : * the default XML namespace will be set to ("", n.uri). So the uri string
7577 : * is really the only usefully stored value of the default namespace.
7578 : */
7579 : JSBool
7580 13123 : js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
7581 : {
7582 : JSObject *ns, *obj, *tmp;
7583 : jsval v;
7584 :
7585 13123 : JSObject *scopeChain = GetCurrentScopeChain(cx);
7586 13123 : if (!scopeChain)
7587 0 : return false;
7588 :
7589 13123 : obj = NULL;
7590 14177 : for (tmp = scopeChain; tmp; tmp = tmp->enclosingScope()) {
7591 13861 : if (tmp->isBlock() || tmp->isWith())
7592 594 : continue;
7593 13267 : if (!tmp->getSpecial(cx, tmp, SpecialId::defaultXMLNamespace(), &v))
7594 0 : return JS_FALSE;
7595 13267 : if (!JSVAL_IS_PRIMITIVE(v)) {
7596 12807 : *vp = v;
7597 12807 : return JS_TRUE;
7598 : }
7599 460 : obj = tmp;
7600 : }
7601 :
7602 316 : ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, 0, NULL);
7603 316 : if (!ns)
7604 0 : return JS_FALSE;
7605 316 : v = OBJECT_TO_JSVAL(ns);
7606 316 : if (!obj->defineSpecial(cx, SpecialId::defaultXMLNamespace(), v,
7607 316 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT)) {
7608 0 : return JS_FALSE;
7609 : }
7610 316 : *vp = v;
7611 316 : return JS_TRUE;
7612 : }
7613 :
7614 : JSBool
7615 9 : js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
7616 : {
7617 : Value argv[2];
7618 9 : argv[0].setString(cx->runtime->emptyString);
7619 9 : argv[1] = v;
7620 9 : JSObject *ns = JS_ConstructObjectWithArguments(cx, Jsvalify(&NamespaceClass), NULL, 2, argv);
7621 9 : if (!ns)
7622 0 : return JS_FALSE;
7623 :
7624 9 : JSObject &varobj = cx->fp()->varObj();
7625 9 : if (!varobj.defineSpecial(cx, SpecialId::defaultXMLNamespace(), ObjectValue(*ns),
7626 9 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT)) {
7627 0 : return JS_FALSE;
7628 : }
7629 9 : return JS_TRUE;
7630 : }
7631 :
7632 : JSBool
7633 0 : js_ToAttributeName(JSContext *cx, Value *vp)
7634 : {
7635 : JSObject *qn;
7636 :
7637 0 : qn = ToAttributeName(cx, *vp);
7638 0 : if (!qn)
7639 0 : return JS_FALSE;
7640 0 : vp->setObject(*qn);
7641 0 : return JS_TRUE;
7642 : }
7643 :
7644 : JSFlatString *
7645 1378 : js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote)
7646 : {
7647 2756 : StringBuffer sb(cx);
7648 1378 : return EscapeAttributeValue(cx, sb, str, quote);
7649 : }
7650 :
7651 : JSString *
7652 18 : js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
7653 : {
7654 18 : size_t len = str->length();
7655 18 : const jschar *chars = str->getChars(cx);
7656 18 : if (!chars)
7657 0 : return NULL;
7658 :
7659 18 : size_t len2 = str2->length();
7660 18 : const jschar *chars2 = str2->getChars(cx);
7661 18 : if (!chars2)
7662 0 : return NULL;
7663 :
7664 18 : size_t newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
7665 18 : jschar *newchars = (jschar *) cx->malloc_((newlen+1) * sizeof(jschar));
7666 18 : if (!newchars)
7667 0 : return NULL;
7668 :
7669 18 : js_strncpy(newchars, chars, len);
7670 18 : newchars += len;
7671 18 : if (isName) {
7672 9 : *newchars++ = ' ';
7673 9 : js_strncpy(newchars, chars2, len2);
7674 9 : newchars += len2;
7675 : } else {
7676 9 : *newchars++ = '=';
7677 9 : *newchars++ = '"';
7678 9 : js_strncpy(newchars, chars2, len2);
7679 9 : newchars += len2;
7680 9 : *newchars++ = '"';
7681 : }
7682 18 : *newchars = 0;
7683 18 : return js_NewString(cx, newchars - newlen, newlen);
7684 : }
7685 :
7686 : JSFlatString *
7687 0 : js_EscapeElementValue(JSContext *cx, JSString *str)
7688 : {
7689 0 : StringBuffer sb(cx);
7690 0 : return EscapeElementValue(cx, sb, str, 0);
7691 : }
7692 :
7693 : JSString *
7694 0 : js_ValueToXMLString(JSContext *cx, const Value &v)
7695 : {
7696 0 : return ToXMLString(cx, v, 0);
7697 : }
7698 :
7699 : JSBool
7700 18 : js_GetAnyName(JSContext *cx, jsid *idp)
7701 : {
7702 18 : JSObject *global = cx->hasfp() ? &cx->fp()->scopeChain().global() : cx->globalObject;
7703 18 : Value v = global->getReservedSlot(JSProto_AnyName);
7704 18 : if (v.isUndefined()) {
7705 18 : JSObject *obj = NewObjectWithGivenProto(cx, &AnyNameClass, NULL, global);
7706 18 : if (!obj)
7707 0 : return false;
7708 :
7709 18 : JS_ASSERT(!obj->getProto());
7710 :
7711 18 : JSRuntime *rt = cx->runtime;
7712 18 : if (!InitXMLQName(cx, obj, rt->emptyString, rt->emptyString, rt->atomState.starAtom))
7713 0 : return false;
7714 :
7715 18 : v.setObject(*obj);
7716 18 : SetReservedSlot(global, JSProto_AnyName, v);
7717 : }
7718 18 : *idp = OBJECT_TO_JSID(&v.toObject());
7719 18 : return true;
7720 : }
7721 :
7722 : JSBool
7723 54 : js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *idp)
7724 : {
7725 : JSObject *nameobj;
7726 : jsval v;
7727 : JSObject *qn;
7728 : jsid funid;
7729 : JSObject *obj, *target, *proto, *pobj;
7730 : JSXML *xml;
7731 : JSBool found;
7732 : JSProperty *prop;
7733 :
7734 54 : JS_ASSERT(nameval.isObject());
7735 54 : nameobj = &nameval.toObject();
7736 54 : if (nameobj->getClass() == &AnyNameClass) {
7737 18 : v = STRING_TO_JSVAL(cx->runtime->atomState.starAtom);
7738 18 : nameobj = JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 1, &v);
7739 18 : if (!nameobj)
7740 0 : return JS_FALSE;
7741 : } else {
7742 72 : JS_ASSERT(nameobj->getClass() == &AttributeNameClass ||
7743 72 : nameobj->getClass() == &QNameClass);
7744 : }
7745 :
7746 54 : qn = nameobj;
7747 :
7748 : JSAtom *name;
7749 54 : funid = GetLocalNameFromFunctionQName(qn, &name, cx)
7750 27 : ? ATOM_TO_JSID(name)
7751 81 : : JSID_VOID;
7752 :
7753 54 : obj = cx->stack.currentScriptedScopeChain();
7754 45 : do {
7755 : /* Skip any With object that can wrap XML. */
7756 72 : target = obj;
7757 144 : while (target->getClass() == &WithClass) {
7758 0 : proto = target->getProto();
7759 0 : if (!proto)
7760 0 : break;
7761 0 : target = proto;
7762 : }
7763 :
7764 72 : if (target->isXML()) {
7765 0 : if (JSID_IS_VOID(funid)) {
7766 0 : xml = (JSXML *) target->getPrivate();
7767 0 : found = HasNamedProperty(xml, qn);
7768 : } else {
7769 0 : if (!HasFunctionProperty(cx, target, funid, &found))
7770 0 : return JS_FALSE;
7771 : }
7772 0 : if (found) {
7773 0 : *idp = OBJECT_TO_JSID(nameobj);
7774 0 : *objp = target;
7775 0 : return JS_TRUE;
7776 : }
7777 72 : } else if (!JSID_IS_VOID(funid)) {
7778 27 : if (!target->lookupGeneric(cx, funid, &pobj, &prop))
7779 0 : return JS_FALSE;
7780 27 : if (prop) {
7781 27 : *idp = funid;
7782 27 : *objp = target;
7783 27 : return JS_TRUE;
7784 : }
7785 : }
7786 : } while ((obj = obj->enclosingScope()) != NULL);
7787 :
7788 54 : JSAutoByteString printable;
7789 27 : JSString *str = ConvertQNameToString(cx, nameobj);
7790 27 : if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
7791 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
7792 27 : JSMSG_UNDEFINED_XML_NAME, printable.ptr());
7793 : }
7794 27 : return JS_FALSE;
7795 : }
7796 :
7797 : static JSBool
7798 720 : GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
7799 : {
7800 720 : JS_ASSERT(obj->isXML());
7801 :
7802 : /*
7803 : * See comments before xml_lookupGeneric about the need for the proto
7804 : * chain lookup.
7805 : */
7806 720 : JSObject *target = obj;
7807 180 : for (;;) {
7808 900 : if (!js_GetProperty(cx, target, id, vp))
7809 0 : return false;
7810 900 : if (!JSVAL_IS_PRIMITIVE(*vp) && JSVAL_TO_OBJECT(*vp)->isFunction())
7811 630 : return true;
7812 270 : target = target->getProto();
7813 270 : if (target == NULL || !target->isNative())
7814 : break;
7815 : }
7816 :
7817 90 : JSXML *xml = (JSXML *) obj->getPrivate();
7818 90 : if (!HasSimpleContent(xml))
7819 0 : return true;
7820 :
7821 : /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
7822 90 : JSObject *proto = obj->global().getOrCreateStringPrototype(cx);
7823 90 : if (!proto)
7824 0 : return false;
7825 :
7826 90 : return proto->getGeneric(cx, id, vp);
7827 : }
7828 :
7829 : static JSXML *
7830 9 : GetPrivate(JSContext *cx, JSObject *obj, const char *method)
7831 : {
7832 9 : if (!obj->isXML()) {
7833 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7834 : JSMSG_INCOMPATIBLE_METHOD,
7835 0 : js_XML_str, method, obj->getClass()->name);
7836 0 : return NULL;
7837 : }
7838 9 : return (JSXML *)obj->getPrivate();
7839 : }
7840 :
7841 : JSBool
7842 9 : js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
7843 : {
7844 : JSXML *xml, *list;
7845 :
7846 9 : xml = GetPrivate(cx, obj, "descendants internal method");
7847 9 : if (!xml)
7848 0 : return JS_FALSE;
7849 :
7850 9 : list = Descendants(cx, xml, id);
7851 9 : if (!list)
7852 0 : return JS_FALSE;
7853 9 : *vp = OBJECT_TO_JSVAL(list->object);
7854 9 : return JS_TRUE;
7855 : }
7856 :
7857 : JSBool
7858 0 : js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
7859 : {
7860 : JSXML *list;
7861 : uint32_t n;
7862 :
7863 0 : list = (JSXML *) listobj->getPrivate();
7864 0 : for (n = list->xml_kids.length; n != 0; --n)
7865 0 : DeleteListElement(cx, list, 0);
7866 :
7867 0 : return JS_TRUE;
7868 : }
7869 :
7870 : struct JSXMLFilter
7871 : {
7872 : HeapPtr<JSXML> list;
7873 : HeapPtr<JSXML> result;
7874 : HeapPtr<JSXML> kid;
7875 : JSXMLArrayCursor<JSXML> cursor;
7876 :
7877 54 : JSXMLFilter(JSXML *list, JSXMLArray<JSXML> *array)
7878 54 : : list(list), result(NULL), kid(NULL), cursor(array) {}
7879 :
7880 54 : ~JSXMLFilter() {}
7881 : };
7882 :
7883 : static void
7884 0 : xmlfilter_trace(JSTracer *trc, JSObject *obj)
7885 : {
7886 0 : JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7887 0 : if (!filter)
7888 0 : return;
7889 :
7890 0 : JS_ASSERT(filter->list);
7891 0 : MarkXML(trc, &filter->list, "list");
7892 0 : if (filter->result)
7893 0 : MarkXML(trc, &filter->result, "result");
7894 0 : if (filter->kid)
7895 0 : MarkXML(trc, &filter->kid, "kid");
7896 :
7897 : /*
7898 : * We do not need to trace the cursor as that would be done when
7899 : * tracing the filter->list.
7900 : */
7901 : }
7902 :
7903 : static void
7904 54 : xmlfilter_finalize(FreeOp *fop, JSObject *obj)
7905 : {
7906 54 : JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
7907 54 : if (!filter)
7908 0 : return;
7909 :
7910 54 : fop->delete_(filter);
7911 : }
7912 :
7913 : Class js_XMLFilterClass = {
7914 : "XMLFilter",
7915 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
7916 : JS_PropertyStub, /* addProperty */
7917 : JS_PropertyStub, /* delProperty */
7918 : JS_PropertyStub, /* getProperty */
7919 : JS_StrictPropertyStub, /* setProperty */
7920 : JS_EnumerateStub,
7921 : JS_ResolveStub,
7922 : JS_ConvertStub,
7923 : xmlfilter_finalize,
7924 : NULL, /* checkAccess */
7925 : NULL, /* call */
7926 : NULL, /* construct */
7927 : NULL, /* hasInstance */
7928 : xmlfilter_trace
7929 : };
7930 :
7931 : JSBool
7932 126 : js_StepXMLListFilter(JSContext *cx, JSBool initialized)
7933 : {
7934 : jsval *sp;
7935 : JSObject *obj, *filterobj, *resobj, *kidobj;
7936 : JSXML *xml, *list;
7937 : JSXMLFilter *filter;
7938 :
7939 126 : sp = cx->regs().sp;
7940 126 : if (!initialized) {
7941 : /*
7942 : * We haven't iterated yet, so initialize the filter based on the
7943 : * value stored in sp[-2].
7944 : */
7945 63 : if (!VALUE_IS_XML(sp[-2])) {
7946 9 : js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL);
7947 9 : return JS_FALSE;
7948 : }
7949 54 : obj = JSVAL_TO_OBJECT(sp[-2]);
7950 54 : xml = (JSXML *) obj->getPrivate();
7951 :
7952 54 : if (xml->xml_class == JSXML_CLASS_LIST) {
7953 9 : list = xml;
7954 : } else {
7955 45 : obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7956 45 : if (!obj)
7957 0 : return JS_FALSE;
7958 :
7959 : /*
7960 : * Root just-created obj. sp[-2] cannot be used yet for rooting
7961 : * as it may be the only root holding xml.
7962 : */
7963 45 : sp[-1] = OBJECT_TO_JSVAL(obj);
7964 45 : list = (JSXML *) obj->getPrivate();
7965 45 : if (!Append(cx, list, xml))
7966 0 : return JS_FALSE;
7967 : }
7968 :
7969 54 : JSObject *parent = GetGlobalForScopeChain(cx);
7970 54 : filterobj = NewObjectWithGivenProto(cx, &js_XMLFilterClass, NULL, parent);
7971 54 : if (!filterobj)
7972 0 : return JS_FALSE;
7973 :
7974 : /*
7975 : * Init all filter fields before setPrivate exposes it to
7976 : * xmlfilter_trace or xmlfilter_finalize.
7977 : */
7978 54 : filter = cx->new_<JSXMLFilter>(list, &list->xml_kids);
7979 54 : if (!filter)
7980 0 : return JS_FALSE;
7981 54 : filterobj->setPrivate(filter);
7982 :
7983 : /* Store filterobj to use in the later iterations. */
7984 54 : sp[-2] = OBJECT_TO_JSVAL(filterobj);
7985 :
7986 54 : resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
7987 54 : if (!resobj)
7988 0 : return JS_FALSE;
7989 :
7990 : /* This also roots resobj. */
7991 54 : filter->result = (JSXML *) resobj->getPrivate();
7992 : } else {
7993 : /* We have iterated at least once. */
7994 63 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
7995 63 : JS_ASSERT(JSVAL_TO_OBJECT(sp[-2])->getClass() == &js_XMLFilterClass);
7996 63 : filter = (JSXMLFilter *) JSVAL_TO_OBJECT(sp[-2])->getPrivate();
7997 63 : JS_ASSERT(filter->kid);
7998 :
7999 : /* Check if the filter expression wants to append the element. */
8000 99 : if (js_ValueToBoolean(sp[-1]) &&
8001 36 : !Append(cx, filter->result, filter->kid)) {
8002 0 : return JS_FALSE;
8003 : }
8004 : }
8005 :
8006 : /* Do the iteration. */
8007 117 : filter->kid = filter->cursor.getNext();
8008 117 : if (!filter->kid) {
8009 : /*
8010 : * Do not defer finishing the cursor until the next GC cycle to avoid
8011 : * accumulation of dead cursors associated with filter->list.
8012 : */
8013 54 : filter->cursor.disconnect();
8014 54 : JS_ASSERT(filter->result->object);
8015 54 : sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
8016 54 : kidobj = NULL;
8017 : } else {
8018 63 : kidobj = js_GetXMLObject(cx, filter->kid);
8019 63 : if (!kidobj)
8020 0 : return JS_FALSE;
8021 : }
8022 :
8023 : /* Null as kidobj at sp[-1] signals filter termination. */
8024 117 : sp[-1] = OBJECT_TO_JSVAL(kidobj);
8025 117 : return JS_TRUE;
8026 : }
8027 :
8028 : JSObject *
8029 595 : js_ValueToXMLObject(JSContext *cx, const Value &v)
8030 : {
8031 595 : return ToXML(cx, v);
8032 : }
8033 :
8034 : JSObject *
8035 18 : js_ValueToXMLListObject(JSContext *cx, const Value &v)
8036 : {
8037 18 : return ToXMLList(cx, v);
8038 : }
8039 :
8040 : JSObject *
8041 0 : js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name,
8042 : JSString *value)
8043 : {
8044 : unsigned flags;
8045 : JSObject *obj;
8046 : JSXML *xml;
8047 : JSObject *qn;
8048 :
8049 0 : if (!GetXMLSettingFlags(cx, &flags))
8050 0 : return NULL;
8051 :
8052 0 : if ((xml_class == JSXML_CLASS_COMMENT &&
8053 : (flags & XSF_IGNORE_COMMENTS)) ||
8054 : (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION &&
8055 : (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) {
8056 0 : return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
8057 : }
8058 :
8059 0 : obj = js_NewXMLObject(cx, xml_class);
8060 0 : if (!obj)
8061 0 : return NULL;
8062 0 : xml = (JSXML *) obj->getPrivate();
8063 0 : if (name) {
8064 0 : JSAtom *atomName = js_AtomizeString(cx, name);
8065 0 : if (!atomName)
8066 0 : return NULL;
8067 0 : qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, atomName);
8068 0 : if (!qn)
8069 0 : return NULL;
8070 0 : xml->name = qn;
8071 : }
8072 0 : xml->xml_value = value;
8073 0 : return obj;
8074 : }
8075 :
8076 : JSString *
8077 0 : js_MakeXMLCDATAString(JSContext *cx, JSString *str)
8078 : {
8079 0 : StringBuffer sb(cx);
8080 0 : return MakeXMLCDATAString(cx, sb, str);
8081 : }
8082 :
8083 : JSString *
8084 0 : js_MakeXMLCommentString(JSContext *cx, JSString *str)
8085 : {
8086 0 : StringBuffer sb(cx);
8087 0 : return MakeXMLCommentString(cx, sb, str);
8088 : }
8089 :
8090 : JSString *
8091 0 : js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str)
8092 : {
8093 0 : StringBuffer sb(cx);
8094 0 : return MakeXMLPIString(cx, sb, name, str);
8095 : }
8096 :
8097 : #endif /* JS_HAS_XML_SUPPORT */
|