diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 3f8740b3daf6..f8b959b03852 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -77,6 +77,7 @@ #include "nsIForm.h" #include "nsIFormControl.h" #include "nsHTMLAtoms.h" +#include "nsLayoutAtoms.h" static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; static NS_DEFINE_IID(kParserServiceCID, NS_PARSERSERVICE_CID); @@ -1597,9 +1598,8 @@ nsContentUtils::CheckQName(const nsAString& aQualifiedName, NS_ENSURE_TRUE(parserService, NS_ERROR_FAILURE); const PRUnichar *colon; - return parserService->IsValidQName(PromiseFlatString(aQualifiedName), - aNamespaceAware, &colon) ? - NS_OK : NS_ERROR_DOM_INVALID_CHARACTER_ERR; + return parserService->CheckQName(PromiseFlatString(aQualifiedName), + aNamespaceAware, &colon); } // static @@ -1614,11 +1614,9 @@ nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI, const nsAFlatString& qName = PromiseFlatString(aQualifiedName); const PRUnichar* colon; - if (!parserService->IsValidQName(qName, PR_TRUE, &colon)) { - return NS_ERROR_DOM_INVALID_CHARACTER_ERR; - } + nsresult rv = parserService->CheckQName(qName, PR_TRUE, &colon); + NS_ENSURE_SUCCESS(rv, rv); - nsresult rv; if (colon) { const PRUnichar* end; qName.EndReading(end); @@ -1632,8 +1630,29 @@ nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI, rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nsnull, aNamespaceURI, aNodeInfo); } + NS_ENSURE_SUCCESS(rv, rv); - return rv; + nsIAtom* prefix = (*aNodeInfo)->GetPrefixAtom(); + PRInt32 nsID = (*aNodeInfo)->NamespaceID(); + nsIAtom* nil = nsnull; + + // NAMESPACE_ERR: Raised if the qualifiedName is a malformed qualified name, + // if the qualifiedName has a prefix and the namespaceURI is null, if the + // qualifiedName has a prefix that is "xml" and the namespaceURI is different + // from "http://www.w3.org/XML/1998/namespace", if the qualifiedName or its + // prefix is "xmlns" and the namespaceURI is different from + // "http://www.w3.org/2000/xmlns/", or if the namespaceURI is + // "http://www.w3.org/2000/xmlns/" and neither the qualifiedName nor its + // prefix is "xmlns". + PRBool xmlPrefix = prefix == nsLayoutAtoms::xmlNameSpace; + PRBool xmlns = (*aNodeInfo)->Equals(nsLayoutAtoms::xmlnsNameSpace, nil) || + prefix == nsLayoutAtoms::xmlnsNameSpace; + + return (prefix && DOMStringIsNull(aNamespaceURI)) || + (xmlPrefix && nsID != kNameSpaceID_XML) || + (xmlns && nsID != kNameSpaceID_XMLNS) || + (nsID == kNameSpaceID_XMLNS && !xmlns) ? + NS_ERROR_DOM_NAMESPACE_ERR : NS_OK; } void diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 26a669be1ef7..804e1b8c1f54 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -121,6 +121,7 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID); #include "nsScriptEventManager.h" #include "nsIXPathEvaluatorInternal.h" #include "nsIElementFactory.h" +#include "nsIParserService.h" #ifdef DEBUG #include "nsICharsetAlias.h" @@ -362,8 +363,25 @@ nsDOMImplementation::CreateDocument(const nsAString& aNamespaceURI, nsresult rv; if (!aQualifiedName.IsEmpty()) { - rv = nsContentUtils::CheckQName(aQualifiedName); + nsIParserService *parserService = + nsContentUtils::GetParserServiceWeakRef(); + NS_ENSURE_TRUE(parserService, NS_ERROR_FAILURE); + + const nsAFlatString& qName = PromiseFlatString(aQualifiedName); + const PRUnichar *colon; + rv = parserService->CheckQName(qName, PR_TRUE, &colon); NS_ENSURE_SUCCESS(rv, rv); + + if (colon && + (DOMStringIsNull(aNamespaceURI) || + (Substring(qName.get(), colon).Equals(NS_LITERAL_STRING("xml")) && + !aNamespaceURI.Equals(NS_LITERAL_STRING("http://www.w3.org/XML/1998/namespace"))))) { + return NS_ERROR_DOM_NAMESPACE_ERR; + } + } + else if (DOMStringIsNull(aQualifiedName) && + !DOMStringIsNull(aNamespaceURI)) { + return NS_ERROR_DOM_NAMESPACE_ERR; } if (aDoctype) { diff --git a/expat/xmltok/moz_extensions.c b/expat/xmltok/moz_extensions.c index cccacaa730f3..892f3b4d1fa7 100644 --- a/expat/xmltok/moz_extensions.c +++ b/expat/xmltok/moz_extensions.c @@ -64,69 +64,82 @@ int MOZ_byte_type(unsigned char p) #endif -#define CHECK_NAME_CASES(ptr) \ - case BT_NONASCII: \ - if (!IS_NAME_CHAR_MINBPC(ptr)) \ - return 0; \ - case BT_NMSTRT: \ - case BT_HEX: \ - case BT_DIGIT: \ - case BT_NAME: \ - case BT_MINUS: +#define MOZ_EXPAT_VALID_QNAME (0) +#define MOZ_EXPAT_EMPTY_QNAME (1 << 0) +#define MOZ_EXPAT_INVALID_CHARACTER (1 << 1) +#define MOZ_EXPAT_MALFORMED (1 << 2) -#define CHECK_NMSTRT_CASES(ptr) \ - case BT_NONASCII: \ - if (!IS_NMSTRT_CHAR_MINBPC(ptr)) \ - return 0; \ - case BT_NMSTRT: \ - case BT_HEX: - -int MOZ_XMLIsValidQName(const char* ptr, const char* end, - int ns_aware, const char** colon) +int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware, + const char** colon) { + int result = MOZ_EXPAT_VALID_QNAME; + int nmstrt = 1; *colon = 0; - if (ptr == end) - return 0; - switch (BYTE_TYPE(ptr)) { - CHECK_NMSTRT_CASES(ptr) - ptr += 2; - break; - default: - return 0; + if (ptr == end) { + return MOZ_EXPAT_EMPTY_QNAME; } - while (ptr != end) { + do { switch (BYTE_TYPE(ptr)) { case BT_COLON: if (ns_aware) { - if (*colon != 0) - return 0; + if (*colon != 0 || nmstrt || ptr + 2 == end) { + // We already encountered a colon or this is the first or the last + // character so the QName is malformed. + result |= MOZ_EXPAT_MALFORMED; + } *colon = ptr; - ptr += 2; - if (ptr == end) - return 0; - switch (BYTE_TYPE(ptr)) { - CHECK_NMSTRT_CASES(ptr) - break; - default: - return 0; + nmstrt = 1; + } + else if (nmstrt) { + // This is the first character so the QName is malformed. + result |= MOZ_EXPAT_MALFORMED; + nmstrt = 0; + } + break; + case BT_NONASCII: + if (nmstrt) { + if (!IS_NMSTRT_CHAR_MINBPC(ptr)) { + // If this is a valid name character the QName is malformed, + // otherwise it contains an invalid character. + result |= IS_NAME_CHAR_MINBPC(ptr) ? MOZ_EXPAT_MALFORMED : + MOZ_EXPAT_INVALID_CHARACTER; } } - ptr += 2; + else if (!IS_NAME_CHAR_MINBPC(ptr)) { + result |= MOZ_EXPAT_INVALID_CHARACTER; + } + nmstrt = 0; break; - CHECK_NAME_CASES(ptr) - ptr += 2; + case BT_NMSTRT: + case BT_HEX: + nmstrt = 0; + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (nmstrt) { + result |= MOZ_EXPAT_MALFORMED; + nmstrt = 0; + } break; default: - return 0; + result |= MOZ_EXPAT_INVALID_CHARACTER; + nmstrt = 0; } - } - return 1; + ptr += 2; + } while (ptr != end); + return result; } int MOZ_XMLIsLetter(const char* ptr) { switch (BYTE_TYPE(ptr)) { - CHECK_NMSTRT_CASES(ptr) + case BT_NONASCII: + if (!IS_NMSTRT_CHAR_MINBPC(ptr)) { + return 0; + } + case BT_NMSTRT: + case BT_HEX: return 1; default: return 0; @@ -136,7 +149,15 @@ int MOZ_XMLIsLetter(const char* ptr) int MOZ_XMLIsNCNameChar(const char* ptr) { switch (BYTE_TYPE(ptr)) { - CHECK_NAME_CASES(ptr) + case BT_NONASCII: + if (!IS_NAME_CHAR_MINBPC(ptr)) { + return 0; + } + case BT_NMSTRT: + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: return 1; default: return 0; diff --git a/extensions/transformiix/source/xml/XMLUtils.h b/extensions/transformiix/source/xml/XMLUtils.h index 659ead7fa735..849cb551b4ca 100644 --- a/extensions/transformiix/source/xml/XMLUtils.h +++ b/extensions/transformiix/source/xml/XMLUtils.h @@ -102,8 +102,8 @@ public: }; #ifdef TX_EXE -extern "C" int MOZ_XMLIsValidQName(const char* ptr, const char* end, - int ns_aware, const char** colon); +extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, + int ns_aware, const char** colon); extern "C" int MOZ_XMLIsLetter(const char* ptr); extern "C" int MOZ_XMLIsNCNameChar(const char* ptr); #else @@ -148,15 +148,15 @@ public: aQName.EndReading(end); const char *colonPtr; - PRBool valid = MOZ_XMLIsValidQName(NS_REINTERPRET_CAST(const char*, - aQName.get()), - NS_REINTERPRET_CAST(const char*, - end), - PR_TRUE, &colonPtr); + int result = MOZ_XMLCheckQName(NS_REINTERPRET_CAST(const char*, + aQName.get()), + NS_REINTERPRET_CAST(const char*, + end), + PR_TRUE, &colonPtr); *aColon = NS_REINTERPRET_CAST(const PRUnichar*, colonPtr); - return valid; + return result == 0; #else - return gTxParserService->IsValidQName(aQName, PR_TRUE, aColon); + return NS_SUCCEEDED(gTxParserService->CheckQName(aQName, PR_TRUE, aColon)); #endif } diff --git a/htmlparser/public/nsIParserService.h b/htmlparser/public/nsIParserService.h index 7900a419a037..3f2ed8e6d6b4 100644 --- a/htmlparser/public/nsIParserService.h +++ b/htmlparser/public/nsIParserService.h @@ -103,7 +103,7 @@ class nsIParserService : public nsISupports { NS_IMETHOD GetTopicObservers(const nsAString& aTopic, nsIObserverEntry** aEntry) = 0; - virtual PRBool IsValidQName(const nsASingleFragmentString& aQName, + virtual nsresult CheckQName(const nsASingleFragmentString& aQName, PRBool aNamespaceAware, const PRUnichar** aColon) = 0; virtual PRBool IsXMLLetter(PRUnichar aChar) = 0; diff --git a/htmlparser/src/nsParserService.cpp b/htmlparser/src/nsParserService.cpp index 0bc4ca2b549f..bba7c6b9513e 100644 --- a/htmlparser/src/nsParserService.cpp +++ b/htmlparser/src/nsParserService.cpp @@ -19,6 +19,7 @@ * */ +#include "nsDOMError.h" #include "nsIAtom.h" #include "nsParserService.h" #include "nsHTMLEntities.h" @@ -182,6 +183,32 @@ nsParserService::GetTopicObservers(const nsAString& aTopic, return result; } +nsresult +nsParserService::CheckQName(const nsASingleFragmentString& aQName, + PRBool aNamespaceAware, + const PRUnichar** aColon) +{ + const char* colon; + const PRUnichar *begin, *end; + aQName.BeginReading(begin); + aQName.EndReading(end); + int result = MOZ_XMLCheckQName(NS_REINTERPRET_CAST(const char*, begin), + NS_REINTERPRET_CAST(const char*, end), + aNamespaceAware, &colon); + *aColon = NS_REINTERPRET_CAST(const PRUnichar*, colon); + + if (result == 0) { + return NS_OK; + } + + // MOZ_EXPAT_INVALID_CHARACTER + if (result & (1 << 1)) { + return NS_ERROR_DOM_INVALID_CHARACTER_ERR; + } + + return NS_ERROR_DOM_NAMESPACE_ERR; +} + class nsMatchesTopic : public nsDequeFunctor{ const nsAString& mString; public: diff --git a/htmlparser/src/nsParserService.h b/htmlparser/src/nsParserService.h index a82cb6179c2f..79d1cf3edcdd 100644 --- a/htmlparser/src/nsParserService.h +++ b/htmlparser/src/nsParserService.h @@ -25,8 +25,8 @@ #include "nsDTDUtils.h" #include "nsVoidArray.h" -extern "C" int MOZ_XMLIsValidQName(const char* ptr, const char* end, - int ns_aware, const char** colon); +extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, + int ns_aware, const char** colon); extern "C" int MOZ_XMLIsLetter(const char* ptr); extern "C" int MOZ_XMLIsNCNameChar(const char* ptr); @@ -63,20 +63,9 @@ public: NS_IMETHOD GetTopicObservers(const nsAString& aTopic, nsIObserverEntry** aEntry); - PRBool IsValidQName(const nsASingleFragmentString& aQName, - PRBool aNamespaceAware, - const PRUnichar** aColon) - { - const char* colon; - const PRUnichar *begin, *end; - aQName.BeginReading(begin); - aQName.EndReading(end); - int result = MOZ_XMLIsValidQName(NS_REINTERPRET_CAST(const char*, begin), - NS_REINTERPRET_CAST(const char*, end), - aNamespaceAware, &colon); - *aColon = NS_REINTERPRET_CAST(const PRUnichar*, colon); - return !!result; - } + nsresult CheckQName(const nsASingleFragmentString& aQName, + PRBool aNamespaceAware, const PRUnichar** aColon); + PRBool IsXMLLetter(PRUnichar aChar) { return MOZ_XMLIsLetter(NS_REINTERPRET_CAST(const char*, &aChar)); diff --git a/parser/expat/lib/moz_extensions.c b/parser/expat/lib/moz_extensions.c index cccacaa730f3..892f3b4d1fa7 100644 --- a/parser/expat/lib/moz_extensions.c +++ b/parser/expat/lib/moz_extensions.c @@ -64,69 +64,82 @@ int MOZ_byte_type(unsigned char p) #endif -#define CHECK_NAME_CASES(ptr) \ - case BT_NONASCII: \ - if (!IS_NAME_CHAR_MINBPC(ptr)) \ - return 0; \ - case BT_NMSTRT: \ - case BT_HEX: \ - case BT_DIGIT: \ - case BT_NAME: \ - case BT_MINUS: +#define MOZ_EXPAT_VALID_QNAME (0) +#define MOZ_EXPAT_EMPTY_QNAME (1 << 0) +#define MOZ_EXPAT_INVALID_CHARACTER (1 << 1) +#define MOZ_EXPAT_MALFORMED (1 << 2) -#define CHECK_NMSTRT_CASES(ptr) \ - case BT_NONASCII: \ - if (!IS_NMSTRT_CHAR_MINBPC(ptr)) \ - return 0; \ - case BT_NMSTRT: \ - case BT_HEX: - -int MOZ_XMLIsValidQName(const char* ptr, const char* end, - int ns_aware, const char** colon) +int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware, + const char** colon) { + int result = MOZ_EXPAT_VALID_QNAME; + int nmstrt = 1; *colon = 0; - if (ptr == end) - return 0; - switch (BYTE_TYPE(ptr)) { - CHECK_NMSTRT_CASES(ptr) - ptr += 2; - break; - default: - return 0; + if (ptr == end) { + return MOZ_EXPAT_EMPTY_QNAME; } - while (ptr != end) { + do { switch (BYTE_TYPE(ptr)) { case BT_COLON: if (ns_aware) { - if (*colon != 0) - return 0; + if (*colon != 0 || nmstrt || ptr + 2 == end) { + // We already encountered a colon or this is the first or the last + // character so the QName is malformed. + result |= MOZ_EXPAT_MALFORMED; + } *colon = ptr; - ptr += 2; - if (ptr == end) - return 0; - switch (BYTE_TYPE(ptr)) { - CHECK_NMSTRT_CASES(ptr) - break; - default: - return 0; + nmstrt = 1; + } + else if (nmstrt) { + // This is the first character so the QName is malformed. + result |= MOZ_EXPAT_MALFORMED; + nmstrt = 0; + } + break; + case BT_NONASCII: + if (nmstrt) { + if (!IS_NMSTRT_CHAR_MINBPC(ptr)) { + // If this is a valid name character the QName is malformed, + // otherwise it contains an invalid character. + result |= IS_NAME_CHAR_MINBPC(ptr) ? MOZ_EXPAT_MALFORMED : + MOZ_EXPAT_INVALID_CHARACTER; } } - ptr += 2; + else if (!IS_NAME_CHAR_MINBPC(ptr)) { + result |= MOZ_EXPAT_INVALID_CHARACTER; + } + nmstrt = 0; break; - CHECK_NAME_CASES(ptr) - ptr += 2; + case BT_NMSTRT: + case BT_HEX: + nmstrt = 0; + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (nmstrt) { + result |= MOZ_EXPAT_MALFORMED; + nmstrt = 0; + } break; default: - return 0; + result |= MOZ_EXPAT_INVALID_CHARACTER; + nmstrt = 0; } - } - return 1; + ptr += 2; + } while (ptr != end); + return result; } int MOZ_XMLIsLetter(const char* ptr) { switch (BYTE_TYPE(ptr)) { - CHECK_NMSTRT_CASES(ptr) + case BT_NONASCII: + if (!IS_NMSTRT_CHAR_MINBPC(ptr)) { + return 0; + } + case BT_NMSTRT: + case BT_HEX: return 1; default: return 0; @@ -136,7 +149,15 @@ int MOZ_XMLIsLetter(const char* ptr) int MOZ_XMLIsNCNameChar(const char* ptr) { switch (BYTE_TYPE(ptr)) { - CHECK_NAME_CASES(ptr) + case BT_NONASCII: + if (!IS_NAME_CHAR_MINBPC(ptr)) { + return 0; + } + case BT_NMSTRT: + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: return 1; default: return 0; diff --git a/parser/htmlparser/public/nsIParserService.h b/parser/htmlparser/public/nsIParserService.h index 7900a419a037..3f2ed8e6d6b4 100644 --- a/parser/htmlparser/public/nsIParserService.h +++ b/parser/htmlparser/public/nsIParserService.h @@ -103,7 +103,7 @@ class nsIParserService : public nsISupports { NS_IMETHOD GetTopicObservers(const nsAString& aTopic, nsIObserverEntry** aEntry) = 0; - virtual PRBool IsValidQName(const nsASingleFragmentString& aQName, + virtual nsresult CheckQName(const nsASingleFragmentString& aQName, PRBool aNamespaceAware, const PRUnichar** aColon) = 0; virtual PRBool IsXMLLetter(PRUnichar aChar) = 0; diff --git a/parser/htmlparser/src/nsParserService.cpp b/parser/htmlparser/src/nsParserService.cpp index 0bc4ca2b549f..bba7c6b9513e 100644 --- a/parser/htmlparser/src/nsParserService.cpp +++ b/parser/htmlparser/src/nsParserService.cpp @@ -19,6 +19,7 @@ * */ +#include "nsDOMError.h" #include "nsIAtom.h" #include "nsParserService.h" #include "nsHTMLEntities.h" @@ -182,6 +183,32 @@ nsParserService::GetTopicObservers(const nsAString& aTopic, return result; } +nsresult +nsParserService::CheckQName(const nsASingleFragmentString& aQName, + PRBool aNamespaceAware, + const PRUnichar** aColon) +{ + const char* colon; + const PRUnichar *begin, *end; + aQName.BeginReading(begin); + aQName.EndReading(end); + int result = MOZ_XMLCheckQName(NS_REINTERPRET_CAST(const char*, begin), + NS_REINTERPRET_CAST(const char*, end), + aNamespaceAware, &colon); + *aColon = NS_REINTERPRET_CAST(const PRUnichar*, colon); + + if (result == 0) { + return NS_OK; + } + + // MOZ_EXPAT_INVALID_CHARACTER + if (result & (1 << 1)) { + return NS_ERROR_DOM_INVALID_CHARACTER_ERR; + } + + return NS_ERROR_DOM_NAMESPACE_ERR; +} + class nsMatchesTopic : public nsDequeFunctor{ const nsAString& mString; public: diff --git a/parser/htmlparser/src/nsParserService.h b/parser/htmlparser/src/nsParserService.h index a82cb6179c2f..79d1cf3edcdd 100644 --- a/parser/htmlparser/src/nsParserService.h +++ b/parser/htmlparser/src/nsParserService.h @@ -25,8 +25,8 @@ #include "nsDTDUtils.h" #include "nsVoidArray.h" -extern "C" int MOZ_XMLIsValidQName(const char* ptr, const char* end, - int ns_aware, const char** colon); +extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, + int ns_aware, const char** colon); extern "C" int MOZ_XMLIsLetter(const char* ptr); extern "C" int MOZ_XMLIsNCNameChar(const char* ptr); @@ -63,20 +63,9 @@ public: NS_IMETHOD GetTopicObservers(const nsAString& aTopic, nsIObserverEntry** aEntry); - PRBool IsValidQName(const nsASingleFragmentString& aQName, - PRBool aNamespaceAware, - const PRUnichar** aColon) - { - const char* colon; - const PRUnichar *begin, *end; - aQName.BeginReading(begin); - aQName.EndReading(end); - int result = MOZ_XMLIsValidQName(NS_REINTERPRET_CAST(const char*, begin), - NS_REINTERPRET_CAST(const char*, end), - aNamespaceAware, &colon); - *aColon = NS_REINTERPRET_CAST(const PRUnichar*, colon); - return !!result; - } + nsresult CheckQName(const nsASingleFragmentString& aQName, + PRBool aNamespaceAware, const PRUnichar** aColon); + PRBool IsXMLLetter(PRUnichar aChar) { return MOZ_XMLIsLetter(NS_REINTERPRET_CAST(const char*, &aChar));