--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3923,17 +3923,17 @@ nsDocument::GetChildArray(uint32_t* aChi
}
nsresult
nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
bool aNotify)
{
if (aKid->IsElement() && GetRootElement()) {
- NS_ERROR("Inserting element child when we already have one");
+ NS_WARNING("Inserting root element when we already have one");
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
}
return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
}
void
nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -233,17 +233,18 @@ nsHtml5Parser::Parse(const nsAString& aS
mTreeBuilder->StartPlainText();
mTokenizer->StartPlainText();
}
/*
* If you move the following line, be very careful not to cause
* WillBuildModel to be called before the document has had its
* script global object set.
*/
- mExecutor->WillBuildModel(eDTDMode_unknown);
+ rv = mExecutor->WillBuildModel(eDTDMode_unknown);
+ NS_ENSURE_SUCCESS(rv, rv);
}
// Return early if the parser has processed EOF
if (mExecutor->IsComplete()) {
return NS_OK;
}
if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
@@ -251,17 +252,17 @@ nsHtml5Parser::Parse(const nsAString& aS
NS_ASSERTION(!GetStreamParser(),
"Had stream parser but got document.close().");
if (mDocumentClosed) {
// already closed
return NS_OK;
}
mDocumentClosed = true;
if (!mBlocked && !mInDocumentWrite) {
- ParseUntilBlocked();
+ return ParseUntilBlocked();
}
return NS_OK;
}
// If we got this far, we are dealing with a document.write or
// document.writeln call--not document.close().
NS_ASSERTION(IsInsertionPointDefined(),
@@ -374,17 +375,18 @@ nsHtml5Parser::Parse(const nsAString& aS
if (inRootContext) {
mRootContextLineNumber = mTokenizer->getLineNumber();
} else {
mTokenizer->setLineNumber(lineNumberSave);
}
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush(); // Move ops to the executor
- mExecutor->FlushDocumentWrite(); // run the ops
+ rv = mExecutor->FlushDocumentWrite(); // run the ops
+ NS_ENSURE_SUCCESS(rv, rv);
// Flushing tree ops can cause all sorts of things.
// Return early if the parser got terminated.
if (mExecutor->IsComplete()) {
return NS_OK;
}
}
// Ignore suspension requests
}
@@ -433,17 +435,18 @@ nsHtml5Parser::Parse(const nsAString& aS
}
}
if (!mBlocked) { // buffer was tokenized to completion
NS_ASSERTION(!stackBuffer.hasMore(),
"Buffer wasn't tokenized to completion?");
// Scripting semantics require a forced tree builder flush here
mTreeBuilder->Flush(); // Move ops to the executor
- mExecutor->FlushDocumentWrite(); // run the ops
+ rv = mExecutor->FlushDocumentWrite(); // run the ops
+ NS_ENSURE_SUCCESS(rv, rv);
} else if (stackBuffer.hasMore()) {
// The buffer wasn't tokenized to completion. Tokenize the untokenized
// content in order to preload stuff. This content will be retokenized
// later for normal parsing.
if (!mDocWriteSpeculatorActive) {
mDocWriteSpeculatorActive = true;
if (!mDocWriteSpeculativeTreeBuilder) {
// Lazily initialize if uninitialized
@@ -591,44 +594,48 @@ bool
nsHtml5Parser::IsScriptCreated()
{
return !GetStreamParser();
}
/* End nsIParser */
// not from interface
-void
+nsresult
nsHtml5Parser::ParseUntilBlocked()
{
- if (mBlocked || mExecutor->IsComplete() || NS_FAILED(mExecutor->IsBroken())) {
- return;
+ nsresult rv = mExecutor->IsBroken();
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mBlocked || mExecutor->IsComplete()) {
+ return NS_OK;
}
NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
NS_ASSERTION(!mInDocumentWrite,
"ParseUntilBlocked entered while in doc.write!");
mDocWriteSpeculatorActive = false;
for (;;) {
if (!mFirstBuffer->hasMore()) {
if (mFirstBuffer == mLastBuffer) {
if (mExecutor->IsComplete()) {
// something like cache manisfests stopped the parse in mid-flight
- return;
+ return NS_OK;
}
if (mDocumentClosed) {
NS_ASSERTION(!GetStreamParser(),
"This should only happen with script-created parser.");
mTokenizer->eof();
mTreeBuilder->StreamEnded();
mTreeBuilder->Flush();
mExecutor->FlushDocumentWrite();
+ // The below call does memory cleanup, so call it even if the
+ // parser has been marked as broken.
mTokenizer->end();
- return;
+ return NS_OK;
}
// never release the last buffer.
NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
"Sentinel buffer had its indeces changed.");
if (GetStreamParser()) {
if (mReturnToStreamParserPermitted &&
!mExecutor->IsScriptExecuting()) {
mTreeBuilder->Flush();
@@ -640,44 +647,45 @@ nsHtml5Parser::ParseUntilBlocked()
} else {
// Script-created parser
mTreeBuilder->Flush();
// No need to flush the executor, because the executor is already
// in a flush
NS_ASSERTION(mExecutor->IsInFlushLoop(),
"How did we come here without being in the flush loop?");
}
- return; // no more data for now but expecting more
+ return NS_OK; // no more data for now but expecting more
}
mFirstBuffer = mFirstBuffer->next;
continue;
}
if (mBlocked || mExecutor->IsComplete()) {
- return;
+ return NS_OK;
}
// now we have a non-empty buffer
mFirstBuffer->adjust(mLastWasCR);
mLastWasCR = false;
if (mFirstBuffer->hasMore()) {
bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
if (inRootContext) {
mTokenizer->setLineNumber(mRootContextLineNumber);
}
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
if (inRootContext) {
mRootContextLineNumber = mTokenizer->getLineNumber();
}
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush();
- mExecutor->FlushDocumentWrite();
+ nsresult rv = mExecutor->FlushDocumentWrite();
+ NS_ENSURE_SUCCESS(rv, rv);
}
if (mBlocked) {
- return;
+ return NS_OK;
}
}
continue;
}
}
nsresult
nsHtml5Parser::Initialize(nsIDocument* aDoc,
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -256,17 +256,17 @@ class nsHtml5Parser : public nsIParser,
return nullptr;
}
return mStreamListener->GetDelegate();
}
/**
* Parse until pending data is exhausted or a script blocks the parser
*/
- void ParseUntilBlocked();
+ nsresult ParseUntilBlocked();
private:
virtual ~nsHtml5Parser();
// State variables
/**
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -788,17 +788,17 @@ nsHtml5StreamParser::WriteStreamBytes(co
uint32_t aCount,
uint32_t* aWriteCount)
{
NS_ASSERTION(IsParserThread(), "Wrong thread!");
// mLastBuffer should always point to a buffer of the size
// NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
if (!mLastBuffer) {
NS_WARNING("mLastBuffer should not be null!");
- MarkAsBroken();
+ MarkAsBroken(NS_ERROR_NULL_POINTER);
return NS_ERROR_NULL_POINTER;
}
if (mLastBuffer->getEnd() == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
nsHtml5OwningUTF16Buffer::FalliblyCreate(
NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
if (!newBuf) {
return NS_ERROR_OUT_OF_MEMORY;
@@ -894,17 +894,18 @@ nsHtml5StreamParser::OnStartRequest(nsIR
mTokenizer->StartPlainText();
}
/*
* If you move the following line, be very careful not to cause
* WillBuildModel to be called before the document has had its
* script global object set.
*/
- mExecutor->WillBuildModel(eDTDMode_unknown);
+ rv = mExecutor->WillBuildModel(eDTDMode_unknown);
+ NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
nsHtml5OwningUTF16Buffer::FalliblyCreate(
NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
if (!newBuf) {
// marks this stream parser as terminated,
// which prevents entry to code paths that
// would use mFirstBuffer or mLastBuffer.
@@ -999,18 +1000,19 @@ nsHtml5StreamParser::DoStopRequest()
if (IsTerminated()) {
return;
}
mStreamState = STREAM_ENDED;
if (!mUnicodeDecoder) {
uint32_t writeCount;
- if (NS_FAILED(FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
- MarkAsBroken();
+ nsresult rv;
+ if (NS_FAILED(rv = FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
+ MarkAsBroken(rv);
return;
}
} else if (mFeedChardet) {
mChardet->Done();
}
if (IsTerminatedOrInterrupted()) {
return;
@@ -1072,17 +1074,17 @@ nsHtml5StreamParser::DoDataAvailable(con
mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
mFeedChardet = !dontFeed;
}
rv = WriteStreamBytes(aBuffer, aLength, &writeCount);
} else {
rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
}
if (NS_FAILED(rv)) {
- MarkAsBroken();
+ MarkAsBroken(rv);
return;
}
NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
if (IsTerminatedOrInterrupted()) {
return;
}
@@ -1658,21 +1660,21 @@ nsHtml5StreamParser::TimerFlush()
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
}
}
void
-nsHtml5StreamParser::MarkAsBroken()
+nsHtml5StreamParser::MarkAsBroken(nsresult aRv)
{
NS_ASSERTION(IsParserThread(), "Wrong thread!");
mTokenizerMutex.AssertCurrentThreadOwns();
Terminate();
- mTreeBuilder->MarkAsBroken();
+ mTreeBuilder->MarkAsBroken(aRv);
mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(false);
NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -212,17 +212,17 @@ class nsHtml5StreamParser : public nsICh
#ifdef DEBUG
bool IsParserThread() {
bool ret;
mThread->IsOnCurrentThread(&ret);
return ret;
}
#endif
- void MarkAsBroken();
+ void MarkAsBroken(nsresult aRv);
/**
* Marks the stream parser as interrupted. If you ever add calls to this
* method, be sure to review Uninterrupt usage very, very carefully to
* avoid having a previous in-flight runnable cancel your Interrupt()
* call on the other thread too soon.
*/
void Interrupt()
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -913,24 +913,24 @@ void
nsHtml5TreeBuilder::DropHandles()
{
MOZ_ASSERT(!mBuilder, "Must not drop handles with builder.");
mOldHandles.Clear();
mHandlesUsed = 0;
}
void
-nsHtml5TreeBuilder::MarkAsBroken()
+nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv)
{
if (MOZ_UNLIKELY(mBuilder)) {
MOZ_ASSERT_UNREACHABLE("Must not call this with builder.");
return;
}
mOpQueue.Clear(); // Previous ops don't matter anymore
- mOpQueue.AppendElement()->Init(eTreeOpMarkAsBroken);
+ mOpQueue.AppendElement()->Init(aRv);
}
void
nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle)
{
MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
startTag(nsHtml5ElementName::ELT_TITLE,
nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -218,9 +218,9 @@
void errNoCheckUnclosedElementsOnStack();
void errEndTagDidNotMatchCurrentOpenElement(nsIAtom* aName, nsIAtom* aOther);
void errEndTagViolatesNestingRules(nsIAtom* aName);
void errEndWithUnclosedElements(nsIAtom* aName);
- void MarkAsBroken();
+ void MarkAsBroken(nsresult aRv);
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -407,17 +407,21 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
return;
}
// Not sure if this grip is still needed, but previously, the code
// gripped before calling ParseUntilBlocked();
nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip =
GetParser()->GetStreamParser();
// Now parse content left in the document.write() buffer queue if any.
// This may generate tree ops on its own or dequeue a speculation.
- GetParser()->ParseUntilBlocked();
+ nsresult rv = GetParser()->ParseUntilBlocked();
+ if (NS_FAILED(rv)) {
+ MarkAsBroken(rv);
+ return;
+ }
}
if (mOpQueue.IsEmpty()) {
// Avoid bothering the rest of the engine with a doc update if there's
// nothing to do.
return;
}
@@ -490,31 +494,34 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
#endif
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
return;
}
}
}
}
-void
+nsresult
nsHtml5TreeOpExecutor::FlushDocumentWrite()
{
+ nsresult rv = IsBroken();
+ NS_ENSURE_SUCCESS(rv, rv);
+
FlushSpeculativeLoads(); // Make sure speculative loads never start after the
// corresponding normal loads for the same URLs.
if (MOZ_UNLIKELY(!mParser)) {
// The parse has ended.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
- return;
+ return rv;
}
if (mFlushState != eNotFlushing) {
// XXX Can this happen? In case it can, let's avoid crashing.
- return;
+ return rv;
}
mFlushState = eInFlush;
// avoid crashing near EOF
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
nsRefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
@@ -537,38 +544,39 @@ nsHtml5TreeOpExecutor::FlushDocumentWrit
iter < end;
++iter) {
if (MOZ_UNLIKELY(!mParser)) {
// The previous tree op caused a call to nsIParser::Terminate().
break;
}
NS_ASSERTION(mFlushState == eInDocUpdate,
"Tried to perform tree op outside update batch.");
- nsresult rv = iter->Perform(this, &scriptElement);
+ rv = iter->Perform(this, &scriptElement);
if (NS_FAILED(rv)) {
MarkAsBroken(rv);
break;
}
}
mOpQueue.Clear();
EndDocUpdate();
mFlushState = eNotFlushing;
if (MOZ_UNLIKELY(!mParser)) {
// Ending the doc update caused a call to nsIParser::Terminate().
- return;
+ return rv;
}
if (scriptElement) {
// must be tail call when mFlushState is eNotFlushing
RunScript(scriptElement);
}
+ return rv;
}
// copied from HTML content sink
bool
nsHtml5TreeOpExecutor::IsScriptEnabled()
{
if (!mDocument || !mDocShell)
return true;
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -173,17 +173,17 @@ class nsHtml5TreeOpExecutor MOZ_FINAL :
virtual nsresult MarkAsBroken(nsresult aReason);
void StartLayout();
void FlushSpeculativeLoads();
void RunFlushLoop();
- void FlushDocumentWrite();
+ nsresult FlushDocumentWrite();
void MaybeSuspend();
void Start();
void NeedsCharsetSwitchTo(const char* aEncoding,
int32_t aSource,
uint32_t aLineNumber);
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -193,16 +193,20 @@ nsHtml5TreeOperation::AppendToDocument(n
MOZ_ASSERT(aBuilder);
MOZ_ASSERT(aBuilder->GetDocument() == aNode->OwnerDoc());
MOZ_ASSERT(aBuilder->IsInDocUpdate());
nsresult rv = NS_OK;
nsIDocument* doc = aBuilder->GetDocument();
uint32_t childCount = doc->GetChildCount();
rv = doc->AppendChildTo(aNode, false);
+ if (rv == NS_ERROR_DOM_HIERARCHY_REQUEST_ERR) {
+ aNode->SetParserHasNotified();
+ return NS_OK;
+ }
NS_ENSURE_SUCCESS(rv, rv);
aNode->SetParserHasNotified();
nsNodeUtils::ContentInserted(doc, aNode, childCount);
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"Someone forgot to block scripts");
if (aNode->IsElement()) {
nsContentUtils::AddScriptRunner(
@@ -722,18 +726,17 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
return AppendDoctypeToDocument(name, publicId, systemId, aBuilder);
}
case eTreeOpGetDocumentFragmentForTemplate: {
nsIContent* node = *(mOne.node);
*mTwo.node = GetDocumentFragmentForTemplate(node);
return NS_OK;
}
case eTreeOpMarkAsBroken: {
- aBuilder->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
- return NS_OK;
+ return mOne.result;
}
case eTreeOpRunScript: {
nsIContent* node = *(mOne.node);
nsAHtml5TreeBuilderState* snapshot = mTwo.state;
if (snapshot) {
aBuilder->InitializeDocWriteParserState(snapshot, mFour.integer);
}
*aScriptElement = node;
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -428,16 +428,25 @@ class nsHtml5TreeOperation {
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
"Op code must be uninitialized when initializing.");
NS_PRECONDITION(aNode, "Initialized tree op with null node.");
mOpCode = aOpCode;
mOne.node = static_cast<nsIContent**>(aNode);
mFour.integer = aInt;
}
+ inline void Init(nsresult aRv)
+ {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
+ NS_PRECONDITION(NS_FAILED(aRv), "Initialized tree op with non-failure.");
+ mOpCode = eTreeOpMarkAsBroken;
+ mOne.result = aRv;
+ }
+
inline void InitAddClass(nsIContentHandle* aNode, const char16_t* aClass)
{
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
"Op code must be uninitialized when initializing.");
NS_PRECONDITION(aNode, "Initialized tree op with null node.");
NS_PRECONDITION(aClass, "Initialized tree op with null string.");
// aClass must be a literal string that does not need freeing
mOpCode = eTreeOpAddClass;
@@ -480,17 +489,18 @@ class nsHtml5TreeOperation {
// Make the queue take items the size of pointer and make the op code
// decide how many operands it dequeues after it.
eHtml5TreeOperation mOpCode;
union {
nsIContent** node;
nsIAtom* atom;
nsHtml5HtmlAttributes* attributes;
nsHtml5DocumentMode mode;
- char16_t* unicharPtr;
+ char16_t* unicharPtr;
char* charPtr;
nsHtml5TreeOperationStringPair* stringPair;
nsAHtml5TreeBuilderState* state;
int32_t integer;
+ nsresult result;
} mOne, mTwo, mThree, mFour;
};
#endif // nsHtml5TreeOperation_h