Bug 543458 - Make the HTML5 tree op executor use nsContentSink code for deciding when to return to the event loop. r=bnewman.

--HG--
extra : rebase_source : fa41825753811279070d232c7d467600d7b2d523
This commit is contained in:
Henri Sivonen 2010-02-26 11:18:38 +02:00
Родитель 385bd7f8b5
Коммит ced01aa917
28 изменённых файлов: 652 добавлений и 530 удалений

Просмотреть файл

@ -1728,11 +1728,13 @@ nsContentSink::WillBuildModelImpl()
void
nsContentSink::ContinueInterruptedParsingIfEnabled()
{
// This shouldn't be called in the HTML5 case.
if (mParser && mParser->IsParserEnabled()) {
mParser->ContinueInterruptedParsing();
}
}
// Overridden in the HTML5 case
void
nsContentSink::ContinueInterruptedParsingAsync()
{

Просмотреть файл

@ -300,7 +300,7 @@ private:
protected:
void ContinueInterruptedParsingAsync();
virtual void ContinueInterruptedParsingAsync();
void ContinueInterruptedParsingIfEnabled();
nsCOMPtr<nsIDocument> mDocument;

Просмотреть файл

@ -2880,18 +2880,10 @@ pref("html5.flushtimer.startdelay", 200);
pref("html5.flushtimer.continuedelay", 150);
// Time in milliseconds between timer firings once the timer has starting
// firing.
pref("html5.flushtimer.interval", 100);
pref("html5.flushtimer.interval", 120);
// Push/Pop/Replace State prefs
pref("browser.history.allowPushState", true);
pref("browser.history.allowReplaceState", true);
pref("browser.history.allowPopState", true);
pref("browser.history.maxStateObjectSize", 655360);
// Initial max length for number of tree ops in on flush.
pref("html5.opqueue.initiallengthlimit", 200);
// Maximum time in milliseconds to spend flushing the tree op queue when not forced to completion
pref("html5.opqueue.maxtime", 100);
// Minimun number of tree ops to flush regardless of time (takes precedence over the maxtime pref)
pref("html5.opqueue.minlength", 100);
// Maximum number of tree ops to flush regardless of time (takes precedence over the maxtime pref)
pref("html5.opqueue.maxlength", 4500); // most top sites stay under this value

Просмотреть файл

@ -74,7 +74,7 @@ CPPSRCS = \
nsHtml5TreeOpExecutor.cpp \
nsHtml5StreamParser.cpp \
nsHtml5Speculation.cpp \
nsHtml5SpeculativeLoader.cpp \
nsHtml5SpeculativeLoad.cpp \
$(NULL)
FORCE_STATIC_LIB = 1

Просмотреть файл

@ -47,7 +47,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -47,7 +47,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -48,7 +48,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -48,7 +48,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -72,7 +72,6 @@ nsHtml5Module::InitializeStatics()
nsHtml5TreeBuilder::initializeStatics();
nsHtml5UTF16Buffer::initializeStatics();
nsHtml5StreamParser::InitializeStatics();
nsHtml5TreeOpExecutor::InitializeStatics();
#ifdef DEBUG
sNsHtml5ModuleInitialized = PR_TRUE;
#endif

Просмотреть файл

@ -179,28 +179,8 @@ nsHtml5Parser::GetStreamListener(nsIStreamListener** aListener)
NS_IMETHODIMP
nsHtml5Parser::ContinueInterruptedParsing()
{
// If there are scripts executing, then the content sink is jumping the gun
// (probably due to a synchronous XMLHttpRequest) and will re-enable us
// later, see bug 460706.
if (mExecutor->IsScriptExecuting()) {
return NS_OK;
}
if (mExecutor->IsFlushing()) {
// A nested event loop dequeued the continue event and there aren't
// scripts executing. What's currently causing the flush is running to
// completion or there will be a script later and the script will cause
// another continue event.
return NS_OK;
}
// If the stream has already finished, there's a good chance
// that we might start closing things down when the parser
// is reenabled. To make sure that we're not deleted across
// the reenabling process, hold a reference to ourselves.
nsCOMPtr<nsIParser> kungFuDeathGrip(this);
nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
ParseUntilBlocked();
return NS_OK;
NS_NOTREACHED("Don't call. For interface compat only.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP_(void)
@ -360,7 +340,6 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
}
if (!mBlocked) {
// mExecutor->WillResume();
while (buffer->hasMore()) {
buffer->adjust(mLastWasCR);
mLastWasCR = PR_FALSE;
@ -387,10 +366,9 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
if (mTreeBuilder->HasScript()) {
// No need to flush characters, because an end tag was tokenized last
mTreeBuilder->Flush(); // Move ops to the executor
mExecutor->Flush(PR_TRUE); // run the ops
mExecutor->FlushDocumentWrite(); // run the ops
}
if (mBlocked) {
// mExecutor->WillInterrupt();
break;
}
// Ignore suspension requests
@ -403,7 +381,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
// Scripting semantics require a forced tree builder flush here
mTreeBuilder->flushCharacters(); // Flush trailing characters
mTreeBuilder->Flush(); // Move ops to the executor
mExecutor->Flush(PR_TRUE); // run the ops
mExecutor->FlushDocumentWrite(); // run the ops
}
return NS_OK;
@ -498,7 +476,7 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
mTokenizer->eof();
mTreeBuilder->StreamEnded();
mTreeBuilder->Flush();
mExecutor->Flush(PR_TRUE);
mExecutor->FlushDocumentWrite();
mTokenizer->end();
mExecutor->DropParserAndPerfHint();
mAtomTable.Clear();
@ -592,7 +570,6 @@ nsHtml5Parser::ParseUntilBlocked()
}
NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
mExecutor->WillResume();
for (;;) {
if (!mFirstBuffer->hasMore()) {
if (mFirstBuffer == mLastBuffer) {
@ -606,7 +583,7 @@ nsHtml5Parser::ParseUntilBlocked()
mTokenizer->eof();
mTreeBuilder->StreamEnded();
mTreeBuilder->Flush();
mExecutor->Flush(PR_TRUE);
mExecutor->FlushDocumentWrite();
mTokenizer->end();
return;
} else {
@ -651,10 +628,9 @@ nsHtml5Parser::ParseUntilBlocked()
}
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush();
mExecutor->Flush(PR_TRUE);
mExecutor->FlushDocumentWrite();
}
if (mBlocked) {
// mExecutor->WillInterrupt();
return;
}
}

Просмотреть файл

@ -143,10 +143,9 @@ class nsHtml5Parser : public nsIParser,
NS_IMETHOD GetStreamListener(nsIStreamListener** aListener);
/**
* If scripts are not executing, maybe flushes tree builder and parses
* until suspension.
* Don't call. For interface compat only.
*/
NS_IMETHOD ContinueInterruptedParsing();
NS_IMETHOD ContinueInterruptedParsing();
/**
* Blocks the parser.
@ -159,7 +158,7 @@ class nsHtml5Parser : public nsIParser,
NS_IMETHOD_(void) UnblockParser();
/**
* Query whether the parser is enabled or not.
* Query whether the parser is enabled (i.e. not blocked) or not.
*/
NS_IMETHOD_(PRBool) IsParserEnabled();
@ -312,19 +311,17 @@ class nsHtml5Parser : public nsIParser,
void ContinueAfterFailedCharsetSwitch();
#ifdef DEBUG
PRBool HasStreamParser() {
return !!mStreamParser;
nsHtml5StreamParser* GetStreamParser() {
return mStreamParser;
}
#endif
private:
/**
* Parse until pending data is exhausted or a script blocks the parser
*/
void ParseUntilBlocked();
private:
// State variables
/**

Просмотреть файл

@ -47,7 +47,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -15,7 +15,7 @@
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
@ -35,54 +35,42 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsHtml5SpeculativeLoader_h__
#define nsHtml5SpeculativeLoader_h__
#include "mozilla/Mutex.h"
#include "nsIURI.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsHtml5SpeculativeLoad.h"
#include "nsHtml5TreeOpExecutor.h"
#include "nsHashSets.h"
class nsHtml5SpeculativeLoader
nsHtml5SpeculativeLoad::nsHtml5SpeculativeLoad()
#ifdef DEBUG
: mOpCode(eSpeculativeLoadUninitialized)
#endif
{
public:
nsHtml5SpeculativeLoader(nsHtml5TreeOpExecutor* aExecutor);
~nsHtml5SpeculativeLoader();
MOZ_COUNT_CTOR(nsHtml5SpeculativeLoad);
}
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
void PreloadScript(const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType);
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
void PreloadImage(const nsAString& aURL);
void ProcessManifest(const nsAString& aURL);
private:
/**
* Get a nsIURI for an nsString if the URL hasn't been preloaded yet.
*/
already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL);
nsAutoRefCnt mRefCnt;
/**
* The executor to use as the context for preloading.
*/
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
/**
* URLs already preloaded/preloading.
*/
nsCStringHashSet mPreloadedURLs;
};
#endif // nsHtml5SpeculativeLoader_h__
nsHtml5SpeculativeLoad::~nsHtml5SpeculativeLoad()
{
MOZ_COUNT_DTOR(nsHtml5SpeculativeLoad);
NS_ASSERTION(mOpCode != eSpeculativeLoadUninitialized,
"Uninitialized speculative load.");
}
void
nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
{
switch (mOpCode) {
case eSpeculativeLoadImage:
aExecutor->PreloadImage(mUrl);
break;
case eSpeculativeLoadScript:
aExecutor->PreloadScript(mUrl, mCharset, mType);
break;
case eSpeculativeLoadStyle:
aExecutor->PreloadStyle(mUrl, mCharset);
break;
case eSpeculativeLoadManifest:
aExecutor->ProcessOfflineManifest(mUrl);
break;
default:
NS_NOTREACHED("Bogus speculative load.");
break;
}
}

Просмотреть файл

@ -0,0 +1,113 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is HTML Parser C++ Translator code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Henri Sivonen <hsivonen@iki.fi>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsHtml5SpeculativeLoad_h_
#define nsHtml5SpeculativeLoad_h_
#include "nsString.h"
class nsHtml5TreeOpExecutor;
enum eHtml5SpeculativeLoad {
#ifdef DEBUG
eSpeculativeLoadUninitialized,
#endif
eSpeculativeLoadImage,
eSpeculativeLoadScript,
eSpeculativeLoadStyle,
eSpeculativeLoadManifest
};
class nsHtml5SpeculativeLoad {
public:
nsHtml5SpeculativeLoad();
~nsHtml5SpeculativeLoad();
inline void InitImage(const nsAString& aUrl) {
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadImage;
mUrl.Assign(aUrl);
}
inline void InitScript(const nsAString& aUrl,
const nsAString& aCharset,
const nsAString& aType) {
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadScript;
mUrl.Assign(aUrl);
mCharset.Assign(aCharset);
mType.Assign(aType);
}
inline void InitStyle(const nsAString& aUrl, const nsAString& aCharset) {
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadStyle;
mUrl.Assign(aUrl);
mCharset.Assign(aCharset);
}
/**
* "Speculative" manifest loads aren't truly speculative--if a manifest
* gets loaded, we are committed to it. There can never be a <script>
* before the manifest, so the situation of having to undo a manifest due
* to document.write() never arises. The reason why a parser
* thread-discovered manifest gets loaded via the speculative load queue
* as opposed to tree operation queue is that the manifest must get
* processed before any actually speculative loads such as scripts. Thus,
* manifests seen by the parser thread have to maintain the queue order
* relative to true speculative loads. See bug 541079.
*/
inline void InitManifest(const nsAString& aUrl) {
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
mOpCode = eSpeculativeLoadManifest;
mUrl.Assign(aUrl);
}
void Perform(nsHtml5TreeOpExecutor* aExecutor);
private:
eHtml5SpeculativeLoad mOpCode;
nsString mUrl;
nsString mCharset;
nsString mType;
};
#endif // nsHtml5SpeculativeLoad_h_

Просмотреть файл

@ -1,135 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=2 et tw=79: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Henri Sivonen <hsivonen@iki.fi>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsHtml5SpeculativeLoader.h"
#include "nsNetUtil.h"
#include "nsScriptLoader.h"
#include "nsIDocument.h"
nsHtml5SpeculativeLoader::
nsHtml5SpeculativeLoader(nsHtml5TreeOpExecutor* aExecutor)
: mExecutor(aExecutor)
{
MOZ_COUNT_CTOR(nsHtml5SpeculativeLoader);
mPreloadedURLs.Init(23); // Mean # of preloadable resources per page on dmoz
}
nsHtml5SpeculativeLoader::~nsHtml5SpeculativeLoader()
{
MOZ_COUNT_DTOR(nsHtml5SpeculativeLoader);
}
NS_IMPL_THREADSAFE_ADDREF(nsHtml5SpeculativeLoader)
NS_IMPL_THREADSAFE_RELEASE(nsHtml5SpeculativeLoader)
already_AddRefed<nsIURI>
nsHtml5SpeculativeLoader::ConvertIfNotPreloadedYet(const nsAString& aURL)
{
nsIDocument* doc = mExecutor->GetDocument();
if (!doc) {
return nsnull;
}
nsIURI* base = doc->GetBaseURI();
const nsCString& charset = doc->GetDocumentCharacterSet();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, charset.get(), base);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create a URI");
return nsnull;
}
nsCAutoString spec;
uri->GetSpec(spec);
if (mPreloadedURLs.Contains(spec)) {
return nsnull;
}
mPreloadedURLs.Put(spec);
nsIURI* retURI = uri;
NS_ADDREF(retURI);
return retURI;
}
void
nsHtml5SpeculativeLoader::PreloadScript(const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
nsIDocument* doc = mExecutor->GetDocument();
if (doc) {
doc->ScriptLoader()->PreloadURI(uri, aCharset, aType);
}
}
void
nsHtml5SpeculativeLoader::PreloadStyle(const nsAString& aURL,
const nsAString& aCharset)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
nsIDocument* doc = mExecutor->GetDocument();
if (doc) {
doc->PreloadStyle(uri, aCharset);
}
}
void
nsHtml5SpeculativeLoader::PreloadImage(const nsAString& aURL)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
nsIDocument* doc = mExecutor->GetDocument();
if (doc) {
doc->MaybePreLoadImage(uri);
}
}
void
nsHtml5SpeculativeLoader::ProcessManifest(const nsAString& aURL)
{
mExecutor->ProcessOfflineManifest(aURL);
}

