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