Bug 502568 - HTML5 parser should flush occasionally when loading pure text. r=bnewman.

This commit is contained in:
Henri Sivonen
2009-11-17 10:52:30 +02:00
parent b2bb00d735
commit b90f753473
15 changed files with 444 additions and 240 deletions

View File

@@ -54,6 +54,22 @@
static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
PRInt32 nsHtml5StreamParser::sTimerStartDelay = 200;
PRInt32 nsHtml5StreamParser::sTimerContinueDelay = 150;
PRInt32 nsHtml5StreamParser::sTimerInterval = 100;
// static
void
nsHtml5StreamParser::InitializeStatics()
{
nsContentUtils::AddIntPrefVarCache("html5.flushtimer.startdelay",
&sTimerStartDelay);
nsContentUtils::AddIntPrefVarCache("html5.flushtimer.continuedelay",
&sTimerContinueDelay);
nsContentUtils::AddIntPrefVarCache("html5.flushtimer.interval",
&sTimerInterval);
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5StreamParser)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5StreamParser)
@@ -67,6 +83,10 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5StreamParser)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
if (tmp->mFlushTimer) {
tmp->mFlushTimer->Cancel();
tmp->mFlushTimer = nsnull;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
tmp->mOwner = nsnull;
@@ -125,6 +145,7 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
, mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
, mThread(nsHtml5Module::GetStreamParserThread())
, mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
, mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mAtomTable.Init(); // we aren't checking for OOM anyway...
@@ -150,6 +171,10 @@ nsHtml5StreamParser::~nsHtml5StreamParser()
mTreeBuilder = nsnull;
mTokenizer = nsnull;
mOwner = nsnull;
if (mFlushTimer) {
mFlushTimer->Cancel();
mFlushTimer = nsnull;
}
}
void
@@ -483,6 +508,11 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
*/
mExecutor->WillBuildModel(eDTDMode_unknown);
mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
static_cast<void*> (this),
sTimerStartDelay,
nsITimer::TYPE_ONE_SHOT);
nsresult rv = NS_OK;
mReparseForbidden = PR_FALSE;
@@ -719,7 +749,6 @@ nsHtml5StreamParser::ParseAvailableData()
mFirstBuffer->setStart(0);
mFirstBuffer->setEnd(0);
}
mTreeBuilder->Flush();
return; // no more data for now but expecting more
case STREAM_ENDED:
if (mAtEOF) {
@@ -772,7 +801,6 @@ nsHtml5StreamParser::ParseAvailableData()
if (IsTerminatedOrInterrupted()) {
return;
}
mTreeBuilder->MaybeFlush();
}
continue;
}
@@ -873,6 +901,11 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
mTreeBuilder->SetOpSink(mExecutor->GetStage());
mExecutor->StartReadingFromStage();
mSpeculating = PR_FALSE;
mFlushTimer->Cancel(); // just in case
mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
static_cast<void*> (this),
sTimerContinueDelay,
nsITimer::TYPE_ONE_SHOT);
// Copy state over
mLastWasCR = aLastWasCR;
mTokenizer->loadState(aTokenizer);
@@ -890,6 +923,11 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
mTreeBuilder->SetOpSink(mExecutor->GetStage());
mExecutor->StartReadingFromStage();
mSpeculating = PR_FALSE;
mFlushTimer->Cancel(); // just in case
mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
static_cast<void*> (this),
sTimerContinueDelay,
nsITimer::TYPE_ONE_SHOT);
}
}
Uninterrupt();
@@ -912,6 +950,89 @@ nsHtml5StreamParser::ContinueAfterFailedCharsetSwitch()
Uninterrupt();
nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserContinuation(this);
if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch ParseAvailableData event");
}
NS_WARNING("Failed to dispatch nsHtml5StreamParserContinuation");
}
}
// Using a static, because the method name Notify is taken by the chardet
// callback.
void
nsHtml5StreamParser::TimerCallback(nsITimer* aTimer, void* aClosure)
{
(static_cast<nsHtml5StreamParser*> (aClosure))->PostTimerFlush();
}
void
nsHtml5StreamParser::TimerFlush()
{
NS_ASSERTION(IsParserThread(), "Wrong thread!");
mTokenizerMutex.AssertCurrentThreadOwns();
if (mSpeculating) {
return;
}
// we aren't speculating and we don't know when new data is
// going to arrive. Send data to the main thread.
// However, don't do 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.
if (mTreeBuilder->IsDiscretionaryFlushSafe()) {
mTreeBuilder->flushCharacters();
if (mTreeBuilder->Flush()) {
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
}
}
class nsHtml5StreamParserTimerFlusher : public nsRunnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
public:
nsHtml5StreamParserTimerFlusher(nsHtml5StreamParser* aStreamParser)
: mStreamParser(aStreamParser)
{}
NS_IMETHODIMP Run()
{
mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
mStreamParser->TimerFlush();
return NS_OK;
}
};
void
nsHtml5StreamParser::PostTimerFlush()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFlushTimer->Cancel(); // just in case
// The following line reads a mutex-protected variable without acquiring
// the mutex. This is OK, because failure to exit early here is harmless.
// The early exit here is merely an optimization. Note that parser thread
// may have set mSpeculating to true where it previously was false--not
// the other way round. mSpeculating is set to false only on the main thread.
if (mSpeculating) {
// No need for timer flushes when speculating
return;
}
// Schedule the next timer shot
mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
static_cast<void*> (this),
sTimerInterval,
nsITimer::TYPE_ONE_SHOT);
// TODO: (If mDocument isn't in the frontmost tab or If the user isn't
// interacting with the browser) and this isn't every nth timer flush, return
nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserTimerFlusher(this);
if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch nsHtml5StreamParserTimerFlusher");
}
}