Bug 732343 - Defend HTML document loading against extensions causing bogus use of the old HTML parser. r=smaug.

This commit is contained in:
Henri Sivonen 2012-03-31 17:10:34 +03:00
parent 11c200ab21
commit e1c9589023
6 changed files with 119 additions and 137 deletions

View File

@ -183,9 +183,10 @@ public:
/**
* Let the document know that we're starting to load data into it.
* @param aCommand The parser command
* @param aCommand The parser command. Must not be null.
* XXXbz It's odd to have that here.
* @param aChannel The channel the data will come from
* @param aChannel The channel the data will come from. The channel must be
* able to report its Content-Type.
* @param aLoadGroup The loadgroup this document should use from now on.
* Note that the document might not be the only thing using
* this loadgroup.
@ -204,6 +205,9 @@ public:
* @param aSink The content sink to use for the data. If this is null and
* the document needs a content sink, it will create one based
* on whatever it knows about the data it's going to load.
* This MUST be null if the underlying document is an HTML
* document. Even in the XML case, please don't add new calls
* with non-null sink.
*
* Once this has been called, the document will return false for
* MayStartLayout() until SetMayStartLayout(true) is called on it. Making

View File

@ -150,8 +150,6 @@ const PRInt32 kBackward = 1;
//#define DEBUG_charset
#define NS_USE_NEW_PLAIN_TEXT 1
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
PRUint32 nsHTMLDocument::gWyciwygSessionCnt = 0;
@ -560,51 +558,56 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
bool aReset,
nsIContentSink* aSink)
{
if (!aCommand) {
MOZ_NOT_REACHED("Command is mandatory");
return NS_ERROR_INVALID_POINTER;
}
if (aSink) {
MOZ_NOT_REACHED("Got a sink override. Should not happen for HTML doc.");
return NS_ERROR_INVALID_ARG;
}
if (!mIsRegularHTML) {
MOZ_NOT_REACHED("Must not set HTML doc to XHTML mode before load start.");
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsCAutoString contentType;
aChannel->GetContentType(contentType);
bool viewSource = aCommand && !nsCRT::strcmp(aCommand, "view-source");
bool plainText = (contentType.EqualsLiteral(TEXT_PLAIN) ||
bool view = !strcmp(aCommand, "view") ||
!strcmp(aCommand, "external-resource");
bool viewSource = !strcmp(aCommand, "view-source");
bool asData = !strcmp(aCommand, kLoadAsData);
if(!(view || viewSource || asData)) {
MOZ_NOT_REACHED("Bad parser command");
return NS_ERROR_INVALID_ARG;
}
bool html = contentType.EqualsLiteral(TEXT_HTML);
bool xhtml = !html && contentType.Equals("application/xhtml+xml");
bool plainText = !html && !xhtml && (contentType.EqualsLiteral(TEXT_PLAIN) ||
contentType.EqualsLiteral(TEXT_CSS) ||
contentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
contentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
contentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
contentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
contentType.EqualsLiteral(TEXT_JAVASCRIPT));
bool loadAsHtml5 = nsHtml5Module::sEnabled || viewSource || plainText;
if (!NS_USE_NEW_PLAIN_TEXT && !viewSource) {
plainText = false;
if (!(html || xhtml || plainText || viewSource)) {
MOZ_NOT_REACHED("Channel with bad content type.");
return NS_ERROR_INVALID_ARG;
}
NS_ASSERTION(!(plainText && aSink),
"Someone tries to load plain text into a custom sink.");
bool loadAsHtml5 = true;
if (aSink) {
loadAsHtml5 = false;
}
if (contentType.Equals("application/xhtml+xml") && !viewSource) {
// We're parsing XHTML as XML, remember that.
mIsRegularHTML = false;
mCompatMode = eCompatibility_FullStandards;
loadAsHtml5 = false;
}
#ifdef DEBUG
else {
NS_ASSERTION(mIsRegularHTML,
"Hey, someone forgot to reset mIsRegularHTML!!!");
}
#endif
if (loadAsHtml5 && !viewSource &&
(!(contentType.EqualsLiteral("text/html") || plainText) &&
aCommand && !nsCRT::strcmp(aCommand, "view"))) {
loadAsHtml5 = false;
if (!viewSource && xhtml) {
// We're parsing XHTML as XML, remember that.
mIsRegularHTML = false;
mCompatMode = eCompatibility_FullStandards;
loadAsHtml5 = false;
}
// TODO: Proper about:blank treatment is bug 543435
if (loadAsHtml5 && aCommand && !nsCRT::strcmp(aCommand, "view")) {
if (loadAsHtml5 && view) {
// mDocumentURI hasn't been set, yet, so get the URI from the channel
nsCOMPtr<nsIURI> uri;
aChannel->GetOriginalURI(getter_AddRefs(uri));
@ -622,14 +625,6 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
CSSLoader()->SetCompatibilityMode(mCompatMode);
bool needsParser = true;
if (aCommand)
{
if (!nsCRT::strcmp(aCommand, "view delayedContentLoad")) {
needsParser = false;
}
}
nsresult rv = nsDocument::StartDocumentLoad(aCommand,
aChannel, aLoadGroup,
aContainer,
@ -649,24 +644,22 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
nsCOMPtr<nsICachingChannel> cachingChan = do_QueryInterface(aChannel);
if (needsParser) {
if (loadAsHtml5) {
mParser = nsHtml5Module::NewHtml5Parser();
if (plainText) {
if (viewSource) {
mParser->MarkAsNotScriptCreated("view-source-plain");
} else {
mParser->MarkAsNotScriptCreated("plain-text");
}
} else if (viewSource && !contentType.EqualsLiteral("text/html")) {
mParser->MarkAsNotScriptCreated("view-source-xml");
if (loadAsHtml5) {
mParser = nsHtml5Module::NewHtml5Parser();
if (plainText) {
if (viewSource) {
mParser->MarkAsNotScriptCreated("view-source-plain");
} else {
mParser->MarkAsNotScriptCreated(aCommand);
mParser->MarkAsNotScriptCreated("plain-text");
}
} else if (viewSource && !html) {
mParser->MarkAsNotScriptCreated("view-source-xml");
} else {
mParser = do_CreateInstance(kCParserCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
mParser->MarkAsNotScriptCreated(aCommand);
}
} else {
mParser = do_CreateInstance(kCParserCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
PRInt32 textType = GET_BIDI_OPTION_TEXTTYPE(GetBidiOptions());
@ -852,55 +845,38 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
}
// Set the parser as the stream listener for the document loader...
if (mParser) {
rv = NS_OK;
nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener();
listener.forget(aDocListener);
rv = NS_OK;
nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener();
listener.forget(aDocListener);
#ifdef DEBUG_charset
printf(" charset = %s source %d\n",
charset.get(), charsetSource);
printf(" charset = %s source %d\n",
charset.get(), charsetSource);
#endif
mParser->SetDocumentCharset(parserCharset, parserCharsetSource);
mParser->SetCommand(aCommand);
mParser->SetDocumentCharset(parserCharset, parserCharsetSource);
mParser->SetCommand(aCommand);
// create the content sink
nsCOMPtr<nsIContentSink> sink;
if (aSink) {
NS_ASSERTION(!loadAsHtml5, "Panic: We are loading as HTML5 and someone tries to set an external sink!");
sink = aSink;
if (!IsHTML()) {
MOZ_ASSERT(!loadAsHtml5);
nsCOMPtr<nsIXMLContentSink> xmlsink;
NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri,
docShell, aChannel);
mParser->SetContentSink(xmlsink);
} else {
if (loadAsHtml5) {
nsHtml5Module::Initialize(mParser, this, uri, docShell, aChannel);
} else {
if (!IsHTML()) {
nsCOMPtr<nsIXMLContentSink> xmlsink;
rv = NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri,
docShell, aChannel);
sink = xmlsink;
} else {
if (loadAsHtml5) {
nsHtml5Module::Initialize(mParser, this, uri, docShell, aChannel);
sink = mParser->GetContentSink();
} else {
nsCOMPtr<nsIHTMLContentSink> htmlsink;
rv = NS_NewHTMLContentSink(getter_AddRefs(htmlsink), this, uri,
docShell, aChannel);
sink = htmlsink;
}
}
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(sink,
"null sink with successful result from factory method");
// about:blank *only*
nsCOMPtr<nsIHTMLContentSink> htmlsink;
NS_NewHTMLContentSink(getter_AddRefs(htmlsink), this, uri,
docShell, aChannel);
mParser->SetContentSink(htmlsink);
}
mParser->SetContentSink(sink);
// parser the content of the URI
mParser->Parse(uri, nsnull, (void *)this);
}
// parser the content of the URI
mParser->Parse(uri, nsnull, (void *)this);
return rv;
}
@ -1560,35 +1536,13 @@ nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
mSecurityInfo = securityInfo;
mParserAborted = false;
bool loadAsHtml5 = nsHtml5Module::sEnabled;
if (loadAsHtml5) {
mParser = nsHtml5Module::NewHtml5Parser();
rv = NS_OK;
} else {
mParser = do_CreateInstance(kCParserCID, &rv);
}
mParser = nsHtml5Module::NewHtml5Parser();
nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
rv = NS_OK;
// This will be propagated to the parser when someone actually calls write()
SetContentTypeInternal(contentType);
if (NS_SUCCEEDED(rv)) {
if (loadAsHtml5) {
nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
} else {
nsCOMPtr<nsIHTMLContentSink> sink;
rv = NS_NewHTMLContentSink(getter_AddRefs(sink), this, uri, shell,
channel);
if (NS_FAILED(rv)) {
// Don't use a parser without a content sink.
mParser = nsnull;
return rv;
}
mParser->SetContentSink(sink);
}
}
// Prepare the docshell and the document viewer for the impending
// out of band document.write()
shell->PrepareForNewContentModel();
@ -2271,21 +2225,17 @@ nsHTMLDocument::GenerateParserKey(void)
// The script loader provides us with the currently executing script element,
// which is guaranteed to be unique per script.
if (nsHtml5Module::sEnabled) {
nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
if (script && mParser && mParser->IsScriptCreated()) {
nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
if (creatorParser != mParser) {
// Make scripts that aren't inserted by the active parser of this document
// participate in the context of the script that document.open()ed
// this document.
return nsnull;
}
nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
if (script && mParser && mParser->IsScriptCreated()) {
nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
if (creatorParser != mParser) {
// Make scripts that aren't inserted by the active parser of this document
// participate in the context of the script that document.open()ed
// this document.
return nsnull;
}
return script;
} else {
return mScriptLoader->GetCurrentScript();
}
return script;
}
/* attribute DOMString designMode; */