Просмотреть файл

@ -48,7 +48,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -47,7 +47,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;

Просмотреть файл

@ -51,7 +51,6 @@
#include "nsHtml5AtomTable.h"
#include "nsHtml5Module.h"
#include "nsHtml5RefPtr.h"
#include "nsHtml5SpeculativeLoader.h"
static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
@ -92,9 +91,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
tmp->mOwner = nsnull;
tmp->mExecutorFlusher = nsnull;
tmp->mLoadFlusher = nsnull;
tmp->mExecutor = nsnull;
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChardet)
tmp->mTreeBuilder->DropSpeculativeLoader();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
@ -109,18 +108,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExecutorFlusher->mExecutor");
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
}
// hack: count the strongly owned edge wrapped in the runnable
if (tmp->mLoadFlusher) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadFlusher->mExecutor");
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
}
// hack: count self if held by mChardet
if (tmp->mChardet) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mChardet->mObserver");
cb.NoteXPCOMChild(static_cast<nsIStreamListener*>(tmp));
}
// hack: count the strongly owned edge wrapped in the speculative loader
if (tmp->mTreeBuilder->HasSpeculativeLoader()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mTreeBuilder->mSpeculativeLoader->mExecutor");
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
class nsHtml5ExecutorFlusher : public nsRunnable
@ -133,7 +131,22 @@ class nsHtml5ExecutorFlusher : public nsRunnable
{}
NS_IMETHODIMP Run()
{
mExecutor->Flush(PR_FALSE);
mExecutor->RunFlushLoop();
return NS_OK;
}
};
class nsHtml5LoadFlusher : public nsRunnable
{
private:
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
public:
nsHtml5LoadFlusher(nsHtml5TreeOpExecutor* aExecutor)
: mExecutor(aExecutor)
{}
NS_IMETHODIMP Run()
{
mExecutor->FlushSpeculativeLoads();
return NS_OK;
}
};
@ -143,8 +156,8 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
: mFirstBuffer(new nsHtml5UTF16Buffer(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE))
, mLastBuffer(mFirstBuffer)
, mExecutor(aExecutor)
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage(),
new nsHtml5SpeculativeLoader(mExecutor)))
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage(),
mExecutor->GetStage()))
, mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
, mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
, mOwner(aOwner)
@ -152,6 +165,7 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
, mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
, mThread(nsHtml5Module::GetStreamParserThread())
, mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
, mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
, mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@ -755,6 +769,13 @@ nsHtml5StreamParser::ParseAvailableData()
mFirstBuffer->setStart(0);
mFirstBuffer->setEnd(0);
}
mTreeBuilder->FlushLoads();
// Dispatch this runnable unconditionally, because the loads
// that need flushing may have been flushed earlier even if the
// flush right above here did nothing.
if (NS_FAILED(NS_DispatchToMainThread(mLoadFlusher))) {
NS_WARNING("failed to dispatch load flush event");
}
return; // no more data for now but expecting more
case STREAM_ENDED:
if (mAtEOF) {
@ -859,9 +880,12 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
// the first speculation isn't the current speculation, so there's
// no need to bother the parser thread.
speculation->FlushToSink(mExecutor);
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
NS_ASSERTION(!mExecutor->IsScriptExecuting(),
"ParseUntilBlocked() was supposed to ensure we don't come "
"here when scripts are executing.");
NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
"RunFlushLoop() didn't call ParseUntilBlocked() which is the "
"only caller of this method?");
mSpeculations.RemoveElementAt(0);
return;
}
@ -920,9 +944,12 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
// We've got a successful speculation and at least a moment ago it was
// the current speculation
mSpeculations.ElementAt(0)->FlushToSink(mExecutor);
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
NS_ASSERTION(!mExecutor->IsScriptExecuting(),
"ParseUntilBlocked() was supposed to ensure we don't come "
"here when scripts are executing.");
NS_ASSERTION(mExecutor->IsInFlushLoop(), "How are we here if "
"RunFlushLoop() didn't call ParseUntilBlocked() which is the "
"only caller of this method?");
mSpeculations.RemoveElementAt(0);
if (mSpeculations.IsEmpty()) {
// yes, it was still the only speculation. Now stop speculating

Просмотреть файл

@ -453,6 +453,8 @@ class nsHtml5StreamParser : public nsIStreamListener,
nsCOMPtr<nsIRunnable> mExecutorFlusher;
nsCOMPtr<nsIRunnable> mLoadFlusher;
/**
* The chardet instance if chardet is enabled.
*/

Просмотреть файл

@ -50,7 +50,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5TreeBuilder;
class nsHtml5MetaScanner;

Просмотреть файл

@ -56,7 +56,6 @@
#include "nsAHtml5TreeBuilderState.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5MetaScanner;

Просмотреть файл

@ -46,12 +46,11 @@
#include "nsEventDispatcher.h"
#include "nsContentUtils.h"
#include "nsNodeUtils.h"
#include "nsHtml5SpeculativeLoader.h"
// this really should be autogenerated...
jArray<PRUnichar,PRInt32> nsHtml5TreeBuilder::ISINDEX_PROMPT = jArray<PRUnichar,PRInt32>();
nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
nsHtml5SpeculativeLoader* aSpeculativeLoader)
nsHtml5TreeOpStage* aStage)
: scriptingEnabled(PR_FALSE)
, fragment(PR_FALSE)
, contextNode(nsnull)
@ -60,7 +59,7 @@ nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
, mOpSink(aOpSink)
, mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH])
, mHandlesUsed(0)
, mSpeculativeLoader(aSpeculativeLoader)
, mSpeculativeLoadStage(aStage)
, mCurrentHtmlScriptIsAsyncOrDefer(PR_FALSE)
#ifdef DEBUG
, mActive(PR_FALSE)
@ -76,87 +75,6 @@ nsHtml5TreeBuilder::~nsHtml5TreeBuilder()
mOpQueue.Clear();
}
class nsHtml5SpeculativeScript : public nsRunnable
{
private:
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
nsString mURL;
nsString mCharset;
nsString mType;
public:
nsHtml5SpeculativeScript(nsHtml5SpeculativeLoader* aSpeculativeLoader,
const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType)
: mSpeculativeLoader(aSpeculativeLoader)
, mURL(aURL)
, mCharset(aCharset)
, mType(aType)
{}
NS_IMETHODIMP Run()
{
mSpeculativeLoader->PreloadScript(mURL, mCharset, mType);
return NS_OK;
}
};
class nsHtml5SpeculativeStyle : public nsRunnable
{
private:
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
nsString mURL;
nsString mCharset;
public:
nsHtml5SpeculativeStyle(nsHtml5SpeculativeLoader* aSpeculativeLoader,
const nsAString& aURL,
const nsAString& aCharset)
: mSpeculativeLoader(aSpeculativeLoader)
, mURL(aURL)
, mCharset(aCharset)
{}
NS_IMETHODIMP Run()
{
mSpeculativeLoader->PreloadStyle(mURL, mCharset);
return NS_OK;
}
};
class nsHtml5SpeculativeImage : public nsRunnable
{
private:
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
nsString mURL;
public:
nsHtml5SpeculativeImage(nsHtml5SpeculativeLoader* aSpeculativeLoader,
const nsAString& aURL)
: mSpeculativeLoader(aSpeculativeLoader)
, mURL(aURL)
{}
NS_IMETHODIMP Run()
{
mSpeculativeLoader->PreloadImage(mURL);
return NS_OK;
}
};
class nsHtml5SpeculativeManifest : public nsRunnable
{
private:
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
nsString mURL;
public:
nsHtml5SpeculativeManifest(nsHtml5SpeculativeLoader* aSpeculativeLoader,
const nsAString& aURL)
: mSpeculativeLoader(aSpeculativeLoader)
, mURL(aURL)
{}
NS_IMETHODIMP Run()
{
mSpeculativeLoader->ProcessManifest(mURL);
return NS_OK;
}
};
nsIContent**
nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes)
{
@ -174,13 +92,13 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
// Start wall of code for speculative loading and line numbers
if (mSpeculativeLoader) {
if (mSpeculativeLoadStage) {
switch (aNamespace) {
case kNameSpaceID_XHTML:
if (nsHtml5Atoms::img == aName) {
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
if (url) {
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
mSpeculativeLoadQueue.AppendElement()->InitImage(*url);
}
} else if (nsHtml5Atoms::script == aName) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -191,10 +109,9 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
if (url) {
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
Dispatch(new nsHtml5SpeculativeScript(mSpeculativeLoader,
*url,
(charset) ? *charset : EmptyString(),
(type) ? *type : EmptyString()));
mSpeculativeLoadQueue.AppendElement()->InitScript(*url,
(charset) ? *charset : EmptyString(),
(type) ? *type : EmptyString());
mCurrentHtmlScriptIsAsyncOrDefer =
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
@ -207,15 +124,14 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
if (url) {
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
*url,
(charset) ? *charset : EmptyString()));
mSpeculativeLoadQueue.AppendElement()->InitStyle(*url,
(charset) ? *charset : EmptyString());
}
}
} else if (nsHtml5Atoms::video == aName) {
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
if (url) {
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
mSpeculativeLoadQueue.AppendElement()->InitImage(*url);
}
} else if (nsHtml5Atoms::style == aName) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -224,7 +140,7 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
} else if (nsHtml5Atoms::html == aName) {
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
if (url) {
Dispatch(new nsHtml5SpeculativeManifest(mSpeculativeLoader, *url));
mSpeculativeLoadQueue.AppendElement()->InitManifest(*url);
}
}
break;
@ -232,7 +148,7 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
if (nsHtml5Atoms::image == aName) {
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
if (url) {
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
mSpeculativeLoadQueue.AppendElement()->InitImage(*url);
}
} else if (nsHtml5Atoms::script == aName) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -242,10 +158,9 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
if (url) {
nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
Dispatch(new nsHtml5SpeculativeScript(mSpeculativeLoader,
*url,
EmptyString(),
(type) ? *type : EmptyString()));
mSpeculativeLoadQueue.AppendElement()->InitScript(*url,
EmptyString(),
(type) ? *type : EmptyString());
}
} else if (nsHtml5Atoms::style == aName) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
@ -254,9 +169,7 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
if (url) {
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
*url,
EmptyString()));
mSpeculativeLoadQueue.AppendElement()->InitStyle(*url, EmptyString());
}
}
break;
@ -636,6 +549,7 @@ nsHtml5TreeBuilder::HasScript()
PRBool
nsHtml5TreeBuilder::Flush()
{
FlushLoads();
PRBool hasOps = !mOpQueue.IsEmpty();
if (hasOps) {
mOpSink->MoveOpsFrom(mOpQueue);
@ -643,6 +557,14 @@ nsHtml5TreeBuilder::Flush()
return hasOps;
}
void
nsHtml5TreeBuilder::FlushLoads()
{
if (!mSpeculativeLoadQueue.IsEmpty()) {
mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue);
}
}
void
nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset,
PRInt32 aCharsetSource)
@ -683,11 +605,6 @@ nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRI
mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
}
void
nsHtml5TreeBuilder::DropSpeculativeLoader() {
mSpeculativeLoader = nsnull;
}
PRBool
nsHtml5TreeBuilder::IsDiscretionaryFlushSafe()
{

Просмотреть файл

@ -40,11 +40,12 @@
private:
nsTArray<nsHtml5TreeOperation> mOpQueue;
nsTArray<nsHtml5SpeculativeLoad> mSpeculativeLoadQueue;
nsAHtml5TreeOpSink* mOpSink;
nsAutoArrayPtr<nsIContent*> mHandles;
PRInt32 mHandlesUsed;
nsTArray<nsAutoArrayPtr<nsIContent*> > mOldHandles;
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
nsHtml5TreeOpStage* mSpeculativeLoadStage;
PRBool mCurrentHtmlScriptIsAsyncOrDefer;
#ifdef DEBUG
PRBool mActive;
@ -60,8 +61,8 @@
public:
nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
nsHtml5SpeculativeLoader* aSpeculativeLoader);
nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
nsHtml5TreeOpStage* aStage);
~nsHtml5TreeBuilder();
@ -77,14 +78,10 @@
mOpQueue.Clear();
}
PRBool HasSpeculativeLoader() {
return !!mSpeculativeLoader;
}
void DropSpeculativeLoader();
PRBool Flush();
void FlushLoads();
void SetDocumentCharset(nsACString& aCharset, PRInt32 aCharsetSource);
void StreamEnded();
@ -92,9 +89,3 @@
void NeedsCharsetSwitchTo(const nsACString& aEncoding);
void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine);
inline void Dispatch(nsIRunnable* aEvent) {
if (NS_FAILED(NS_DispatchToMainThread(aEvent))) {
NS_WARNING("Failed to dispatch speculative load runnable.");
}
}

