Fix for bug 286132 (xml-stylesheet PI doesn't handle href attribute as in specification). r=sicking, sr=bz.

This commit is contained in:
peterv%propagandism.org 2006-03-16 13:47:42 +00:00
parent 821df10a37
commit 3efee8b1f4
12 changed files with 261 additions and 136 deletions

View File

@ -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")

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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);

View File

@ -1133,64 +1133,80 @@ nsXMLContentSink::HandleProcessingInstruction(const PRUnichar *aTarget,
{
FlushText();
nsresult result = NS_OK;
const nsDependentString target(aTarget);
const nsDependentString data(aData);
nsCOMPtr<nsIContent> node;
result = NS_NewXMLProcessingInstruction(getter_AddRefs(node),
mNodeInfoManager, target, data);
if (NS_OK == result) {
nsCOMPtr<nsIStyleSheetLinkingElement> 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<nsIStyleSheetLinkingElement> 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

View File

@ -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();

View File

@ -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<nsIURI> 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;
}

View File

@ -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

View File

@ -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)

View File

@ -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);