📄 jsxml.c
字号:
cx->xmlSettingFlags &= ~flag; return JS_TRUE;}static JSPropertySpec xml_static_props[] = { {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_ignoreProcessingInstructions_str, XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, xml_setting_getter, NULL}, {0,0,0,0,0}};/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)/* * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. * This flag means a couple of things: * * - The top JSXML created for a parse tree must have an object owning it. * * - That the default namespace normally inherited from the temporary * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source * string must, in the case of a precompiled XML object tree, inherit via * ad-hoc code in ParseNodeToXML. * * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. */#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */#define IS_XML(str) \ (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))#define IS_XMLNS(str) \ (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))#define IS_XML_CHARS(chars) \ (JS_TOLOWER((chars)[0]) == 'x' && \ JS_TOLOWER((chars)[1]) == 'm' && \ JS_TOLOWER((chars)[2]) == 'l')#define HAS_NS_AFTER_XML(chars) \ (JS_TOLOWER((chars)[3]) == 'n' && \ JS_TOLOWER((chars)[4]) == 's')#define IS_XMLNS_CHARS(chars) \ (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))#define STARTS_WITH_XML(chars,length) \ (length >= 3 && IS_XML_CHARS(chars))static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";static JSXMLQName *ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, JSBool isAttributeName){ JSString *str, *uri, *prefix, *localName; size_t length, offset; const jschar *start, *limit, *colon; uint32 n; JSXMLNamespace *ns; JS_ASSERT(pn->pn_arity == PN_NULLARY); str = ATOM_TO_STRING(pn->pn_atom); length = JSSTRING_LENGTH(str); start = JSSTRING_CHARS(str); JS_ASSERT(length != 0 && *start != '@'); JS_ASSERT(length != 1 || *start != '*'); uri = cx->runtime->emptyString; limit = start + length; colon = js_strchr_limit(start, ':', limit); if (colon) { offset = PTRDIFF(colon, start, jschar); prefix = js_NewDependentString(cx, str, 0, offset, 0); if (!prefix) return NULL; if (STARTS_WITH_XML(start, offset)) { if (offset == 3) { uri = JS_InternString(cx, xml_namespace_str); if (!uri) return NULL; } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { uri = JS_InternString(cx, xmlns_namespace_str); if (!uri) return NULL; } else { uri = NULL; } } else { uri = NULL; n = inScopeNSes->length; while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { uri = ns->uri; break; } } } if (!uri) { js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, js_ValueToPrintableString(cx, STRING_TO_JSVAL(prefix))); return NULL; } localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); if (!localName) return NULL; } else { if (isAttributeName) { /* * An unprefixed attribute is not in any namespace, so set prefix * as well as uri to the empty string. */ prefix = uri; } else { /* * Loop from back to front looking for the closest declared default * namespace. */ n = inScopeNSes->length; while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); if (!ns->prefix || IS_EMPTY(ns->prefix)) { uri = ns->uri; break; } } prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; } localName = str; } return js_NewXMLQName(cx, uri, prefix, localName);}static JSString *ChompXMLWhitespace(JSContext *cx, JSString *str){ size_t length, newlength, offset; const jschar *cp, *start, *end; jschar c; length = JSSTRING_LENGTH(str); for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { c = *cp; if (!JS_ISXMLSPACE(c)) break; } while (end > cp) { c = end[-1]; if (!JS_ISXMLSPACE(c)) break; --end; } newlength = PTRDIFF(end, cp, jschar); if (newlength == length) return str; offset = PTRDIFF(cp, start, jschar); return js_NewDependentString(cx, str, offset, newlength, 0);}static JSXML *ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, uintN flags){ JSXML *xml, *kid, *attr, *attrj; JSString *str; uint32 length, n, i, j; JSParseNode *pn2, *pn3, *head, **pnp; JSXMLNamespace *ns; JSXMLQName *qn, *attrjqn; JSXMLClass xml_class; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_OVER_RECURSED); return NULL; }#define PN2X_SKIP_CHILD ((JSXML *) 1) /* * Cases return early to avoid common code that gets an outermost xml's * object, which protects GC-things owned by xml and its descendants from * garbage collection. */ xml = NULL; if (!js_EnterLocalRootScope(cx)) return NULL; switch (pn->pn_type) { case TOK_XMLELEM: length = inScopeNSes->length; pn2 = pn->pn_head; xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); if (!xml) goto fail; flags &= ~XSF_PRECOMPILED_ROOT; n = pn->pn_count; JS_ASSERT(n >= 2); n -= 2; if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) goto fail; i = 0; while ((pn2 = pn2->pn_next) != NULL) { if (!pn2->pn_next) { /* Don't append the end tag! */ JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); break; } if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && pn2->pn_type == TOK_XMLSPACE) { --n; continue; } kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); if (kid == PN2X_SKIP_CHILD) { --n; continue; } if (!kid) goto fail; /* Store kid in xml right away, to protect it from GC. */ XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); kid->parent = xml; ++i; /* XXX where is this documented in an XML spec, or in E4X? */ if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { str = ChompXMLWhitespace(cx, kid->xml_value); if (!str) goto fail; kid->xml_value = str; } } JS_ASSERT(i == n); if (n < pn->pn_count - 2) XMLArrayTrim(&xml->xml_kids); XMLARRAY_TRUNCATE(cx, inScopeNSes, length); break; case TOK_XMLLIST: xml = js_NewXML(cx, JSXML_CLASS_LIST); if (!xml) goto fail; n = pn->pn_count; if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) goto fail; i = 0; for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { /* * Always ignore insignificant whitespace in lists -- we shouldn't * condition this on an XML.ignoreWhitespace setting when the list * constructor is XMLList (note XML/XMLList unification hazard). */ if (pn2->pn_type == TOK_XMLSPACE) { --n; continue; } kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); if (kid == PN2X_SKIP_CHILD) { --n; continue; } if (!kid) goto fail; XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); ++i; } if (n < pn->pn_count) XMLArrayTrim(&xml->xml_kids); break; case TOK_XMLSTAGO: case TOK_XMLPTAGC: length = inScopeNSes->length; pn2 = pn->pn_head; JS_ASSERT(pn2->pn_type == TOK_XMLNAME); if (pn2->pn_arity == PN_LIST) goto syntax; xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); if (!xml) goto fail; /* First pass: check syntax and process namespace declarations. */ JS_ASSERT(pn->pn_count >= 1); n = pn->pn_count - 1; pnp = &pn2->pn_next; head = *pnp; while ((pn2 = *pnp) != NULL) { size_t length; const jschar *chars; if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) goto syntax; /* Enforce "Well-formedness constraint: Unique Att Spec". */ for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { if (pn3->pn_atom == pn2->pn_atom) { js_ReportCompileErrorNumber(cx, pn2, JSREPORT_PN | JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR, js_ValueToPrintableString(cx, ATOM_KEY(pn2->pn_atom))); goto fail; } } str = ATOM_TO_STRING(pn2->pn_atom); pn2 = pn2->pn_next; JS_ASSERT(pn2); if (pn2->pn_type != TOK_XMLATTR) goto syntax; length = JSSTRING_LENGTH(str); chars = JSSTRING_CHARS(str); if (length >= 5 && IS_XMLNS_CHARS(chars) && (length == 5 || chars[5] == ':')) { JSString *uri, *prefix; uri = ATOM_TO_STRING(pn2->pn_atom); if (length == 5) { /* 10.3.2.1. Step 6(h)(i)(1)(a). */ prefix = cx->runtime->emptyString; } else { prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); if (!prefix) goto fail; } /* * Once the new ns is appended to xml->xml_namespaces, it is * protected from GC by the object that owns xml -- which is * either xml->object if outermost, or the object owning xml's * oldest ancestor if !outermost. */ ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); if (!ns) goto fail; /* * Don't add a namespace that's already in scope. If someone * extracts a child property from its parent via [[Get]], then * we enforce the invariant, noted many times in ECMA-357, that * the child's namespaces form a possibly-improper superset of * its ancestors' namespaces. */ if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { goto fail; } } JS_ASSERT(n >= 2); n -= 2; *pnp = pn2->pn_next; /* XXXbe recycle pn2 */ continue; } pnp = &pn2->pn_next; } /* * If called from js_ParseNodeToXMLObject, emulate the effect of the * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to * the String Type" (ECMA-357 10.3.1). */ if (flags & XSF_PRECOMPILED_ROOT) { JS_ASSERT(length >= 1); ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, namespace_identity)); ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); if (!ns) goto fail; if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) goto fail; } XMLArrayTrim(&xml->xml_namespaces); /* Second pass: process tag name and attributes, using namespaces. */ pn2 = pn->pn_head; qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); if (!qn) goto fail; xml->name = qn; JS_ASSERT((n & 1) == 0); n >>= 1; if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) goto fail; for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); if (!qn) { xml->xml_attrs.length = i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -