diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 370d251aa769..aa2161d64849 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -85,6 +85,7 @@ GK_ATOM(allowevents, "allowevents") GK_ATOM(allownegativeassertions, "allownegativeassertions") GK_ATOM(allowuntrusted, "allowuntrusted") GK_ATOM(alt, "alt") +GK_ATOM(alternate, "alternate") GK_ATOM(always, "always") GK_ATOM(ancestor, "ancestor") GK_ATOM(ancestorOrSelf, "ancestor-or-self") diff --git a/content/base/src/nsParserUtils.cpp b/content/base/src/nsParserUtils.cpp index 802f81bc9212..8b79634f7ccd 100644 --- a/content/base/src/nsParserUtils.cpp +++ b/content/base/src/nsParserUtils.cpp @@ -40,6 +40,8 @@ #include "jsapi.h" #include "nsReadableUtils.h" #include "nsCRT.h" +#include "nsContentUtils.h" +#include "nsIParserService.h" #define SKIP_WHITESPACE(iter, end_iter) \ while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \ @@ -57,16 +59,14 @@ break PRBool -nsParserUtils::GetQuotedAttributeValue(const nsAString& aSource, - const nsAString& aAttribute, +nsParserUtils::GetQuotedAttributeValue(const nsString& aSource, nsIAtom *aName, nsAString& aValue) { - NS_ASSERTION(!aAttribute.IsEmpty(), "Empty attribute name cannot be searched for usefully"); aValue.Truncate(); - nsAString::const_iterator start, end; - aSource.BeginReading(start); - aSource.EndReading(end); - nsAString::const_iterator iter; + + const PRUnichar *start = aSource.get(); + const PRUnichar *end = start + aSource.Length(); + const PRUnichar *iter; while (start != end) { SKIP_WHITESPACE(start, end); @@ -74,7 +74,7 @@ nsParserUtils::GetQuotedAttributeValue(const nsAString& aSource, SKIP_ATTR_NAME(iter, end); // Remember the attr name. - const nsAString & attrName = Substring(start, iter); + const nsDependentSubstring & attrName = Substring(start, iter); // Now check whether this is a valid name="value" pair. start = iter; @@ -96,23 +96,60 @@ nsParserUtils::GetQuotedAttributeValue(const nsAString& aSource, ++start; // Point to the first char of the value. iter = start; - if (!FindCharInReadable(q, iter, end)) { + + while (iter != end && *iter != q) { + ++iter; + } + + if (iter == end) { // Oops, unterminated quoted string. break; } - + // At this point attrName holds the name of the "attribute" and // the value is between start and iter. - if (!attrName.Equals(aAttribute)) { - // Resume scanning after the end of the attribute value. - start = iter; - ++start; // To move past the quote char. - continue; + if (aName->Equals(attrName)) { + nsAString::iterator dest; + aValue.BeginWriting(dest); + + while (start != iter) { + if (*start == kLessThan) { + return PR_FALSE; + } + + if (*start == kAmpersand) { + nsIParserService* parserService = nsContentUtils::GetParserService(); + NS_ENSURE_TRUE(parserService, PR_FALSE); + + const PRUnichar *pos = start; + + // Point to first character after the ampersand. + ++start; + + // We rely on the fact that if this is a valid character reference, + // dest will be a buffer with at least two PRUnichars (starting with + // &#), so enough to contain any UTF-16 character. + PRUint32 count = + parserService->DecodeEntity(start, iter, &pos, dest.get()); + NS_ENSURE_TRUE(count > 0, PR_FALSE); + + start = pos; + dest.advance(count); + } + else { + *dest = *start; + ++start; + ++dest; + } + } + + return PR_TRUE; } - aValue = Substring(start, iter); - return PR_TRUE; + // Resume scanning after the end of the attribute value. + start = iter; + ++start; // To move past the quote char. } return PR_FALSE; diff --git a/content/base/src/nsParserUtils.h b/content/base/src/nsParserUtils.h index 4f8228389c77..a2dfbd9d29e2 100644 --- a/content/base/src/nsParserUtils.h +++ b/content/base/src/nsParserUtils.h @@ -39,12 +39,23 @@ #define nsParserUtils_h__ #include "nsString.h" +class nsIAtom; class nsParserUtils { public: + /** + * This will parse aSource, to extract the value of the pseudo attribute + * with the name specified in aName. See + * http://www.w3.org/TR/xml-stylesheet/#NT-StyleSheetPI for the specification + * which is used to parse aSource. + * + * @param aSource the string to parse + * @param aName the name of the attribute to get the value for + * @param aValue [out] the value for the attribute with name specified in + * aAttribute. Empty if the attribute isn't present. + */ static PRBool - GetQuotedAttributeValue(const nsAString& aSource, - const nsAString& aAttribute, + GetQuotedAttributeValue(const nsString& aSource, nsIAtom *aName, nsAString& aValue); static PRBool diff --git a/content/xml/content/src/nsXMLProcessingInstruction.cpp b/content/xml/content/src/nsXMLProcessingInstruction.cpp index 800ef59652e7..4ae05c2012f5 100644 --- a/content/xml/content/src/nsXMLProcessingInstruction.cpp +++ b/content/xml/content/src/nsXMLProcessingInstruction.cpp @@ -122,13 +122,12 @@ nsXMLProcessingInstruction::GetData(nsAString& aData) } PRBool -nsXMLProcessingInstruction::GetAttrValue(const nsAString& aAttr, - nsAString& aValue) +nsXMLProcessingInstruction::GetAttrValue(nsIAtom *aName, nsAString& aValue) { nsAutoString data; GetData(data); - return nsParserUtils::GetQuotedAttributeValue(data, aAttr, aValue); + return nsParserUtils::GetQuotedAttributeValue(data, aName, aValue); } PRBool diff --git a/content/xml/content/src/nsXMLProcessingInstruction.h b/content/xml/content/src/nsXMLProcessingInstruction.h index 22c95ed42987..61e64b907ac8 100644 --- a/content/xml/content/src/nsXMLProcessingInstruction.h +++ b/content/xml/content/src/nsXMLProcessingInstruction.h @@ -73,7 +73,17 @@ public: #endif protected: - PRBool GetAttrValue(const nsAString& aAttr, nsAString& aValue); + /** + * This will parse the content of the PI, to extract the value of the pseudo + * attribute with the name specified in aName. See + * http://www.w3.org/TR/xml-stylesheet/#NT-StyleSheetPI for the specification + * which is used to parse the content of the PI. + * + * @param aName the name of the attribute to get the value for + * @param aValue [out] the value for the attribute with name specified in + * aAttribute. Empty if the attribute isn't present. + */ + PRBool GetAttrValue(nsIAtom *aName, nsAString& aValue); nsAutoString mTarget; }; diff --git a/content/xml/content/src/nsXMLStylesheetPI.cpp b/content/xml/content/src/nsXMLStylesheetPI.cpp index 1cec89e4fe6d..d557219a1a9a 100644 --- a/content/xml/content/src/nsXMLStylesheetPI.cpp +++ b/content/xml/content/src/nsXMLStylesheetPI.cpp @@ -148,11 +148,7 @@ nsXMLStylesheetPI::SetNodeValue(const nsAString& aNodeValue) NS_IMETHODIMP nsXMLStylesheetPI::GetCharset(nsAString& aCharset) { - if (!GetAttrValue(NS_LITERAL_STRING("charset"), aCharset)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; + return GetAttrValue(nsGkAtoms::charset, aCharset) ? NS_OK : NS_ERROR_FAILURE; } void @@ -163,7 +159,7 @@ nsXMLStylesheetPI::GetStyleSheetURL(PRBool* aIsInline, *aURI = nsnull; nsAutoString href; - GetAttrValue(NS_LITERAL_STRING("href"), href); + GetAttrValue(nsGkAtoms::href, href); if (href.IsEmpty()) { return; } @@ -197,31 +193,29 @@ nsXMLStylesheetPI::GetStyleSheetInfo(nsAString& aTitle, return; } - nsAutoString title, type, media, alternate; + nsAutoString data; + GetData(data); - GetAttrValue(NS_LITERAL_STRING("title"), title); - title.CompressWhitespace(); - aTitle.Assign(title); + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::title, aTitle); - GetAttrValue(NS_LITERAL_STRING("alternate"), alternate); + nsAutoString alternate; + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::alternate, alternate); // if alternate, does it have title? if (alternate.EqualsLiteral("yes")) { if (aTitle.IsEmpty()) { // alternates must have title return; - } else { - *aIsAlternate = PR_TRUE; } + + *aIsAlternate = PR_TRUE; } - GetAttrValue(NS_LITERAL_STRING("media"), media); - aMedia.Assign(media); - ToLowerCase(aMedia); // case sensitivity? + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::media, aMedia); - GetAttrValue(NS_LITERAL_STRING("type"), type); + nsAutoString type; + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::type, type); - nsAutoString mimeType; - nsAutoString notUsed; + nsAutoString mimeType, notUsed; nsParserUtils::SplitMimeType(type, mimeType, notUsed); if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) { aType.Assign(type); diff --git a/content/xml/document/src/nsXMLContentSink.cpp b/content/xml/document/src/nsXMLContentSink.cpp index 8b7b8b47f82c..36545931fb6a 100644 --- a/content/xml/document/src/nsXMLContentSink.cpp +++ b/content/xml/document/src/nsXMLContentSink.cpp @@ -1133,64 +1133,80 @@ nsXMLContentSink::HandleProcessingInstruction(const PRUnichar *aTarget, { FlushText(); - nsresult result = NS_OK; const nsDependentString target(aTarget); const nsDependentString data(aData); nsCOMPtr node; - result = NS_NewXMLProcessingInstruction(getter_AddRefs(node), - mNodeInfoManager, target, data); - if (NS_OK == result) { - nsCOMPtr ssle(do_QueryInterface(node)); + nsresult rv = NS_NewXMLProcessingInstruction(getter_AddRefs(node), + mNodeInfoManager, target, data); + NS_ENSURE_SUCCESS(rv, rv); - if (ssle) { - ssle->InitStyleLinkElement(mParser, PR_FALSE); - ssle->SetEnableUpdates(PR_FALSE); - mPrettyPrintXML = PR_FALSE; - } + nsCOMPtr ssle(do_QueryInterface(node)); + if (ssle) { + ssle->InitStyleLinkElement(mParser, PR_FALSE); + ssle->SetEnableUpdates(PR_FALSE); + mPrettyPrintXML = PR_FALSE; + } - result = AddContentAsLeaf(node); + rv = AddContentAsLeaf(node); + NS_ENSURE_SUCCESS(rv, rv); - if (ssle) { - ssle->SetEnableUpdates(PR_TRUE); - result = ssle->UpdateStyleSheet(nsnull, nsnull); + if (ssle) { + ssle->SetEnableUpdates(PR_TRUE); + rv = ssle->UpdateStyleSheet(nsnull, nsnull); - if (NS_FAILED(result)) { - if (result == NS_ERROR_HTMLPARSER_BLOCK && mParser) { - mParser->BlockParser(); - } - return result; + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_HTMLPARSER_BLOCK && mParser) { + mParser->BlockParser(); } - } - - // If it's not a CSS stylesheet PI... - nsAutoString type; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("type"), type); - if (mState == eXMLContentSinkState_InProlog && - target.EqualsLiteral("xml-stylesheet") && - !type.LowerCaseEqualsLiteral("text/css")) { - nsAutoString href, title, media, alternate; - - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("href"), href); - // If there was no href, we can't do anything with this PI - if (href.IsEmpty()) { - return NS_OK; - } - - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("title"), title); - title.CompressWhitespace(); - - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("media"), media); - ToLowerCase(media); - - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("alternate"), alternate); - - result = ProcessStyleLink(node, href, alternate.EqualsLiteral("yes"), - title, type, media); + return rv; } } - return result; + + // If it's not a CSS stylesheet PI... + nsAutoString type; + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::type, type); + + if (mState != eXMLContentSinkState_InProlog || + !target.EqualsLiteral("xml-stylesheet") || + type.LowerCaseEqualsLiteral("text/css")) { + return NS_OK; + } + + nsAutoString href, title, media; + PRBool isAlternate = PR_FALSE; + ParsePIData(data, href, title, media, isAlternate); + + // If there was no href, we can't do anything with this PI + if (href.IsEmpty()) { + return NS_OK; + } + + return ProcessStyleLink(node, href, isAlternate, title, type, media); +} + +/* static */ +void +nsXMLContentSink::ParsePIData(const nsString &aData, nsString &aHref, + nsString &aTitle, nsString &aMedia, + PRBool &aIsAlternate) +{ + nsParserUtils::GetQuotedAttributeValue(aData, nsGkAtoms::href, aHref); + + // If there was no href, we can't do anything with this PI + if (aHref.IsEmpty()) { + return; + } + + nsParserUtils::GetQuotedAttributeValue(aData, nsGkAtoms::title, aTitle); + + nsParserUtils::GetQuotedAttributeValue(aData, nsGkAtoms::media, aMedia); + + nsAutoString alternate; + nsParserUtils::GetQuotedAttributeValue(aData, nsGkAtoms::alternate, alternate); + + aIsAlternate = alternate.EqualsLiteral("yes"); } NS_IMETHODIMP diff --git a/content/xml/document/src/nsXMLContentSink.h b/content/xml/document/src/nsXMLContentSink.h index 8f0be02cedaf..5398d796b0d0 100644 --- a/content/xml/document/src/nsXMLContentSink.h +++ b/content/xml/document/src/nsXMLContentSink.h @@ -92,6 +92,10 @@ public: NS_IMETHOD OnDocumentCreated(nsIDOMDocument *aResultDocument); NS_IMETHOD OnTransformDone(nsresult aResult, nsIDOMDocument *aResultDocument); + static void ParsePIData(const nsString &aData, nsString &aHref, + nsString &aTitle, nsString &aMedia, + PRBool &aIsAlternate); + protected: void StartLayout(); diff --git a/content/xul/document/src/nsXULContentSink.cpp b/content/xul/document/src/nsXULContentSink.cpp index 033804cf1913..0b602dbebcff 100644 --- a/content/xul/document/src/nsXULContentSink.cpp +++ b/content/xul/document/src/nsXULContentSink.cpp @@ -903,10 +903,11 @@ XULContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget, tmp = targetStart; + nsresult rv; if (FindInReadable(NS_LITERAL_STRING("xul-overlay"), targetStart, targetEnd)) { // Load a XUL overlay. nsAutoString href; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("href"), href); + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::href, href); // If there was no href, we can't do // anything with this PI @@ -916,7 +917,7 @@ XULContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget, // Add the overlay to our list of overlays that need to be processed. nsCOMPtr url; - nsresult rv = NS_NewURI(getter_AddRefs(url), href, nsnull, mDocumentURL); + rv = NS_NewURI(getter_AddRefs(url), href, nsnull, mDocumentURL); if (NS_FAILED(rv)) { // XXX This is wrong, the error message could be out of memory // or something else equally bad, which we should propagate. @@ -924,50 +925,35 @@ XULContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget, return NS_OK; // The URL is bad, move along. Don't propagate for now. } - rv = mPrototype->AddOverlayReference(url); - if (NS_FAILED(rv)) return rv; - } - // If it's a stylesheet PI... - else { - targetStart = tmp; - if (FindInReadable(NS_LITERAL_STRING("xml-stylesheet"), targetStart, targetEnd)) { - nsAutoString href; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("href"), href); - - // If there was no href, we can't do - // anything with this PI - if (href.IsEmpty()) - return NS_OK; - - nsAutoString type; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("type"), type); - - nsAutoString title; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("title"), title); - - title.CompressWhitespace(); - - nsAutoString media; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("media"), media); - - ToLowerCase(media); - - nsAutoString alternate; - nsParserUtils::GetQuotedAttributeValue(data, NS_LITERAL_STRING("alternate"), alternate); - - nsresult result = ProcessStyleLink(nsnull /* XXX need a node here */, - href, alternate.EqualsLiteral("yes"), /* XXX ignore case? */ - title, type, media); - if (NS_FAILED(result)) { - if (result == NS_ERROR_HTMLPARSER_BLOCK && mParser) { - mParser->BlockParser(); - } - return result; // Important! A failure can indicate that the parser should block! - } - } + return mPrototype->AddOverlayReference(url); } - return NS_OK; + targetStart = tmp; + if (!FindInReadable(NS_LITERAL_STRING("xml-stylesheet"), targetStart, + targetEnd)) { + return NS_OK; + } + + // It's a stylesheet PI... + nsAutoString type; + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::type, type); + + nsAutoString href, title, media; + PRBool isAlternate = PR_FALSE; + nsXMLContentSink::ParsePIData(data, href, title, media, isAlternate); + + // If there was no href, we can't do anything with this PI + if (href.IsEmpty()) { + return NS_OK; + } + + // XXX need a node here + rv = ProcessStyleLink(nsnull , href, isAlternate, title, type, media); + if (rv == NS_ERROR_HTMLPARSER_BLOCK && mParser) { + mParser->BlockParser(); + } + + return rv; } diff --git a/parser/expat/lib/moz_extensions.c b/parser/expat/lib/moz_extensions.c index cce97d86090e..3c43e47c9292 100644 --- a/parser/expat/lib/moz_extensions.c +++ b/parser/expat/lib/moz_extensions.c @@ -39,13 +39,15 @@ #ifdef IS_LITTLE_ENDIAN -#define BYTE_TYPE(p) LITTLE2_BYTE_TYPE(&little2_encoding_ns, p) +#define PREFIX(ident) little2_ ## ident +#define BYTE_TYPE(p) LITTLE2_BYTE_TYPE(XmlGetUtf16InternalEncodingNS(), p) #define IS_NAME_CHAR_MINBPC(p) LITTLE2_IS_NAME_CHAR_MINBPC(0, p) #define IS_NMSTRT_CHAR_MINBPC(p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(0, p) #else -#define BYTE_TYPE(p) BIG2_BYTE_TYPE(&big2_encoding_ns, p) +#define PREFIX(ident) big2_ ## ident +#define BYTE_TYPE(p) BIG2_BYTE_TYPE(XmlGetUtf16InternalEncodingNS(), p) #define IS_NAME_CHAR_MINBPC(p) BIG2_IS_NAME_CHAR_MINBPC(0, p) #define IS_NMSTRT_CHAR_MINBPC(p) BIG2_IS_NMSTRT_CHAR_MINBPC(0, p) @@ -151,8 +153,43 @@ int MOZ_XMLIsNCNameChar(const char* ptr) } } +int MOZ_XMLTranslateEntity(const char* ptr, const char* end, const char** next, + XML_Char* result) +{ + const ENCODING* enc = XmlGetUtf16InternalEncodingNS(); + int tok = PREFIX(scanRef)(enc, ptr, end, next); + if (tok <= XML_TOK_INVALID) { + return 0; + } + + if (tok == XML_TOK_CHAR_REF) { + int n = XmlCharRefNumber(enc, ptr); + + // We could get away with just < 0, but better safe than sorry. + if (n <= 0) { + return 0; + } + + return XmlUtf16Encode(n, (unsigned short*)result); + } + + if (tok == XML_TOK_ENTITY_REF) { + // *next points to after the semicolon, so the entity ends at + // *next - enc->minBytesPerChar. + XML_Char ch = + (XML_Char)XmlPredefinedEntityName(enc, ptr, *next - enc->minBytesPerChar); + if (!ch) { + return 0; + } + + *result = ch; + return 1; + } + + return 0; +} + +#undef PREFIX #undef BYTE_TYPE #undef IS_NAME_CHAR_MINBPC #undef IS_NMSTRT_CHAR_MINBPC -#undef CHECK_NAME_CASES -#undef CHECK_NMSTRT_CASES diff --git a/parser/htmlparser/public/nsIParserService.h b/parser/htmlparser/public/nsIParserService.h index 40c7ba6ed3e9..04f5a97773a4 100644 --- a/parser/htmlparser/public/nsIParserService.h +++ b/parser/htmlparser/public/nsIParserService.h @@ -160,6 +160,25 @@ class nsIParserService : public nsISupports { const PRUnichar** aColon) = 0; virtual PRBool IsXMLLetter(PRUnichar aChar) = 0; virtual PRBool IsXMLNCNameChar(PRUnichar aChar) = 0; + + /** + * Decodes an entity into a UTF-16 character. If a ; is found between aStart + * and aEnd it will try to decode the entity and set aNext to point to the + * character after the ;. The resulting UTF-16 character will be written in + * aResult, so if the entity is a valid numeric entity there needs to be + * space for at least two PRUnichars. + * + * @param aStart pointer to the character after the ampersand. + * @param aEnd pointer to the position after the last character of the + * string. + * @param aNext [out] will be set to the character after the ; or null if + * the decoding was unsuccessful. + * @param aResult the buffer to write the resulting UTF-16 character in. + * @return the number of PRUnichars written to aResult. + */ + virtual PRUint32 DecodeEntity(const PRUnichar* aStart, const PRUnichar* aEnd, + const PRUnichar** aNext, + PRUnichar* aResult) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIParserService, NS_IPARSERSERVICE_IID) diff --git a/parser/htmlparser/src/nsParserService.h b/parser/htmlparser/src/nsParserService.h index 3e37f46e22e7..e0461514a174 100644 --- a/parser/htmlparser/src/nsParserService.h +++ b/parser/htmlparser/src/nsParserService.h @@ -44,6 +44,8 @@ extern "C" int MOZ_XMLIsLetter(const char* ptr); extern "C" int MOZ_XMLIsNCNameChar(const char* ptr); +extern "C" PRBool MOZ_XMLTranslateEntity(const char* ptr, const char* end, + const char** next, PRUnichar* result); class nsParserService : public nsIParserService { public: @@ -90,6 +92,15 @@ public: { return MOZ_XMLIsNCNameChar(NS_REINTERPRET_CAST(const char*, &aChar)); } + PRUint32 DecodeEntity(const PRUnichar* aStart, const PRUnichar* aEnd, + const PRUnichar** aNext, PRUnichar* aResult) + { + *aNext = nsnull; + return MOZ_XMLTranslateEntity(NS_REINTERPRET_CAST(const char*, aStart), + NS_REINTERPRET_CAST(const char*, aEnd), + NS_REINTERPRET_CAST(const char**, aNext), + aResult); + } protected: nsObserverEntry* GetEntry(const nsAString& aTopic);