mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 502568 - HTML5 parser should flush occasionally when loading pure text. r=bnewman.
--HG-- extra : rebase_source : f1de8b7729f1570bf859578bf04192a4c5b6df05
This commit is contained in:
parent
0f1b837b09
commit
f7e733583b
@ -2824,3 +2824,12 @@ pref("geo.enabled", true);
|
||||
pref("html5.enable", false);
|
||||
// Toggle which thread the HTML5 parser uses for streama parsing
|
||||
pref("html5.offmainthread", true);
|
||||
// Time in milliseconds between the start of the network stream and the
|
||||
// first time the flush timer fires in the off-the-main-thread HTML5 parser.
|
||||
pref("html5.flushtimer.startdelay", 200);
|
||||
// Time in milliseconds between the return to non-speculating more and the
|
||||
// first time the flush timer fires thereafter.
|
||||
pref("html5.flushtimer.continuedelay", 150);
|
||||
// Time in milliseconds between timer firings once the timer has starting
|
||||
// firing.
|
||||
pref("html5.flushtimer.interval", 100);
|
||||
|
@ -48,15 +48,9 @@ class nsAHtml5TreeOpSink {
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue if flushing is not expensive.
|
||||
* queue into this sink unconditionally.
|
||||
*/
|
||||
virtual void MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue) = 0;
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue unconditionally.
|
||||
*/
|
||||
virtual void ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue) = 0;
|
||||
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -71,6 +71,7 @@ nsHtml5Module::InitializeStatics()
|
||||
nsHtml5Tokenizer::initializeStatics();
|
||||
nsHtml5TreeBuilder::initializeStatics();
|
||||
nsHtml5UTF16Buffer::initializeStatics();
|
||||
nsHtml5StreamParser::InitializeStatics();
|
||||
#ifdef DEBUG
|
||||
sNsHtml5ModuleInitialized = PR_TRUE;
|
||||
#endif
|
||||
|
@ -55,13 +55,7 @@ nsHtml5Speculation::~nsHtml5Speculation()
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5Speculation::MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5Speculation::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
nsHtml5Speculation::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
mOpQueue.SwapElements(aOpQueue);
|
||||
@ -73,5 +67,5 @@ nsHtml5Speculation::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
void
|
||||
nsHtml5Speculation::FlushToSink(nsAHtml5TreeOpSink* aSink)
|
||||
{
|
||||
aSink->ForcedFlush(mOpQueue);
|
||||
aSink->MoveOpsFrom(mOpQueue);
|
||||
}
|
||||
|
@ -71,16 +71,11 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink
|
||||
return mSnapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op.
|
||||
*/
|
||||
virtual void MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue unconditionally.
|
||||
*/
|
||||
virtual void ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
void FlushToSink(nsAHtml5TreeOpSink* aSink);
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsHtml5AtomTable.h"
|
||||
#include "nsHtml5Speculation.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
class nsHtml5Parser;
|
||||
|
||||
@ -106,12 +107,15 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
||||
friend class nsHtml5RequestStopper;
|
||||
friend class nsHtml5DataAvailable;
|
||||
friend class nsHtml5StreamParserContinuation;
|
||||
friend class nsHtml5StreamParserTimerFlusher;
|
||||
|
||||
public:
|
||||
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5StreamParser, nsIStreamListener)
|
||||
|
||||
static void InitializeStatics();
|
||||
|
||||
nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
|
||||
nsHtml5Parser* aOwner);
|
||||
|
||||
@ -301,6 +305,23 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
||||
nsresult SetupDecodingFromBom(const char* aCharsetName,
|
||||
const char* aDecoderCharsetName);
|
||||
|
||||
/**
|
||||
* Callback for mFlushTimer.
|
||||
*/
|
||||
static void TimerCallback(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
/**
|
||||
* Main thread entry point for (maybe) flushing the ops and posting
|
||||
* a flush runnable back on the main thread.
|
||||
*/
|
||||
void PostTimerFlush();
|
||||
|
||||
/**
|
||||
* Parser thread entry point for (maybe) flushing the ops and posting
|
||||
* a flush runnable back on the main thread.
|
||||
*/
|
||||
void TimerFlush();
|
||||
|
||||
nsCOMPtr<nsIRequest> mRequest;
|
||||
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||
|
||||
@ -435,6 +456,31 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
||||
* The document wrapped by the speculative loader.
|
||||
*/
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
|
||||
/**
|
||||
* Timer for flushing tree ops once in a while when not speculating.
|
||||
*/
|
||||
nsCOMPtr<nsITimer> mFlushTimer;
|
||||
|
||||
/**
|
||||
* The pref html5.flushtimer.startdelay: Time in milliseconds between
|
||||
* the start of the network stream and the first time the flush timer
|
||||
* fires.
|
||||
*/
|
||||
static PRInt32 sTimerStartDelay;
|
||||
|
||||
/**
|
||||
* The pref html5.flushtimer.continuedelay: Time in milliseconds between
|
||||
* the return to non-speculating more and the first time the flush timer
|
||||
* fires thereafter.
|
||||
*/
|
||||
static PRInt32 sTimerContinueDelay;
|
||||
|
||||
/**
|
||||
* The pref html5.flushtimer.interval: Time in milliseconds between
|
||||
* timer firings once the timer has starting firing.
|
||||
*/
|
||||
static PRInt32 sTimerInterval;
|
||||
};
|
||||
|
||||
#endif // nsHtml5StreamParser_h__
|
||||
|
@ -314,15 +314,9 @@ nsHtml5TreeBuilder::insertFosterParentedCharacters(PRUnichar* aBuffer, PRInt32 a
|
||||
PRUnichar* bufferCopy = new PRUnichar[aLength];
|
||||
memcpy(bufferCopy, aBuffer, aLength * sizeof(PRUnichar));
|
||||
|
||||
nsIContent** text = AllocateContentHandle();
|
||||
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpCreateTextNode, bufferCopy, aLength, text);
|
||||
|
||||
treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpFosterParent, text, aStackParent, aTable);
|
||||
treeOp->Init(eTreeOpFosterParentText, bufferCopy, aLength, aStackParent, aTable);
|
||||
}
|
||||
|
||||
void
|
||||
@ -346,15 +340,9 @@ nsHtml5TreeBuilder::appendCharacters(nsIContent** aParent, PRUnichar* aBuffer, P
|
||||
PRUnichar* bufferCopy = new PRUnichar[aLength];
|
||||
memcpy(bufferCopy, aBuffer, aLength * sizeof(PRUnichar));
|
||||
|
||||
nsIContent** text = AllocateContentHandle();
|
||||
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpCreateTextNode, bufferCopy, aLength, text);
|
||||
|
||||
treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpAppend, text, aParent);
|
||||
treeOp->Init(eTreeOpAppendText, bufferCopy, aLength, aParent);
|
||||
}
|
||||
|
||||
void
|
||||
@ -366,15 +354,9 @@ nsHtml5TreeBuilder::appendComment(nsIContent** aParent, PRUnichar* aBuffer, PRIn
|
||||
PRUnichar* bufferCopy = new PRUnichar[aLength];
|
||||
memcpy(bufferCopy, aBuffer, aLength * sizeof(PRUnichar));
|
||||
|
||||
nsIContent** comment = AllocateContentHandle();
|
||||
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpCreateComment, bufferCopy, aLength, comment);
|
||||
|
||||
treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpAppend, comment, aParent);
|
||||
treeOp->Init(eTreeOpAppendComment, bufferCopy, aLength, aParent);
|
||||
}
|
||||
|
||||
void
|
||||
@ -385,15 +367,9 @@ nsHtml5TreeBuilder::appendCommentToDocument(PRUnichar* aBuffer, PRInt32 aStart,
|
||||
PRUnichar* bufferCopy = new PRUnichar[aLength];
|
||||
memcpy(bufferCopy, aBuffer, aLength * sizeof(PRUnichar));
|
||||
|
||||
nsIContent** comment = AllocateContentHandle();
|
||||
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpCreateComment, bufferCopy, aLength, comment);
|
||||
|
||||
treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpAppendToDocument, comment);
|
||||
treeOp->Init(eTreeOpAppendCommentToDocument, bufferCopy, aLength);
|
||||
}
|
||||
|
||||
void
|
||||
@ -442,15 +418,9 @@ nsHtml5TreeBuilder::appendDoctypeToDocument(nsIAtom* aName, nsString* aPublicId,
|
||||
{
|
||||
NS_PRECONDITION(aName, "Null name");
|
||||
|
||||
nsIContent** content = AllocateContentHandle();
|
||||
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(aName, *aPublicId, *aSystemId, content);
|
||||
|
||||
treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(eTreeOpAppendToDocument, content);
|
||||
treeOp->Init(aName, *aPublicId, *aSystemId);
|
||||
// nsXMLContentSink can flush here, but what's the point?
|
||||
// It can also interrupt here, but we can't.
|
||||
}
|
||||
@ -614,16 +584,14 @@ nsHtml5TreeBuilder::HasScript()
|
||||
return mOpQueue.ElementAt(len - 1).IsRunScript();
|
||||
}
|
||||
|
||||
void
|
||||
PRBool
|
||||
nsHtml5TreeBuilder::Flush()
|
||||
{
|
||||
mOpSink->ForcedFlush(mOpQueue);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::MaybeFlush()
|
||||
{
|
||||
mOpSink->MaybeFlush(mOpQueue);
|
||||
PRBool hasOps = !mOpQueue.IsEmpty();
|
||||
if (hasOps) {
|
||||
mOpSink->MoveOpsFrom(mOpQueue);
|
||||
}
|
||||
return hasOps;
|
||||
}
|
||||
|
||||
void
|
||||
@ -676,6 +644,14 @@ nsHtml5TreeBuilder::DropSpeculativeLoader() {
|
||||
mSpeculativeLoader = nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHtml5TreeBuilder::IsDiscretionaryFlushSafe()
|
||||
{
|
||||
return !(charBufferLen &&
|
||||
currentPtr >= 0 &&
|
||||
stack[currentPtr]->fosterParenting);
|
||||
}
|
||||
|
||||
// DocumentModeHandler
|
||||
void
|
||||
nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m)
|
||||
|
@ -63,6 +63,8 @@
|
||||
|
||||
~nsHtml5TreeBuilder();
|
||||
|
||||
PRBool IsDiscretionaryFlushSafe();
|
||||
|
||||
PRBool HasScript();
|
||||
|
||||
void SetOpSink(nsAHtml5TreeOpSink* aOpSink) {
|
||||
@ -77,9 +79,7 @@
|
||||
|
||||
void DropSpeculativeLoader();
|
||||
|
||||
void Flush();
|
||||
|
||||
void MaybeFlush();
|
||||
PRBool Flush();
|
||||
|
||||
void SetDocumentCharset(nsACString& aCharset);
|
||||
|
||||
|
@ -60,7 +60,6 @@
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_MAX_QUEUE_TIME 3000UL // milliseconds
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_DEFAULT_QUEUE_LENGTH 200
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH 100
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_MAX_TIME_WITHOUT_FLUSH 5000 // milliseconds
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5TreeOpExecutor)
|
||||
|
||||
@ -74,39 +73,21 @@ NS_IMPL_ADDREF_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFlushTimer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mOwnedElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mOwnedNonElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
|
||||
if (tmp->mFlushTimer) {
|
||||
tmp->mFlushTimer->Cancel();
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFlushTimer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mOwnedElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mOwnedNonElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
|
||||
: mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
|
||||
{
|
||||
// zeroing operator new for everything else
|
||||
// zeroing operator new for everything
|
||||
}
|
||||
|
||||
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
|
||||
{
|
||||
NS_ASSERTION(mOpQueue.IsEmpty(), "Somehow there's stuff in the op queue.");
|
||||
if (mFlushTimer) {
|
||||
mFlushTimer->Cancel(); // XXX why is this even necessary? it is, though.
|
||||
}
|
||||
mFlushTimer = nsnull;
|
||||
}
|
||||
|
||||
static void
|
||||
TimerCallbackFunc(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
(static_cast<nsHtml5TreeOpExecutor*> (aClosure))->Flush();
|
||||
}
|
||||
|
||||
// nsIContentSink
|
||||
@ -283,7 +264,6 @@ void
|
||||
nsHtml5TreeOpExecutor::Flush()
|
||||
{
|
||||
if (!mParser) {
|
||||
mFlushTimer->Cancel();
|
||||
return;
|
||||
}
|
||||
if (mFlushState != eNotFlushing) {
|
||||
@ -296,7 +276,7 @@ nsHtml5TreeOpExecutor::Flush()
|
||||
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
|
||||
|
||||
if (mReadingFromStage) {
|
||||
mStage.RetrieveOperations(mOpQueue);
|
||||
mStage.MoveOpsTo(mOpQueue);
|
||||
}
|
||||
|
||||
nsIContent* scriptElement = nsnull;
|
||||
@ -348,23 +328,11 @@ nsHtml5TreeOpExecutor::Flush()
|
||||
return;
|
||||
}
|
||||
|
||||
ScheduleTimer();
|
||||
|
||||
if (scriptElement) {
|
||||
RunScript(scriptElement); // must be tail call when mFlushState is eNotFlushing
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::ScheduleTimer()
|
||||
{
|
||||
mFlushTimer->Cancel();
|
||||
mFlushTimer->InitWithFuncCallback(TimerCallbackFunc,
|
||||
static_cast<void*> (this),
|
||||
NS_HTML5_TREE_OP_EXECUTOR_MAX_TIME_WITHOUT_FLUSH,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHtml5TreeOpExecutor::ProcessBASETag(nsIContent* aContent)
|
||||
{
|
||||
@ -514,7 +482,6 @@ nsHtml5TreeOpExecutor::Start()
|
||||
{
|
||||
NS_PRECONDITION(!mStarted, "Tried to start when already started.");
|
||||
mStarted = PR_TRUE;
|
||||
ScheduleTimer();
|
||||
}
|
||||
|
||||
void
|
||||
@ -572,13 +539,7 @@ nsHtml5TreeOpExecutor::Reset() {
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
nsHtml5TreeOpExecutor::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
NS_PRECONDITION(mFlushState == eNotFlushing, "mOpQueue modified during tree op execution.");
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
|
@ -50,7 +50,6 @@
|
||||
#include "nsContentSink.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsHtml5DocumentMode.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIScriptElement.h"
|
||||
#include "nsIParser.h"
|
||||
#include "nsCOMArray.h"
|
||||
@ -95,18 +94,12 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
||||
|
||||
PRBool mHasProcessedBase;
|
||||
PRBool mReadingFromStage;
|
||||
nsCOMPtr<nsITimer> mFlushTimer;
|
||||
nsTArray<nsHtml5TreeOperation> mOpQueue;
|
||||
nsTArray<nsIContentPtr> mElementsSeenInThisAppendBatch;
|
||||
nsTArray<nsHtml5PendingNotification> mPendingNotifications;
|
||||
nsHtml5StreamParser* mStreamParser;
|
||||
nsCOMArray<nsIContent> mOwnedElements;
|
||||
|
||||
// This could be optimized away by introducing more tree ops so that
|
||||
// non-elements wouldn't use the handle setup but the text node / comment
|
||||
// / doctype operand would be remembered by the tree op executor.
|
||||
nsCOMArray<nsIContent> mOwnedNonElements;
|
||||
|
||||
/**
|
||||
* Whether the parser has started
|
||||
*/
|
||||
@ -300,21 +293,21 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
||||
mFlushState = eInDocUpdate;
|
||||
}
|
||||
|
||||
inline PRBool HaveNotified(nsIContent* aElement) {
|
||||
NS_PRECONDITION(aElement, "HaveNotified called with null argument.");
|
||||
inline PRBool HaveNotified(nsIContent* aNode) {
|
||||
NS_PRECONDITION(aNode, "HaveNotified called with null argument.");
|
||||
const nsHtml5PendingNotification* start = mPendingNotifications.Elements();
|
||||
const nsHtml5PendingNotification* end = start + mPendingNotifications.Length();
|
||||
for (;;) {
|
||||
nsIContent* parent = aElement->GetParent();
|
||||
nsIContent* parent = aNode->GetParent();
|
||||
if (!parent) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) {
|
||||
if (iter->Contains(parent)) {
|
||||
return iter->HaveNotifiedIndex(parent->IndexOf(aElement));
|
||||
return iter->HaveNotifiedIndex(parent->IndexOf(aNode));
|
||||
}
|
||||
}
|
||||
aElement = parent;
|
||||
aNode = parent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,22 +352,13 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
||||
mOwnedElements.AppendObject(aContent);
|
||||
}
|
||||
|
||||
inline void HoldNonElement(nsIContent* aContent) {
|
||||
mOwnedNonElements.AppendObject(aContent);
|
||||
}
|
||||
|
||||
// The following two methods are for the main-thread case
|
||||
|
||||
/**
|
||||
* No-op
|
||||
*/
|
||||
virtual void MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue unconditionally.
|
||||
*/
|
||||
virtual void ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
nsAHtml5TreeOpSink* GetStage() {
|
||||
return &mStage;
|
||||
@ -386,8 +370,6 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
||||
|
||||
void StreamEnded();
|
||||
|
||||
void ScheduleTimer();
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertStageEmpty() {
|
||||
mStage.AssertEmpty();
|
||||
|
@ -47,16 +47,7 @@ nsHtml5TreeOpStage::~nsHtml5TreeOpStage()
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpStage::MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
mOpQueue.SwapElements(aOpQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpStage::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
nsHtml5TreeOpStage::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
@ -67,7 +58,7 @@ nsHtml5TreeOpStage::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeOpStage::RetrieveOperations(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
nsHtml5TreeOpStage::MoveOpsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
if (aOpQueue.IsEmpty()) {
|
||||
|
@ -50,22 +50,16 @@ class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
|
||||
|
||||
~nsHtml5TreeOpStage();
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue if flushing is not expensive.
|
||||
*/
|
||||
virtual void MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
/**
|
||||
* Flush the operations from the tree operations from the argument
|
||||
* queue unconditionally.
|
||||
*/
|
||||
virtual void ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
/**
|
||||
* Retrieve the staged operations into the argument.
|
||||
*/
|
||||
void RetrieveOperations(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
void MoveOpsTo(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertEmpty();
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include "nsIFormControl.h"
|
||||
#include "nsIStyleSheetLinkingElement.h"
|
||||
#include "nsIDOMDocumentType.h"
|
||||
#include "nsIMutationObserver.h"
|
||||
|
||||
/**
|
||||
* Helper class that opens a notification batch if the current doc
|
||||
@ -105,11 +106,13 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation()
|
||||
case eTreeOpCreateElement:
|
||||
delete mThree.attributes;
|
||||
break;
|
||||
case eTreeOpCreateDoctype:
|
||||
case eTreeOpAppendDoctypeToDocument:
|
||||
delete mTwo.stringPair;
|
||||
break;
|
||||
case eTreeOpCreateTextNode:
|
||||
case eTreeOpCreateComment:
|
||||
case eTreeOpFosterParentText:
|
||||
case eTreeOpAppendText:
|
||||
case eTreeOpAppendComment:
|
||||
case eTreeOpAppendCommentToDocument:
|
||||
delete[] mTwo.unicharPtr;
|
||||
break;
|
||||
case eTreeOpSetDocumentCharset:
|
||||
@ -121,6 +124,107 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation()
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHtml5TreeOperation::AppendTextToTextNode(PRUnichar* aBuffer,
|
||||
PRInt32 aLength,
|
||||
nsIContent* aTextNode,
|
||||
nsHtml5TreeOpExecutor* aBuilder)
|
||||
{
|
||||
NS_PRECONDITION(aTextNode, "Got null text node.");
|
||||
|
||||
if (aBuilder->HaveNotified(aTextNode)) {
|
||||
// This text node has already been notified on, so it's necessary to
|
||||
// notify on the append
|
||||
nsresult rv = NS_OK;
|
||||
PRUint32 oldLength = aTextNode->TextLength();
|
||||
CharacterDataChangeInfo info = {
|
||||
PR_TRUE,
|
||||
oldLength,
|
||||
oldLength,
|
||||
aLength
|
||||
};
|
||||
nsNodeUtils::CharacterDataWillChange(aTextNode, &info);
|
||||
|
||||
rv = aTextNode->AppendText(aBuffer, aLength, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsNodeUtils::CharacterDataChanged(aTextNode, &info);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return aTextNode->AppendText(aBuffer, aLength, PR_FALSE);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsHtml5TreeOperation::AppendText(PRUnichar* aBuffer,
|
||||
PRInt32 aLength,
|
||||
nsIContent* aParent,
|
||||
nsHtml5TreeOpExecutor* aBuilder)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
nsIContent* lastChild = aParent->GetLastChild();
|
||||
if (lastChild && lastChild->IsNodeOfType(nsINode::eTEXT)) {
|
||||
nsHtml5OtherDocUpdate update(aParent->GetOwnerDoc(),
|
||||
aBuilder->GetDocument());
|
||||
return AppendTextToTextNode(aBuffer,
|
||||
aLength,
|
||||
lastChild,
|
||||
aBuilder);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> text;
|
||||
NS_NewTextNode(getter_AddRefs(text), aBuilder->GetNodeInfoManager());
|
||||
NS_ASSERTION(text, "Infallible malloc failed?");
|
||||
rv = text->SetText(aBuffer, aLength, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return Append(text, aParent, aBuilder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHtml5TreeOperation::Append(nsIContent* aNode,
|
||||
nsIContent* aParent,
|
||||
nsHtml5TreeOpExecutor* aBuilder)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
nsIDocument* executorDoc = aBuilder->GetDocument();
|
||||
NS_ASSERTION(executorDoc, "Null doc on executor");
|
||||
nsIDocument* parentDoc = aParent->GetOwnerDoc();
|
||||
NS_ASSERTION(parentDoc, "Null owner doc on old node.");
|
||||
|
||||
if (NS_LIKELY(executorDoc == parentDoc)) {
|
||||
// the usual case. the parent is in the parser's doc
|
||||
aBuilder->PostPendingAppendNotification(aParent, aNode);
|
||||
rv = aParent->AppendChildTo(aNode, PR_FALSE);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// The parent has been moved to another doc
|
||||
parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
|
||||
|
||||
PRUint32 childCount = aParent->GetChildCount();
|
||||
rv = aParent->AppendChildTo(aNode, PR_FALSE);
|
||||
nsNodeUtils::ContentAppended(aParent, childCount);
|
||||
|
||||
parentDoc->EndUpdate(UPDATE_CONTENT_MODEL);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHtml5TreeOperation::AppendToDocument(nsIContent* aNode,
|
||||
nsHtml5TreeOpExecutor* aBuilder)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
aBuilder->FlushPendingAppendNotifications();
|
||||
nsIDocument* doc = aBuilder->GetDocument();
|
||||
PRUint32 childCount = doc->GetChildCount();
|
||||
rv = doc->AppendChildTo(aNode, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsNodeUtils::ContentInserted(doc, aNode, childCount);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
|
||||
nsIContent** aScriptElement)
|
||||
@ -130,28 +234,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
|
||||
case eTreeOpAppend: {
|
||||
nsIContent* node = *(mOne.node);
|
||||
nsIContent* parent = *(mTwo.node);
|
||||
|
||||
nsIDocument* executorDoc = aBuilder->GetDocument();
|
||||
NS_ASSERTION(executorDoc, "Null doc on executor");
|
||||
nsIDocument* parentDoc = parent->GetOwnerDoc();
|
||||
NS_ASSERTION(parentDoc, "Null owner doc on old node.");
|
||||
|
||||
if (NS_LIKELY(executorDoc == parentDoc)) {
|
||||
// the usual case. the parent is in the parser's doc
|
||||
aBuilder->PostPendingAppendNotification(parent, node);
|
||||
rv = parent->AppendChildTo(node, PR_FALSE);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// The parent has been moved to another doc
|
||||
parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
|
||||
|
||||
PRUint32 childCount = parent->GetChildCount();
|
||||
rv = parent->AppendChildTo(node, PR_FALSE);
|
||||
nsNodeUtils::ContentAppended(parent, childCount);
|
||||
|
||||
parentDoc->EndUpdate(UPDATE_CONTENT_MODEL);
|
||||
return rv;
|
||||
return Append(node, parent, aBuilder);
|
||||
}
|
||||
case eTreeOpDetach: {
|
||||
nsIContent* node = *(mOne.node);
|
||||
@ -209,37 +292,11 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsIDocument* executorDoc = aBuilder->GetDocument();
|
||||
NS_ASSERTION(executorDoc, "Null doc on executor");
|
||||
nsIDocument* parentDoc = parent->GetOwnerDoc();
|
||||
NS_ASSERTION(parentDoc, "Null owner doc on old node.");
|
||||
|
||||
if (NS_LIKELY(executorDoc == parentDoc)) {
|
||||
// the usual case. the parent is in the parser's doc
|
||||
aBuilder->PostPendingAppendNotification(parent, node);
|
||||
rv = parent->AppendChildTo(node, PR_FALSE);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// The parent has been moved to another doc
|
||||
parentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);
|
||||
|
||||
PRUint32 childCount = parent->GetChildCount();
|
||||
rv = parent->AppendChildTo(node, PR_FALSE);
|
||||
nsNodeUtils::ContentAppended(parent, childCount);
|
||||
|
||||
parentDoc->EndUpdate(UPDATE_CONTENT_MODEL);
|
||||
return rv;
|
||||
return Append(node, parent, aBuilder);
|
||||
}
|
||||
case eTreeOpAppendToDocument: {
|
||||
nsIContent* node = *(mOne.node);
|
||||
aBuilder->FlushPendingAppendNotifications();
|
||||
nsIDocument* doc = aBuilder->GetDocument();
|
||||
PRUint32 childCount = doc->GetChildCount();
|
||||
rv = doc->AppendChildTo(node, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsNodeUtils::ContentInserted(doc, node, childCount);
|
||||
return rv;
|
||||
return AppendToDocument(node, aBuilder);
|
||||
}
|
||||
case eTreeOpAddAttributes: {
|
||||
nsIContent* node = *(mOne.node);
|
||||
@ -348,41 +405,81 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
case eTreeOpCreateTextNode: {
|
||||
nsIContent** target = mOne.node;
|
||||
case eTreeOpAppendText: {
|
||||
nsIContent* parent = *mOne.node;
|
||||
PRUnichar* buffer = mTwo.unicharPtr;
|
||||
PRInt32 length = mInt;
|
||||
|
||||
nsCOMPtr<nsIContent> text;
|
||||
NS_NewTextNode(getter_AddRefs(text), aBuilder->GetNodeInfoManager());
|
||||
// XXX nsresult and comment null check?
|
||||
text->SetText(buffer, length, PR_FALSE);
|
||||
// XXX nsresult
|
||||
|
||||
aBuilder->HoldNonElement(*target = text);
|
||||
return rv;
|
||||
return AppendText(buffer, length, parent, aBuilder);
|
||||
}
|
||||
case eTreeOpCreateComment: {
|
||||
nsIContent** target = mOne.node;
|
||||
case eTreeOpFosterParentText: {
|
||||
nsIContent* stackParent = *mOne.node;
|
||||
PRUnichar* buffer = mTwo.unicharPtr;
|
||||
PRInt32 length = mInt;
|
||||
nsIContent* table = *mThree.node;
|
||||
|
||||
nsIContent* foster = table->GetParent();
|
||||
|
||||
if (foster && foster->IsNodeOfType(nsINode::eELEMENT)) {
|
||||
aBuilder->FlushPendingAppendNotifications();
|
||||
|
||||
nsHtml5OtherDocUpdate update(foster->GetOwnerDoc(),
|
||||
aBuilder->GetDocument());
|
||||
|
||||
PRUint32 pos = foster->IndexOf(table);
|
||||
|
||||
nsIContent* previousSibling = foster->GetChildAt(pos - 1);
|
||||
if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) {
|
||||
return AppendTextToTextNode(buffer,
|
||||
length,
|
||||
previousSibling,
|
||||
aBuilder);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> text;
|
||||
NS_NewTextNode(getter_AddRefs(text), aBuilder->GetNodeInfoManager());
|
||||
NS_ASSERTION(text, "Infallible malloc failed?");
|
||||
rv = text->SetText(buffer, length, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = foster->InsertChildAt(text, pos, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsNodeUtils::ContentInserted(foster, text, pos);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return AppendText(buffer, length, stackParent, aBuilder);
|
||||
}
|
||||
case eTreeOpAppendComment: {
|
||||
nsIContent* parent = *mOne.node;
|
||||
PRUnichar* buffer = mTwo.unicharPtr;
|
||||
PRInt32 length = mInt;
|
||||
|
||||
nsCOMPtr<nsIContent> comment;
|
||||
NS_NewCommentNode(getter_AddRefs(comment), aBuilder->GetNodeInfoManager());
|
||||
// XXX nsresult and comment null check?
|
||||
comment->SetText(buffer, length, PR_FALSE);
|
||||
// XXX nsresult
|
||||
NS_ASSERTION(comment, "Infallible malloc failed?");
|
||||
rv = comment->SetText(buffer, length, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aBuilder->HoldNonElement(*target = comment);
|
||||
return rv;
|
||||
return Append(comment, parent, aBuilder);
|
||||
}
|
||||
case eTreeOpCreateDoctype: {
|
||||
case eTreeOpAppendCommentToDocument: {
|
||||
PRUnichar* buffer = mTwo.unicharPtr;
|
||||
PRInt32 length = mInt;
|
||||
|
||||
nsCOMPtr<nsIContent> comment;
|
||||
NS_NewCommentNode(getter_AddRefs(comment), aBuilder->GetNodeInfoManager());
|
||||
NS_ASSERTION(comment, "Infallible malloc failed?");
|
||||
rv = comment->SetText(buffer, length, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return AppendToDocument(comment, aBuilder);
|
||||
}
|
||||
case eTreeOpAppendDoctypeToDocument: {
|
||||
nsCOMPtr<nsIAtom> name = Reget(mOne.atom);
|
||||
nsHtml5TreeOperationStringPair* pair = mTwo.stringPair;
|
||||
nsString publicId;
|
||||
nsString systemId;
|
||||
pair->Get(publicId, systemId);
|
||||
nsIContent** target = mThree.node;
|
||||
|
||||
// Adapted from nsXMLContentSink
|
||||
// Create a new doctype node
|
||||
@ -400,8 +497,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
|
||||
voidString);
|
||||
NS_ASSERTION(docType, "Doctype creation failed.");
|
||||
nsCOMPtr<nsIContent> asContent = do_QueryInterface(docType);
|
||||
aBuilder->HoldNonElement(*target = asContent);
|
||||
return rv;
|
||||
return AppendToDocument(asContent, aBuilder);
|
||||
}
|
||||
case eTreeOpRunScript: {
|
||||
nsIContent* node = *(mOne.node);
|
||||
|
@ -59,9 +59,11 @@ enum eHtml5TreeOperation {
|
||||
eTreeOpDocumentMode,
|
||||
eTreeOpCreateElement,
|
||||
eTreeOpSetFormElement,
|
||||
eTreeOpCreateTextNode,
|
||||
eTreeOpCreateComment,
|
||||
eTreeOpCreateDoctype,
|
||||
eTreeOpAppendText,
|
||||
eTreeOpFosterParentText,
|
||||
eTreeOpAppendComment,
|
||||
eTreeOpAppendCommentToDocument,
|
||||
eTreeOpAppendDoctypeToDocument,
|
||||
// Gecko-specific on-pop ops
|
||||
eTreeOpRunScript,
|
||||
eTreeOpDoneAddingChildren,
|
||||
@ -188,12 +190,38 @@ class nsHtml5TreeOperation {
|
||||
inline void Init(eHtml5TreeOperation aOpCode,
|
||||
PRUnichar* aBuffer,
|
||||
PRInt32 aLength,
|
||||
nsIContent** aTarget) {
|
||||
nsIContent** aStackParent,
|
||||
nsIContent** aTable) {
|
||||
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
|
||||
"Op code must be uninitialized when initializing.");
|
||||
NS_PRECONDITION(aBuffer, "Initialized tree op with null buffer.");
|
||||
mOpCode = aOpCode;
|
||||
mOne.node = aStackParent;
|
||||
mTwo.unicharPtr = aBuffer;
|
||||
mThree.node = aTable;
|
||||
mInt = aLength;
|
||||
}
|
||||
|
||||
inline void Init(eHtml5TreeOperation aOpCode,
|
||||
PRUnichar* aBuffer,
|
||||
PRInt32 aLength,
|
||||
nsIContent** aParent) {
|
||||
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
|
||||
"Op code must be uninitialized when initializing.");
|
||||
NS_PRECONDITION(aBuffer, "Initialized tree op with null buffer.");
|
||||
mOpCode = aOpCode;
|
||||
mOne.node = aParent;
|
||||
mTwo.unicharPtr = aBuffer;
|
||||
mInt = aLength;
|
||||
}
|
||||
|
||||
inline void Init(eHtml5TreeOperation aOpCode,
|
||||
PRUnichar* aBuffer,
|
||||
PRInt32 aLength) {
|
||||
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
|
||||
"Op code must be uninitialized when initializing.");
|
||||
NS_PRECONDITION(aBuffer, "Initialized tree op with null buffer.");
|
||||
mOpCode = aOpCode;
|
||||
mOne.node = aTarget;
|
||||
mTwo.unicharPtr = aBuffer;
|
||||
mInt = aLength;
|
||||
}
|
||||
@ -210,13 +238,12 @@ class nsHtml5TreeOperation {
|
||||
|
||||
inline void Init(nsIAtom* aName,
|
||||
const nsAString& aPublicId,
|
||||
const nsAString& aSystemId, nsIContent** aTarget) {
|
||||
const nsAString& aSystemId) {
|
||||
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
|
||||
"Op code must be uninitialized when initializing.");
|
||||
mOpCode = eTreeOpCreateDoctype;
|
||||
mOpCode = eTreeOpAppendDoctypeToDocument;
|
||||
mOne.atom = aName;
|
||||
mTwo.stringPair = new nsHtml5TreeOperationStringPair(aPublicId, aSystemId);
|
||||
mThree.node = aTarget;
|
||||
}
|
||||
|
||||
inline void Init(eHtml5TreeOperation aOpCode, const nsACString& aString) {
|
||||
@ -270,6 +297,23 @@ class nsHtml5TreeOperation {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
nsresult AppendTextToTextNode(PRUnichar* aBuffer,
|
||||
PRInt32 aLength,
|
||||
nsIContent* aTextNode,
|
||||
nsHtml5TreeOpExecutor* aBuilder);
|
||||
|
||||
nsresult AppendText(PRUnichar* aBuffer,
|
||||
PRInt32 aLength,
|
||||
nsIContent* aParent,
|
||||
nsHtml5TreeOpExecutor* aBuilder);
|
||||
|
||||
nsresult Append(nsIContent* aNode,
|
||||
nsIContent* aParent,
|
||||
nsHtml5TreeOpExecutor* aBuilder);
|
||||
|
||||
nsresult AppendToDocument(nsIContent* aNode,
|
||||
nsHtml5TreeOpExecutor* aBuilder);
|
||||
|
||||
// possible optimization:
|
||||
// Make the queue take items the size of pointer and make the op code
|
||||
|
Loading…
Reference in New Issue
Block a user