Bug 502568 - HTML5 parser should flush occasionally when loading pure text. r=bnewman.
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user