PageRenderTime 98ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/js/src/jsxml.cpp

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