Просмотреть файл

@ -77,44 +77,25 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContent
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mOwnedElements)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueLengthLimit = 200;
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxTime = 100; // milliseconds
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMinLength = 100;
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxLength = 4500;
// static
void
nsHtml5TreeOpExecutor::InitializeStatics()
class nsHtml5ExecutorReflusher : public nsRunnable
{
// Changes to the initial max length pref are intentionally allowed
// to reset the run-time-calibrated value.
nsContentUtils::AddIntPrefVarCache("html5.opqueue.initiallengthlimit",
&sTreeOpQueueLengthLimit);
nsContentUtils::AddIntPrefVarCache("html5.opqueue.maxtime",
&sTreeOpQueueMaxTime);
nsContentUtils::AddIntPrefVarCache("html5.opqueue.minlength",
&sTreeOpQueueMinLength);
nsContentUtils::AddIntPrefVarCache("html5.opqueue.maxlength",
&sTreeOpQueueMaxLength);
// Now do some sanity checking to prevent breaking the app via
// about:config so badly that it can't be recovered via about:config.
if (sTreeOpQueueMinLength <= 0) {
sTreeOpQueueMinLength = 200;
}
if (sTreeOpQueueLengthLimit < sTreeOpQueueMinLength) {
sTreeOpQueueLengthLimit = sTreeOpQueueMinLength;
}
if (sTreeOpQueueMaxLength < sTreeOpQueueMinLength) {
sTreeOpQueueMaxLength = sTreeOpQueueMinLength;
}
if (sTreeOpQueueMaxTime <= 0) {
sTreeOpQueueMaxTime = 200;
}
}
private:
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
public:
nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor* aExecutor)
: mExecutor(aExecutor)
{}
NS_IMETHODIMP Run()
{
mExecutor->RunFlushLoop();
return NS_OK;
}
};
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
{
// zeroing operator new for everything
mPreloadedURLs.Init(23); // Mean # of preloadable resources per page on dmoz
// zeroing operator new for everything else
}
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
@ -198,14 +179,15 @@ nsHtml5TreeOpExecutor::DidBuildModel(PRBool aTerminated)
NS_IMETHODIMP
nsHtml5TreeOpExecutor::WillInterrupt()
{
return WillInterruptImpl();
NS_NOTREACHED("Don't call. For interface compat only.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsHtml5TreeOpExecutor::WillResume()
{
WillResumeImpl();
return WillParseImpl();
NS_NOTREACHED("Don't call. For interface compat only.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
@ -292,6 +274,15 @@ nsHtml5TreeOpExecutor::PostEvaluateScript(nsIScriptElement *aElement)
htmlDocument->ScriptExecuted(aElement);
}
void
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync()
{
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
if (NS_FAILED(NS_DispatchToMainThread(flusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
void
nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
{
@ -347,129 +338,267 @@ nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
BeginDocUpdate();
}
class nsHtml5ExecutorReflusher : public nsRunnable
void
nsHtml5TreeOpExecutor::FlushSpeculativeLoads()
{
if (NS_UNLIKELY(!mParser)) {
return;
}
nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
for (nsHtml5SpeculativeLoad* iter = (nsHtml5SpeculativeLoad*)start;
iter < end;
++iter) {
iter->Perform(this);
}
}
class nsHtml5FlushLoopGuard
{
private:
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
PRUint32 mStartTime;
#endif
public:
nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor* aExecutor)
nsHtml5FlushLoopGuard(nsHtml5TreeOpExecutor* aExecutor)
: mExecutor(aExecutor)
{}
NS_IMETHODIMP Run()
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
, mStartTime(PR_IntervalToMilliseconds(PR_IntervalNow()))
#endif
{
mExecutor->Flush(PR_FALSE);
return NS_OK;
mExecutor->mRunFlushLoopOnStack = PR_TRUE;
}
~nsHtml5FlushLoopGuard()
{
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
PRUint32 timeOffTheEventLoop =
PR_IntervalToMilliseconds(PR_IntervalNow()) - mStartTime;
if (timeOffTheEventLoop >
nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop) {
nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop =
timeOffTheEventLoop;
}
printf("Longest time off the event loop: %d\n",
nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop);
#endif
mExecutor->mRunFlushLoopOnStack = PR_FALSE;
}
};
/**
* The purpose of the loop here is to avoid returning to the main event loop
*/
void
nsHtml5TreeOpExecutor::Flush(PRBool aForceWholeQueue)
nsHtml5TreeOpExecutor::RunFlushLoop()
{
if (!mParser) {
mOpQueue.Clear(); // clear in order to be able to assert in destructor
return;
}
if (mFlushState != eNotFlushing) {
if (mRunFlushLoopOnStack) {
// There's already a RunFlushLoop() on the call stack.
return;
}
mFlushState = eInFlush;
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this); // avoid crashing near EOF
nsHtml5FlushLoopGuard guard(this); // this is also the self-kungfu!
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
if (mReadingFromStage) {
mStage.MoveOpsTo(mOpQueue);
// Remember the entry time
(void) nsContentSink::WillParseImpl();
for (;;) {
if (!mParser) {
// Parse has terminated.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
return;
}
if (!mParser->IsParserEnabled()) {
// The parser is blocked.
return;
}
if (mFlushState != eNotFlushing) {
// XXX Can this happen? In case it can, let's avoid crashing.
return;
}
// If there are scripts executing, then the content sink is jumping the gun
// (probably due to a synchronous XMLHttpRequest) and will re-enable us
// later, see bug 460706.
if (IsScriptExecuting()) {
return;
}
if (mReadingFromStage) {
nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
mStage.MoveOpsAndSpeculativeLoadsTo(mOpQueue, speculativeLoadQueue);
// Make sure speculative loads never start after the corresponding
// normal loads for the same URLs.
const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
for (nsHtml5SpeculativeLoad* iter = (nsHtml5SpeculativeLoad*)start;
iter < end;
++iter) {
iter->Perform(this);
}
} else {
FlushSpeculativeLoads(); // Make sure speculative loads never start after
// the corresponding normal loads for the same
// URLs.
// Not sure if this grip is still needed, but previously, the code
// gripped before calling ParseUntilBlocked();
nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip =
static_cast<nsHtml5Parser*> (mParser.get())->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.
static_cast<nsHtml5Parser*> (mParser.get())->ParseUntilBlocked();
}
if (mOpQueue.IsEmpty()) {
// Avoid bothering the rest of the engine with a doc update if there's
// nothing to do.
return;
}
mFlushState = eInFlush;
nsIContent* scriptElement = nsnull;
BeginDocUpdate();
PRUint32 numberOfOpsToFlush = mOpQueue.Length();
mElementsSeenInThisAppendBatch.SetCapacity(numberOfOpsToFlush * 2);
const nsHtml5TreeOperation* first = mOpQueue.Elements();
const nsHtml5TreeOperation* last = first + numberOfOpsToFlush - 1;
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)first;;) {
if (NS_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.");
iter->Perform(this, &scriptElement);
// Be sure not to check the deadline if the last op was just performed.
if (NS_UNLIKELY(iter == last)) {
break;
} else if (NS_UNLIKELY(nsContentSink::DidProcessATokenImpl() ==
NS_ERROR_HTMLPARSER_INTERRUPTED)) {
mOpQueue.RemoveElementsAt(0, (iter - first) + 1);
EndDocUpdate();
mFlushState = eNotFlushing;
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
printf("REFLUSH SCHEDULED (executing ops): %d\n",
++sTimesFlushLoopInterrupted);
#endif
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
return;
}
++iter;
}
mOpQueue.Clear();
EndDocUpdate();
mFlushState = eNotFlushing;
if (NS_UNLIKELY(!mParser)) {
// The parse ended already.
return;
}
if (scriptElement) {
// must be tail call when mFlushState is eNotFlushing
RunScript(scriptElement);
// The script execution machinery makes sure this doesn't get deflected.
if (nsContentSink::DidProcessATokenImpl() ==
NS_ERROR_HTMLPARSER_INTERRUPTED) {
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
printf("REFLUSH SCHEDULED (after script): %d\n",
++sTimesFlushLoopInterrupted);
#endif
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
return;
}
}
}
}
void
nsHtml5TreeOpExecutor::FlushDocumentWrite()
{
if (!mParser) {
// The parse has ended.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
return;
}
FlushSpeculativeLoads(); // Make sure speculative loads never start after the
// corresponding normal loads for the same URLs.
if (mFlushState != eNotFlushing) {
// XXX Can this happen? In case it can, let's avoid crashing.
return;
}
mFlushState = eInFlush;
// avoid crashing near EOF
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
NS_ASSERTION(!mReadingFromStage,
"Got doc write flush when reading from stage");
#ifdef DEBUG
mStage.AssertEmpty();
#endif
nsIContent* scriptElement = nsnull;
BeginDocUpdate();
PRIntervalTime flushStart = 0;
PRUint32 numberOfOpsToFlush = mOpQueue.Length();
PRBool reflushNeeded = PR_FALSE;
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
if (numberOfOpsToFlush > sOpQueueMaxLength) {
sOpQueueMaxLength = numberOfOpsToFlush;
}
printf("QUEUE LENGTH: %d\n", numberOfOpsToFlush);
printf("MAX QUEUE LENGTH: %d\n", sOpQueueMaxLength);
#endif
if (aForceWholeQueue) {
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueMinLength) {
flushStart = PR_IntervalNow(); // compute averages only if enough ops
}
} else {
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueMinLength) {
flushStart = PR_IntervalNow(); // compute averages only if enough ops
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueLengthLimit) {
numberOfOpsToFlush = (PRUint32)sTreeOpQueueLengthLimit;
reflushNeeded = PR_TRUE;
}
}
}
mElementsSeenInThisAppendBatch.SetCapacity(numberOfOpsToFlush * 2);
const nsHtml5TreeOperation* start = mOpQueue.Elements();
const nsHtml5TreeOperation* end = start + numberOfOpsToFlush;
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start; iter < end; ++iter) {
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start;
iter < end;
++iter) {
if (NS_UNLIKELY(!mParser)) {
// The previous tree op caused a call to nsIParser::Terminate();
// The previous tree op caused a call to nsIParser::Terminate().
break;
}
NS_ASSERTION(mFlushState == eInDocUpdate, "Tried to perform tree op outside update batch.");
NS_ASSERTION(mFlushState == eInDocUpdate,
"Tried to perform tree op outside update batch.");
iter->Perform(this, &scriptElement);
}
if (NS_LIKELY(mParser)) {
mOpQueue.RemoveElementsAt(0, numberOfOpsToFlush);
} else {
mOpQueue.Clear(); // only for diagnostics in the destructor of this class
}
mOpQueue.Clear();
if (flushStart) {
PRUint32 delta = PR_IntervalToMilliseconds(PR_IntervalNow() - flushStart);
sTreeOpQueueLengthLimit = delta ?
(PRUint32)(((PRUint64)sTreeOpQueueMaxTime * (PRUint64)numberOfOpsToFlush)
/ delta) :
sTreeOpQueueMaxLength; // if the delta is less than one ms, use max
if (sTreeOpQueueLengthLimit < sTreeOpQueueMinLength) {
// both are signed and sTreeOpQueueMinLength is always positive, so this
// also takes care of the theoretical overflow of sTreeOpQueueLengthLimit
sTreeOpQueueLengthLimit = sTreeOpQueueMinLength;
}
if (sTreeOpQueueLengthLimit > sTreeOpQueueMaxLength) {
sTreeOpQueueLengthLimit = sTreeOpQueueMaxLength;
}
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
printf("FLUSH DURATION (millis): %d\n", delta);
printf("QUEUE NEW MAX LENGTH: %d\n", sTreeOpQueueLengthLimit);
#endif
}
EndDocUpdate();
mFlushState = eNotFlushing;
if (NS_UNLIKELY(!mParser)) {
// Ending the doc update caused a call to nsIParser::Terminate().
return;
}
if (scriptElement) {
NS_ASSERTION(!reflushNeeded, "Got scriptElement when queue not fully flushed.");
RunScript(scriptElement); // must be tail call when mFlushState is eNotFlushing
} else if (reflushNeeded) {
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
printf("REFLUSH SCHEDULED.\n");
#endif
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
if (NS_FAILED(NS_DispatchToMainThread(flusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
// must be tail call when mFlushState is eNotFlushing
RunScript(scriptElement);
}
}
@ -631,8 +760,12 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
// This may have already happened if the script executed, but in case
// it didn't then remove the element so that it doesn't get stuck forever.
htmlDocument->ScriptExecuted(sele);
// mParser may have been nulled out by now, but nsContentSink deals
ContinueInterruptedParsingAsync();
// mParser may have been nulled out by now, but the flusher deals
// If this event isn't needed, it doesn't do anything. It is sometimes
// necessary for the parse to continue after complex situations.
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
}
}
@ -642,11 +775,7 @@ nsHtml5TreeOpExecutor::Init(nsIDocument* aDoc,
nsISupports* aContainer,
nsIChannel* aChannel)
{
nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
NS_ENSURE_SUCCESS(rv, rv);
mCanInterruptParser = PR_FALSE; // without this, nsContentSink calls
// UnblockOnload from DropParserAndPerfHint
return rv;
return nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
}
void
@ -701,6 +830,7 @@ nsHtml5TreeOpExecutor::Reset() {
mOpQueue.Clear();
mStarted = PR_FALSE;
mFlushState = eNotFlushing;
mRunFlushLoopOnStack = PR_FALSE;
mFragmentMode = PR_FALSE;
}
@ -721,9 +851,67 @@ nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* a
static_cast<nsHtml5Parser*> (mParser.get())->InitializeDocWriteParserState(aState, aLine);
}
// Speculative loading
already_AddRefed<nsIURI>
nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYet(const nsAString& aURL)
{
nsIURI* base = mDocument->GetBaseURI();
const nsCString& charset = mDocument->GetDocumentCharacterSet();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, charset.get(), base);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create a URI");
return nsnull;
}
nsCAutoString spec;
uri->GetSpec(spec);
if (mPreloadedURLs.Contains(spec)) {
return nsnull;
}
mPreloadedURLs.Put(spec);
nsIURI* retURI = uri;
NS_ADDREF(retURI);
return retURI;
}
void
nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType);
}
void
nsHtml5TreeOpExecutor::PreloadStyle(const nsAString& aURL,
const nsAString& aCharset)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
mDocument->PreloadStyle(uri, aCharset);
}
void
nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
mDocument->MaybePreLoadImage(uri);
}
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
PRUint32 nsHtml5TreeOpExecutor::sOpQueueMaxLength = 0;
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchMaxSize = 0;
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchSlotsExamined = 0;
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchExaminations = 0;
PRUint32 nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop = 0;
PRUint32 nsHtml5TreeOpExecutor::sTimesFlushLoopInterrupted = 0;
#endif

