Bug 1176681 - Make character buffer allocations in the HTML5 tree builder fallible. r=wchen.

This commit is contained in:
Henri Sivonen 2015-08-25 18:05:46 +03:00
parent e5447e2f4a
commit 55fa40f5a5
4 changed files with 91 additions and 19 deletions

View File

@ -374,6 +374,9 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
}
mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
return mExecutor->MarkAsBroken(rv);
}
if (inRootContext) {
mRootContextLineNumber = mTokenizer->getLineNumber();
@ -485,6 +488,10 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
}
mDocWriteSpeculativeLastWasCR =
mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
nsresult rv;
if (NS_FAILED((rv = mDocWriteSpeculativeTreeBuilder->IsBroken()))) {
return mExecutor->MarkAsBroken(rv);
}
}
}
@ -625,7 +632,11 @@ nsHtml5Parser::ParseUntilBlocked()
"This should only happen with script-created parser.");
if (NS_SUCCEEDED((rv = mExecutor->IsBroken()))) {
mTokenizer->eof();
mTreeBuilder->StreamEnded();
if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
mExecutor->MarkAsBroken(rv);
} else {
mTreeBuilder->StreamEnded();
}
}
mTreeBuilder->Flush();
mExecutor->FlushDocumentWrite();
@ -676,12 +687,16 @@ nsHtml5Parser::ParseUntilBlocked()
return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
}
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
nsresult rv;
if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
return mExecutor->MarkAsBroken(rv);
}
if (inRootContext) {
mRootContextLineNumber = mTokenizer->getLineNumber();
}
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush();
nsresult rv = mExecutor->FlushDocumentWrite();
rv = mExecutor->FlushDocumentWrite();
NS_ENSURE_SUCCESS(rv, rv);
}
if (mBlocked) {

View File

@ -1369,9 +1369,14 @@ nsHtml5StreamParser::ParseAvailableData()
}
if (NS_SUCCEEDED(mTreeBuilder->IsBroken())) {
mTokenizer->eof();
mTreeBuilder->StreamEnded();
if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
mTokenizer->EndViewSource();
nsresult rv;
if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
MarkAsBroken(rv);
} else {
mTreeBuilder->StreamEnded();
if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
mTokenizer->EndViewSource();
}
}
}
FlushTreeOpsAndDisarmTimer();
@ -1394,6 +1399,11 @@ nsHtml5StreamParser::ParseAvailableData()
return;
}
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
nsresult rv;
if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
MarkAsBroken(rv);
return;
}
// At this point, internalEncodingDeclaration() may have called
// Terminate, but that never happens together with script.
// Can't assert that here, though, because it's possible that the main

View File

@ -501,7 +501,15 @@ nsHtml5TreeBuilder::insertFosterParentedCharacters(char16_t* aBuffer, int32_t aS
return;
}
char16_t* bufferCopy = new char16_t[aLength];
char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
if (!bufferCopy) {
// Just assigning mBroken instead of generating tree op. The caller
// of tokenizeBuffer() will call MarkAsBroken() as appropriate.
mBroken = NS_ERROR_OUT_OF_MEMORY;
requestSuspension();
return;
}
memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -553,7 +561,15 @@ nsHtml5TreeBuilder::appendCharacters(nsIContentHandle* aParent, char16_t* aBuffe
return;
}
char16_t* bufferCopy = new char16_t[aLength];
char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
if (!bufferCopy) {
// Just assigning mBroken instead of generating tree op. The caller
// of tokenizeBuffer() will call MarkAsBroken() as appropriate.
mBroken = NS_ERROR_OUT_OF_MEMORY;
requestSuspension();
return;
}
memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -605,7 +621,15 @@ nsHtml5TreeBuilder::appendComment(nsIContentHandle* aParent, char16_t* aBuffer,
return;
}
char16_t* bufferCopy = new char16_t[aLength];
char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
if (!bufferCopy) {
// Just assigning mBroken instead of generating tree op. The caller
// of tokenizeBuffer() will call MarkAsBroken() as appropriate.
mBroken = NS_ERROR_OUT_OF_MEMORY;
requestSuspension();
return;
}
memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -630,7 +654,15 @@ nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer, int32_t aStart, i
return;
}
char16_t* bufferCopy = new char16_t[aLength];
char16_t* bufferCopy = new (mozilla::fallible) char16_t[aLength];
if (!bufferCopy) {
// Just assigning mBroken instead of generating tree op. The caller
// of tokenizeBuffer() will call MarkAsBroken() as appropriate.
mBroken = NS_ERROR_OUT_OF_MEMORY;
requestSuspension();
return;
}
memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -984,20 +1016,30 @@ nsHtml5TreeBuilder::Flush(bool aDiscretionary)
MOZ_ASSERT_UNREACHABLE("Must never flush with builder.");
return false;
}
if (!aDiscretionary ||
!(charBufferLen &&
currentPtr >= 0 &&
stack[currentPtr]->isFosterParenting())) {
// Don't flush text on discretionary flushes if the current element on
// the stack is a foster-parenting element and there's pending text,
// because flushing in that case would make the tree shape dependent on
// where the flush points fall.
flushCharacters();
if (NS_SUCCEEDED(mBroken)) {
if (!aDiscretionary ||
!(charBufferLen &&
currentPtr >= 0 &&
stack[currentPtr]->isFosterParenting())) {
// Don't flush text on discretionary flushes if the current element on
// the stack is a foster-parenting element and there's pending text,
// because flushing in that case would make the tree shape dependent on
// where the flush points fall.
flushCharacters();
}
FlushLoads();
}
FlushLoads();
if (mOpSink) {
bool hasOps = !mOpQueue.IsEmpty();
if (hasOps) {
// If the builder is broken and mOpQueue is not empty, there must be
// one op and it must be eTreeOpMarkAsBroken.
if (NS_FAILED(mBroken)) {
MOZ_ASSERT(mOpQueue.Length() == 1,
"Tree builder is broken with a non-empty op queue whose length isn't 1.");
MOZ_ASSERT(mOpQueue[0].IsMarkAsBroken(),
"Tree builder is broken but the op in queue is not marked as broken.");
}
mOpSink->MoveOpsFrom(mOpQueue);
}
return hasOps;

View File

@ -476,6 +476,11 @@ class nsHtml5TreeOperation {
return mOpCode == eTreeOpRunScript;
}
inline bool IsMarkAsBroken()
{
return mOpCode == eTreeOpMarkAsBroken;
}
inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine)
{
NS_ASSERTION(IsRunScript(),