View File

@ -53,7 +53,6 @@
using namespace mozilla;
// static
bool nsHtml5Module::sEnabled = true;
bool nsHtml5Module::sOffMainThread = true;
nsIThread* nsHtml5Module::sStreamParserThread = nsnull;
nsIThread* nsHtml5Module::sMainThread = nsnull;

View File

@ -49,7 +49,6 @@ class nsHtml5Module
static already_AddRefed<nsIParser> NewHtml5Parser();
static nsresult Initialize(nsIParser* aParser, nsIDocument* aDoc, nsIURI* aURI, nsISupports* aContainer, nsIChannel* aChannel);
static nsIThread* GetStreamParserThread();
static bool sEnabled;
static bool sOffMainThread;
private:
#ifdef DEBUG

View File

@ -239,6 +239,7 @@ nsParser::Initialize(bool aConstructor)
NS_PARSER_FLAG_CAN_TOKENIZE;
mProcessingNetworkData = false;
mIsAboutBlank = false;
}
void
@ -408,6 +409,10 @@ nsParser::SetContentSink(nsIContentSink* aSink)
if (mSink) {
mSink->SetParser(this);
nsCOMPtr<nsIHTMLContentSink> htmlSink = do_QueryInterface(mSink);
if (htmlSink) {
mIsAboutBlank = true;
}
}
}
@ -1996,6 +2001,18 @@ nsParser::DetectMetaTag(const char* aBytes,
return false;
}
static NS_METHOD
NoOpParserWriteFunc(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
{
*writeCount = count;
return NS_OK;
}
typedef struct {
bool mNeedCharsetCheck;
nsParser* mParser;
@ -2088,6 +2105,18 @@ nsParser::OnDataAvailable(nsIRequest *request, nsISupports* aContext,
nsresult rv = NS_OK;
if (mIsAboutBlank) {
MOZ_NOT_REACHED("Must not get OnDataAvailable for about:blank");
// ... but if an extension tries to feed us data for about:blank in a
// release build, silently ignore the data.
PRUint32 totalRead;
rv = pIStream->ReadSegments(NoOpParserWriteFunc,
nsnull,
aLength,
&totalRead);
return rv;
}
CParserContext *theContext = mParserContext;
while (theContext && theContext->mRequest != request) {

View File

@ -454,6 +454,7 @@ protected:
nsCString mCommandStr;
bool mProcessingNetworkData;
bool mIsAboutBlank;
static nsICharsetConverterManager* sCharsetConverterManager;
};