Просмотреть файл

@ -45,6 +45,7 @@
#include "nsIDocument.h"
#include "nsTraceRefcnt.h"
#include "nsHtml5TreeOperation.h"
#include "nsHtml5SpeculativeLoad.h"
#include "nsHtml5PendingNotification.h"
#include "nsTArray.h"
#include "nsContentSink.h"
@ -55,6 +56,8 @@
#include "nsCOMArray.h"
#include "nsAHtml5TreeOpSink.h"
#include "nsHtml5TreeOpStage.h"
#include "nsHashSets.h"
#include "nsIURI.h"
class nsHtml5TreeBuilder;
class nsHtml5Tokenizer;
@ -73,24 +76,21 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
public nsIContentSink,
public nsAHtml5TreeOpSink
{
friend class nsHtml5FlushLoopGuard;
public:
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
static void InitializeStatics();
private:
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
static PRUint32 sOpQueueMaxLength;
static PRUint32 sAppendBatchMaxSize;
static PRUint32 sAppendBatchSlotsExamined;
static PRUint32 sAppendBatchExaminations;
static PRUint32 sLongestTimeOffTheEventLoop;
static PRUint32 sTimesFlushLoopInterrupted;
#endif
static PRInt32 sTreeOpQueueLengthLimit;
static PRInt32 sTreeOpQueueMaxTime;
static PRInt32 sTreeOpQueueMinLength;
static PRInt32 sTreeOpQueueMaxLength;
/**
* Whether EOF needs to be suppressed
@ -105,6 +105,11 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
nsHtml5StreamParser* mStreamParser;
nsCOMArray<nsIContent> mOwnedElements;
/**
* URLs already preloaded/preloading.
*/
nsCStringHashSet mPreloadedURLs;
/**
* Whether the parser has started
*/
@ -114,6 +119,10 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
eHtml5FlushState mFlushState;
PRBool mRunFlushLoopOnStack;
PRBool mCallContinueInterruptedParsingIfEnabled;
PRBool mFragmentMode;
public:
@ -183,6 +192,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
virtual void UpdateChildCounts();
virtual nsresult FlushTags();
virtual void PostEvaluateScript(nsIScriptElement *aElement);
virtual void ContinueInterruptedParsingAsync();
/**
* Sets up style sheet load / parse
*/
@ -226,6 +237,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
void EnableFragmentMode() {
mFragmentMode = PR_TRUE;
mCanInterruptParser = PR_FALSE; // prevent DropParserAndPerfHint
// from unblocking onload
}
PRBool IsFragmentMode() {
@ -316,8 +329,12 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
nsISupports* aContainer, nsIChannel* aChannel);
void FlushSpeculativeLoads();
void Flush(PRBool aForceWholeQueue);
void RunFlushLoop();
void FlushDocumentWrite();
void MaybeSuspend();
@ -336,6 +353,12 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
PRBool IsFlushing() {
return mFlushState >= eInFlush;
}
#ifdef DEBUG
PRBool IsInFlushLoop() {
return mRunFlushLoopOnStack;
}
#endif
void RunScript(nsIContent* aScriptElement);
@ -345,15 +368,13 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
mOwnedElements.AppendObject(aContent);
}
// The following two methods are for the main-thread case
/**
* Flush the operations from the tree operations from the argument
* queue unconditionally.
* queue unconditionally. (This is for the main thread case.)
*/
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
nsAHtml5TreeOpSink* GetStage() {
nsHtml5TreeOpStage* GetStage() {
return &mStage;
}
@ -369,10 +390,23 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
}
#endif
void PreloadScript(const nsAString& aURL,
const nsAString& aCharset,
const nsAString& aType);
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
void PreloadImage(const nsAString& aURL);
private:
nsHtml5Tokenizer* GetTokenizer();
/**
* Get a nsIURI for an nsString if the URL hasn't been preloaded yet.
*/
already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL);
};
#endif // nsHtml5TreeOpExecutor_h__

Просмотреть файл

@ -52,20 +52,48 @@ nsHtml5TreeOpStage::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
mozilla::MutexAutoLock autoLock(mMutex);
if (mOpQueue.IsEmpty()) {
mOpQueue.SwapElements(aOpQueue);
return;
} else {
mOpQueue.MoveElementsFrom(aOpQueue);
}
mOpQueue.MoveElementsFrom(aOpQueue);
}
void
nsHtml5TreeOpStage::MoveOpsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue)
nsHtml5TreeOpStage::MoveOpsAndSpeculativeLoadsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue,
nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue)
{
mozilla::MutexAutoLock autoLock(mMutex);
if (aOpQueue.IsEmpty()) {
mOpQueue.SwapElements(aOpQueue);
return;
} else {
aOpQueue.MoveElementsFrom(mOpQueue);
}
if (aSpeculativeLoadQueue.IsEmpty()) {
mSpeculativeLoadQueue.SwapElements(aSpeculativeLoadQueue);
} else {
aSpeculativeLoadQueue.MoveElementsFrom(mSpeculativeLoadQueue);
}
}
void
nsHtml5TreeOpStage::MoveSpeculativeLoadsFrom(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue)
{
mozilla::MutexAutoLock autoLock(mMutex);
if (mSpeculativeLoadQueue.IsEmpty()) {
mSpeculativeLoadQueue.SwapElements(aSpeculativeLoadQueue);
} else {
mSpeculativeLoadQueue.MoveElementsFrom(aSpeculativeLoadQueue);
}
}
void
nsHtml5TreeOpStage::MoveSpeculativeLoadsTo(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue)
{
mozilla::MutexAutoLock autoLock(mMutex);
if (aSpeculativeLoadQueue.IsEmpty()) {
mSpeculativeLoadQueue.SwapElements(aSpeculativeLoadQueue);
} else {
aSpeculativeLoadQueue.MoveElementsFrom(mSpeculativeLoadQueue);
}
aOpQueue.MoveElementsFrom(mOpQueue);
}
#ifdef DEBUG

Просмотреть файл

@ -42,6 +42,7 @@
#include "nsHtml5TreeOperation.h"
#include "nsTArray.h"
#include "nsAHtml5TreeOpSink.h"
#include "nsHtml5SpeculativeLoad.h"
class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
public:
@ -57,9 +58,20 @@ class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
/**
* Retrieve the staged operations into the argument.
* Retrieve the staged operations and speculative loads into the arguments.
*/
void MoveOpsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue);
void MoveOpsAndSpeculativeLoadsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue,
nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue);
/**
* Move the speculative loads from the argument into the staging queue.
*/
void MoveSpeculativeLoadsFrom(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue);
/**
* Retrieve the staged speculative loads into the argument.
*/
void MoveSpeculativeLoadsTo(nsTArray<nsHtml5SpeculativeLoad>& aSpeculativeLoadQueue);
#ifdef DEBUG
void AssertEmpty();
@ -67,7 +79,8 @@ class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
private:
nsTArray<nsHtml5TreeOperation> mOpQueue;
mozilla::Mutex mMutex;
nsTArray<nsHtml5SpeculativeLoad> mSpeculativeLoadQueue;
mozilla::Mutex mMutex;
};

Просмотреть файл

@ -47,7 +47,6 @@
#include "nsAHtml5EncodingDeclarationHandler.h"
class nsHtml5StreamParser;
class nsHtml5SpeculativeLoader;
class nsHtml5Tokenizer;
class nsHtml5TreeBuilder;