зеркало из https://github.com/mozilla/gecko-dev.git
Bug 482919 - Add speculative parsing to the HTML5 parser. r=bnewman.
--HG-- extra : rebase_source : d8b0840910d47aadee767fcbecf6832d244b1c4b
This commit is contained in:
Родитель
213aca73bc
Коммит
98a6ad4f84
|
@ -73,6 +73,8 @@ CPPSRCS = \
|
|||
nsHtml5StateSnapshot.cpp \
|
||||
nsHtml5TreeOpExecutor.cpp \
|
||||
nsHtml5StreamParser.cpp \
|
||||
nsHtml5Speculation.cpp \
|
||||
nsHtml5SpeculativeLoader.cpp \
|
||||
$(NULL)
|
||||
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
|
|
@ -119,11 +119,7 @@ nsHtml5Parser::nsHtml5Parser()
|
|||
nsHtml5Parser::~nsHtml5Parser()
|
||||
{
|
||||
mTokenizer->end();
|
||||
while (mFirstBuffer) {
|
||||
nsHtml5UTF16Buffer* old = mFirstBuffer;
|
||||
mFirstBuffer = mFirstBuffer->next;
|
||||
delete old;
|
||||
}
|
||||
mFirstBuffer = nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
|
@ -161,7 +157,7 @@ nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
|
|||
NS_IMETHODIMP_(void)
|
||||
nsHtml5Parser::SetDocumentCharset(const nsACString& aCharset, PRInt32 aCharsetSource)
|
||||
{
|
||||
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED,
|
||||
NS_PRECONDITION(!mExecutor->HasStarted(),
|
||||
"Document charset set too late.");
|
||||
NS_PRECONDITION(mStreamParser, "Tried to set charset on a script-only parser.");
|
||||
mStreamParser->SetDocumentCharset(aCharset, aCharsetSource);
|
||||
|
@ -196,6 +192,10 @@ nsHtml5Parser::GetStreamListener(nsIStreamListener** aListener)
|
|||
{
|
||||
if (!mStreamParser) {
|
||||
mStreamParser = new nsHtml5StreamParser(mExecutor, this);
|
||||
nsIDocument* doc = mExecutor->GetDocument();
|
||||
if (doc) {
|
||||
mStreamParser->SetSpeculativeLoaderWithDocument(doc);
|
||||
}
|
||||
}
|
||||
NS_ADDREF(*aListener = mStreamParser);
|
||||
return NS_OK;
|
||||
|
@ -262,10 +262,14 @@ nsHtml5Parser::Parse(nsIURI* aURL, // legacy parameter; ignored
|
|||
* Do NOT cause WillBuildModel to be called synchronously from here!
|
||||
* The document won't be ready for it until OnStartRequest!
|
||||
*/
|
||||
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED,
|
||||
NS_PRECONDITION(!mExecutor->HasStarted(),
|
||||
"Tried to start parse without initializing the parser properly.");
|
||||
if (!mStreamParser) {
|
||||
mStreamParser = new nsHtml5StreamParser(mExecutor, this);
|
||||
nsIDocument* doc = mExecutor->GetDocument();
|
||||
if (doc) {
|
||||
mStreamParser->SetSpeculativeLoaderWithDocument(doc);
|
||||
}
|
||||
}
|
||||
mStreamParser->SetObserver(aObserver);
|
||||
mExecutor->SetStreamParser(mStreamParser);
|
||||
|
@ -293,27 +297,23 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
|
||||
|
||||
// Return early if the parser has processed EOF
|
||||
switch (mExecutor->GetLifeCycle()) {
|
||||
case TERMINATED:
|
||||
return NS_OK;
|
||||
case NOT_STARTED:
|
||||
NS_ASSERTION(!mStreamParser,
|
||||
"Had stream parser but document.write started life cycle.");
|
||||
mExecutor->SetParser(this);
|
||||
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
|
||||
mTokenizer->start();
|
||||
mExecutor->Start();
|
||||
/*
|
||||
* If you move the following line, be very careful not to cause
|
||||
* WillBuildModel to be called before the document has had its
|
||||
* script global object set.
|
||||
*/
|
||||
mExecutor->WillBuildModel(eDTDMode_unknown);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (!mExecutor->HasStarted()) {
|
||||
NS_ASSERTION(!mStreamParser,
|
||||
"Had stream parser but document.write started life cycle.");
|
||||
mExecutor->SetParser(this);
|
||||
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
|
||||
mTokenizer->start();
|
||||
mExecutor->Start();
|
||||
/*
|
||||
* If you move the following line, be very careful not to cause
|
||||
* WillBuildModel to be called before the document has had its
|
||||
* script global object set.
|
||||
*/
|
||||
mExecutor->WillBuildModel(eDTDMode_unknown);
|
||||
}
|
||||
if (mExecutor->IsComplete()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aLastCall && aSourceBuffer.IsEmpty() && aKey == GetRootContextKey()) {
|
||||
// document.close()
|
||||
NS_ASSERTION(!mStreamParser,
|
||||
|
@ -326,7 +326,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
PRInt32 lineNumberSave = mTokenizer->getLineNumber();
|
||||
|
||||
if (!aSourceBuffer.IsEmpty()) {
|
||||
nsHtml5UTF16Buffer* buffer = new nsHtml5UTF16Buffer(aSourceBuffer.Length());
|
||||
nsRefPtr<nsHtml5UTF16Buffer> buffer = new nsHtml5UTF16Buffer(aSourceBuffer.Length());
|
||||
memcpy(buffer->getBuffer(), aSourceBuffer.BeginReading(), aSourceBuffer.Length() * sizeof(PRUnichar));
|
||||
buffer->setEnd(aSourceBuffer.Length());
|
||||
if (!mBlocked) {
|
||||
|
@ -337,12 +337,11 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
if (buffer->hasMore()) {
|
||||
mLastWasCR = mTokenizer->tokenizeBuffer(buffer);
|
||||
if (mTreeBuilder->HasScript()) {
|
||||
mTreeBuilder->Flush(); // moves ops to executor queue
|
||||
mExecutor->Flush(); // executes the queue
|
||||
// Is mBlocked always true here?
|
||||
mTreeBuilder->flushCharacters(); // Flush trailing characters
|
||||
mTreeBuilder->Flush(); // Move ops to the executor
|
||||
mExecutor->Flush(); // run the ops
|
||||
}
|
||||
if (mBlocked) {
|
||||
// XXX is the tail insertion and script exec in the wrong order?
|
||||
// mExecutor->WillInterrupt();
|
||||
break;
|
||||
}
|
||||
|
@ -388,16 +387,17 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
buffer->next = keyHolder;
|
||||
mFirstBuffer = buffer;
|
||||
}
|
||||
MaybePostContinueEvent();
|
||||
} else {
|
||||
delete buffer;
|
||||
if (!mStreamParser) {
|
||||
MaybePostContinueEvent();
|
||||
}
|
||||
} else { // buffer didn't have more
|
||||
// Scripting semantics require a forced tree builder flush here
|
||||
mTreeBuilder->flushCharacters(); // Flush trailing characters
|
||||
mTreeBuilder->Flush(); // Move ops to the executor
|
||||
mExecutor->Flush(); // run the ops
|
||||
}
|
||||
}
|
||||
|
||||
// Scripting semantics require a forced tree builder flush here
|
||||
// TODO: Also flush the pending text node from tree builder
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mTokenizer->setLineNumber(lineNumberSave);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -416,7 +416,7 @@ nsHtml5Parser::Terminate(void)
|
|||
{
|
||||
// We should only call DidBuildModel once, so don't do anything if this is
|
||||
// the second time that Terminate has been called.
|
||||
if (mExecutor->GetLifeCycle() == TERMINATED) {
|
||||
if (mExecutor->IsComplete()) {
|
||||
return NS_OK;
|
||||
}
|
||||
// XXX - [ until we figure out a way to break parser-sink circularity ]
|
||||
|
@ -474,7 +474,7 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
|
|||
mTreeBuilder->setFragmentContext(aContextLocalName, aContextNamespace, &weakTarget, aQuirks);
|
||||
mFragmentMode = PR_TRUE;
|
||||
|
||||
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED, "Tried to start parse without initializing the parser properly.");
|
||||
NS_PRECONDITION(!mExecutor->HasStarted(), "Tried to start parse without initializing the parser properly.");
|
||||
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
|
||||
mTokenizer->start();
|
||||
mExecutor->Start(); // Don't call WillBuildModel in fragment case
|
||||
|
@ -497,7 +497,6 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
|
|||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mTokenizer->end();
|
||||
mExecutor->SetLifeCycle(TERMINATED);
|
||||
mExecutor->DropParserAndPerfHint();
|
||||
mAtomTable.Clear();
|
||||
return NS_OK;
|
||||
|
@ -530,11 +529,7 @@ nsHtml5Parser::Reset()
|
|||
mContinueEvent = nsnull; // weak ref
|
||||
mAtomTable.Clear(); // should be already cleared in the fragment case anyway
|
||||
// Portable parser objects
|
||||
while (mFirstBuffer->next) {
|
||||
nsHtml5UTF16Buffer* oldBuf = mFirstBuffer;
|
||||
mFirstBuffer = mFirstBuffer->next;
|
||||
delete oldBuf;
|
||||
}
|
||||
mFirstBuffer->next = nsnull;
|
||||
mFirstBuffer->setStart(0);
|
||||
mFirstBuffer->setEnd(0);
|
||||
}
|
||||
|
@ -568,58 +563,51 @@ nsHtml5Parser::ParseUntilScript()
|
|||
return;
|
||||
}
|
||||
|
||||
switch (mExecutor->GetLifeCycle()) {
|
||||
case TERMINATED:
|
||||
return;
|
||||
case NOT_STARTED:
|
||||
NS_NOTREACHED("Bad life cycle!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (mExecutor->IsComplete()) {
|
||||
return;
|
||||
}
|
||||
NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
|
||||
|
||||
mExecutor->WillResume();
|
||||
for (;;) {
|
||||
if (!mFirstBuffer->hasMore()) {
|
||||
if (mFirstBuffer == mLastBuffer) {
|
||||
switch (mExecutor->GetLifeCycle()) {
|
||||
case TERMINATED:
|
||||
// something like cache manisfests stopped the parse in mid-flight
|
||||
return;
|
||||
case PARSING:
|
||||
if (mDocumentClosed) {
|
||||
NS_ASSERTION(!mStreamParser,
|
||||
"This should only happen with script-created parser.");
|
||||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mTokenizer->end();
|
||||
return;
|
||||
} else {
|
||||
// never release the last buffer. instead just zero its indeces for refill
|
||||
mFirstBuffer->setStart(0);
|
||||
mFirstBuffer->setEnd(0);
|
||||
if (mStreamParser) {
|
||||
mStreamParser->ContinueAfterScripts(mTokenizer,
|
||||
mTreeBuilder,
|
||||
mLastWasCR);
|
||||
}
|
||||
return; // no more data for now but expecting more
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("It should be impossible to reach this.");
|
||||
return;
|
||||
if (mExecutor->IsComplete()) {
|
||||
// something like cache manisfests stopped the parse in mid-flight
|
||||
return;
|
||||
}
|
||||
if (mDocumentClosed) {
|
||||
NS_ASSERTION(!mStreamParser,
|
||||
"This should only happen with script-created parser.");
|
||||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mTokenizer->end();
|
||||
return;
|
||||
} else {
|
||||
// never release the last buffer.
|
||||
NS_ASSERTION(!mLastBuffer->getStart(),
|
||||
"Sentinel buffer had its indeces changed.");
|
||||
NS_ASSERTION(!mLastBuffer->getEnd(),
|
||||
"Sentinel buffer had its indeces changed.");
|
||||
if (mStreamParser &&
|
||||
mReturnToStreamParserPermitted &&
|
||||
!mExecutor->IsScriptExecuting()) {
|
||||
mReturnToStreamParserPermitted = PR_FALSE;
|
||||
mStreamParser->ContinueAfterScripts(mTokenizer,
|
||||
mTreeBuilder,
|
||||
mLastWasCR);
|
||||
}
|
||||
return; // no more data for now but expecting more
|
||||
}
|
||||
} else {
|
||||
nsHtml5UTF16Buffer* oldBuf = mFirstBuffer;
|
||||
mFirstBuffer = mFirstBuffer->next;
|
||||
delete oldBuf;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mBlocked || (mExecutor->GetLifeCycle() == TERMINATED)) {
|
||||
if (mBlocked || mExecutor->IsComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -644,7 +632,7 @@ nsHtml5Parser::ParseUntilScript()
|
|||
void
|
||||
nsHtml5Parser::MaybePostContinueEvent()
|
||||
{
|
||||
NS_PRECONDITION(mExecutor->GetLifeCycle() != TERMINATED,
|
||||
NS_PRECONDITION(!mExecutor->IsComplete(),
|
||||
"Tried to post continue event when the parser is done.");
|
||||
if (mContinueEvent) {
|
||||
return; // we already have a pending event
|
||||
|
@ -665,6 +653,9 @@ nsHtml5Parser::Initialize(nsIDocument* aDoc,
|
|||
nsISupports* aContainer,
|
||||
nsIChannel* aChannel)
|
||||
{
|
||||
if (mStreamParser && aDoc) {
|
||||
mStreamParser->SetSpeculativeLoaderWithDocument(aDoc);
|
||||
}
|
||||
return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
|
||||
}
|
||||
|
||||
|
@ -680,4 +671,5 @@ nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState)
|
|||
mTokenizer->resetToDataState();
|
||||
mTreeBuilder->loadState(aState, &mAtomTable);
|
||||
mLastWasCR = PR_FALSE;
|
||||
mReturnToStreamParserPermitted = PR_TRUE;
|
||||
}
|
||||
|
|
|
@ -331,7 +331,7 @@ class nsHtml5Parser : public nsIParser {
|
|||
/**
|
||||
* The first buffer in the pending UTF-16 buffer queue
|
||||
*/
|
||||
nsHtml5UTF16Buffer* mFirstBuffer; // manually managed strong ref
|
||||
nsRefPtr<nsHtml5UTF16Buffer> mFirstBuffer;
|
||||
|
||||
/**
|
||||
* The last buffer in the pending UTF-16 buffer queue
|
||||
|
@ -358,6 +358,11 @@ class nsHtml5Parser : public nsIParser {
|
|||
* The stream parser.
|
||||
*/
|
||||
nsRefPtr<nsHtml5StreamParser> mStreamParser;
|
||||
|
||||
/**
|
||||
* Whether it's OK to transfer parsing back to the stream parser
|
||||
*/
|
||||
PRBool mReturnToStreamParserPermitted;
|
||||
|
||||
/**
|
||||
* The scoped atom table
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/* ***** 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) 2009
|
||||
* 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 ***** */
|
||||
|
||||
#include "nsHtml5Speculation.h"
|
||||
|
||||
nsHtml5Speculation::nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer,
|
||||
PRInt32 aStart,
|
||||
nsAHtml5TreeBuilderState* aSnapshot)
|
||||
: mBuffer(aBuffer)
|
||||
, mStart(aStart)
|
||||
, mSnapshot(aSnapshot)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsHtml5Speculation);
|
||||
}
|
||||
|
||||
nsHtml5Speculation::~nsHtml5Speculation()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsHtml5Speculation);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5Speculation::MaybeFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5Speculation::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
||||
{
|
||||
if (mOpQueue.IsEmpty()) {
|
||||
mOpQueue.SwapElements(aOpQueue);
|
||||
return;
|
||||
}
|
||||
mOpQueue.MoveElementsFrom(aOpQueue);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5Speculation::FlushToSink(nsAHtml5TreeOpSink* aSink)
|
||||
{
|
||||
aSink->ForcedFlush(mOpQueue);
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/* ***** 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) 2009
|
||||
* 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 nsHtml5Speculation_h__
|
||||
#define nsHtml5Speculation_h__
|
||||
|
||||
#include "nsHtml5UTF16Buffer.h"
|
||||
#include "nsAHtml5TreeBuilderState.h"
|
||||
#include "nsHtml5TreeOperation.h"
|
||||
#include "nsAHtml5TreeOpSink.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsHtml5Speculation : public nsAHtml5TreeOpSink
|
||||
{
|
||||
public:
|
||||
nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer,
|
||||
PRInt32 aStart,
|
||||
nsAHtml5TreeBuilderState* aSnapshot);
|
||||
|
||||
~nsHtml5Speculation();
|
||||
|
||||
nsHtml5UTF16Buffer* GetBuffer() {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
PRInt32 GetStart() {
|
||||
return mStart;
|
||||
}
|
||||
|
||||
nsAHtml5TreeBuilderState* GetSnapshot() {
|
||||
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);
|
||||
|
||||
void FlushToSink(nsAHtml5TreeOpSink* aSink);
|
||||
|
||||
private:
|
||||
/**
|
||||
* The first buffer in the pending UTF-16 buffer queue
|
||||
*/
|
||||
nsRefPtr<nsHtml5UTF16Buffer> mBuffer;
|
||||
|
||||
/**
|
||||
* The start index of this speculation in the first buffer
|
||||
*/
|
||||
PRInt32 mStart;
|
||||
|
||||
nsAutoPtr<nsAHtml5TreeBuilderState> mSnapshot;
|
||||
|
||||
nsTArray<nsHtml5TreeOperation> mOpQueue;
|
||||
};
|
||||
|
||||
#endif // nsHtml5Speculation_h__
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- 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 "nsICSSLoader.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsScriptLoader.h"
|
||||
#include "nsICSSLoaderObserver.h"
|
||||
|
||||
/**
|
||||
* Used if we need to pass an nsICSSLoaderObserver as parameter,
|
||||
* but don't really need its services
|
||||
*/
|
||||
class nsHtml5DummyCSSLoaderObserver : public nsICSSLoaderObserver {
|
||||
public:
|
||||
NS_IMETHOD
|
||||
StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate, nsresult aStatus) {
|
||||
return NS_OK;
|
||||
}
|
||||
NS_DECL_ISUPPORTS
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsHtml5DummyCSSLoaderObserver, nsICSSLoaderObserver)
|
||||
|
||||
nsHtml5SpeculativeLoader::nsHtml5SpeculativeLoader(nsIDocument* aDocument)
|
||||
: mDocument(aDocument)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
nsHtml5SpeculativeLoader::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
|
||||
nsHtml5SpeculativeLoader::PreloadStyle(const nsAString& aURL,
|
||||
const nsAString& aCharset)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsICSSLoaderObserver> obs = new nsHtml5DummyCSSLoaderObserver();
|
||||
mDocument->CSSLoader()->LoadSheet(uri, mDocument->NodePrincipal(),
|
||||
NS_LossyConvertUTF16toASCII(aCharset),
|
||||
obs);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5SpeculativeLoader::PreloadImage(const nsAString& aURL)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
mDocument->MaybePreLoadImage(uri);
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/* ***** 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) 2009
|
||||
* 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 nsHtml5SpeculativeLoader_h__
|
||||
#define nsHtml5SpeculativeLoader_h__
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsHashSets.h"
|
||||
|
||||
class nsHtml5SpeculativeLoader
|
||||
{
|
||||
public:
|
||||
nsHtml5SpeculativeLoader(nsIDocument* aDocument);
|
||||
~nsHtml5SpeculativeLoader();
|
||||
|
||||
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);
|
||||
|
||||
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 document to use as the context for preloading.
|
||||
*/
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
|
||||
/**
|
||||
* URLs already preloaded/preloading.
|
||||
*/
|
||||
nsCStringHashSet mPreloadedURLs;
|
||||
};
|
||||
|
||||
#endif // nsHtml5SpeculativeLoader_h__
|
|
@ -72,6 +72,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner)
|
||||
tmp->mExecutorFlusher = nsnull;
|
||||
tmp->mExecutor = nsnull;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
||||
tmp->mTreeBuilder->DropSpeculativeLoader();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
|
||||
|
@ -83,6 +85,13 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5StreamParser)
|
|||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExecutorFlusher->mExecutor");
|
||||
cb.NoteXPCOMChild(static_cast<nsIContentSink*> (tmp->mExecutor));
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
|
||||
// hack: count the strongly owned edge wrapped in the speculative loader
|
||||
if (tmp->mDocument) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mTreeBuilder->mSpeculativeLoader->mDocument");
|
||||
cb.NoteXPCOMChild(tmp->mDocument);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
class nsHtml5ExecutorFlusher : public nsRunnable
|
||||
|
@ -109,6 +118,7 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
|
|||
, mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
|
||||
, mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
|
||||
, mOwner(aOwner)
|
||||
, mSpeculationMutex("nsHtml5StreamParser mSpeculationMutex")
|
||||
, mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
|
||||
, mThread(nsHtml5Module::GetStreamParserThread())
|
||||
, mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
|
||||
|
@ -132,17 +142,20 @@ nsHtml5StreamParser::~nsHtml5StreamParser()
|
|||
mUnicodeDecoder = nsnull;
|
||||
mSniffingBuffer = nsnull;
|
||||
mMetaScanner = nsnull;
|
||||
while (mFirstBuffer) {
|
||||
nsHtml5UTF16Buffer* old = mFirstBuffer;
|
||||
mFirstBuffer = mFirstBuffer->next;
|
||||
delete old;
|
||||
}
|
||||
mFirstBuffer = nsnull;
|
||||
mExecutor = nsnull;
|
||||
mTreeBuilder = nsnull;
|
||||
mTokenizer = nsnull;
|
||||
mOwner = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5StreamParser::SetSpeculativeLoaderWithDocument(nsIDocument* aDocument) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mDocument = aDocument;
|
||||
mTreeBuilder->SetSpeculativeLoaderWithDocument(aDocument);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHtml5StreamParser::GetChannel(nsIChannel** aChannel)
|
||||
{
|
||||
|
@ -444,7 +457,7 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
{
|
||||
NS_PRECONDITION(STREAM_NOT_STARTED == mStreamState,
|
||||
"Got OnStartRequest when the stream had already started.");
|
||||
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED,
|
||||
NS_PRECONDITION(!mExecutor->HasStarted(),
|
||||
"Got OnStartRequest at the wrong stage in the executor life cycle.");
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
if (mObserver) {
|
||||
|
@ -503,6 +516,12 @@ nsHtml5StreamParser::DoStopRequest()
|
|||
NS_ASSERTION(IsParserThread(), "Wrong thread!");
|
||||
NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
|
||||
"Stream ended without being open.");
|
||||
mTokenizerMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (IsTerminated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mUnicodeDecoder) {
|
||||
PRUint32 writeCount;
|
||||
FinalizeSniffing(nsnull, 0, &writeCount, 0);
|
||||
|
@ -510,10 +529,12 @@ nsHtml5StreamParser::DoStopRequest()
|
|||
}
|
||||
|
||||
mStreamState = STREAM_ENDED;
|
||||
|
||||
if (!mWaitingForScripts) {
|
||||
ParseUntilScript();
|
||||
}
|
||||
|
||||
if (IsTerminatedOrInterrupted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParseAvailableData();
|
||||
}
|
||||
|
||||
class nsHtml5RequestStopper : public nsRunnable
|
||||
|
@ -526,6 +547,7 @@ class nsHtml5RequestStopper : public nsRunnable
|
|||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
|
||||
mStreamParser->DoStopRequest();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -554,14 +576,23 @@ nsHtml5StreamParser::DoDataAvailable(PRUint8* aBuffer, PRUint32 aLength)
|
|||
NS_ASSERTION(IsParserThread(), "Wrong thread!");
|
||||
NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
|
||||
"DoDataAvailable called when stream not open.");
|
||||
mTokenizerMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (IsTerminated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PRUint32 writeCount;
|
||||
HasDecoder() ? WriteStreamBytes(aBuffer, aLength, &writeCount) :
|
||||
SniffStreamBytes(aBuffer, aLength, &writeCount);
|
||||
// dropping nsresult here
|
||||
NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
|
||||
if (!mWaitingForScripts) {
|
||||
ParseUntilScript();
|
||||
|
||||
if (IsTerminatedOrInterrupted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParseAvailableData();
|
||||
}
|
||||
|
||||
class nsHtml5DataAvailable : public nsRunnable
|
||||
|
@ -580,6 +611,7 @@ class nsHtml5DataAvailable : public nsRunnable
|
|||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
|
||||
mStreamParser->DoDataAvailable(mData, mLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -648,48 +680,46 @@ nsHtml5StreamParser::internalEncodingDeclaration(nsString* aEncoding)
|
|||
}
|
||||
|
||||
void
|
||||
nsHtml5StreamParser::ParseUntilScript()
|
||||
nsHtml5StreamParser::ParseAvailableData()
|
||||
{
|
||||
NS_ASSERTION(IsParserThread(), "Wrong thread!");
|
||||
if (IsTerminated()) {
|
||||
mTokenizerMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (IsTerminatedOrInterrupted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Relax this mutex so that the parser doesn't speculate to
|
||||
// completion when it's already known that the speculation will fail.
|
||||
// Maybe have another boolean and mutex for checking IsSpeculationFailing()
|
||||
// or something like that instead of trying to be fancy with this mutex.
|
||||
mozilla::MutexAutoLock autoLock(mTokenizerMutex);
|
||||
|
||||
mWaitingForScripts = PR_FALSE;
|
||||
|
||||
|
||||
for (;;) {
|
||||
if (!mFirstBuffer->hasMore()) {
|
||||
if (mFirstBuffer == mLastBuffer) {
|
||||
switch (mStreamState) {
|
||||
case STREAM_BEING_READ:
|
||||
// never release the last buffer. instead just zero its indeces for refill
|
||||
mFirstBuffer->setStart(0);
|
||||
mFirstBuffer->setEnd(0);
|
||||
// never release the last buffer.
|
||||
if (!mSpeculating) {
|
||||
// reuse buffer space if not speculating
|
||||
mFirstBuffer->setStart(0);
|
||||
mFirstBuffer->setEnd(0);
|
||||
}
|
||||
mTreeBuilder->Flush();
|
||||
return; // no more data for now but expecting more
|
||||
case STREAM_ENDED:
|
||||
Terminate(); // TODO Don't terminate if this is a speculation
|
||||
if (mAtEOF) {
|
||||
return;
|
||||
}
|
||||
mAtEOF = PR_TRUE;
|
||||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
}
|
||||
return; // no more data and not expecting more
|
||||
default:
|
||||
NS_NOTREACHED("It should be impossible to reach this.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
nsHtml5UTF16Buffer* oldBuf = mFirstBuffer;
|
||||
mFirstBuffer = mFirstBuffer->next;
|
||||
delete oldBuf;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -703,18 +733,23 @@ nsHtml5StreamParser::ParseUntilScript()
|
|||
// Terminate, but that never happens together with script.
|
||||
// Can't assert that here, though, because it's possible that the main
|
||||
// thread has called Terminate() while this thread was parsing.
|
||||
if (IsTerminated()) {
|
||||
return;
|
||||
}
|
||||
if (mTreeBuilder->HasScript()) {
|
||||
mTreeBuilder->AddSnapshotToScript(mTreeBuilder->newSnapshot());
|
||||
mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
|
||||
nsHtml5Speculation* speculation =
|
||||
new nsHtml5Speculation(mFirstBuffer,
|
||||
mFirstBuffer->getStart(),
|
||||
mTreeBuilder->newSnapshot());
|
||||
mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot());
|
||||
mTreeBuilder->Flush();
|
||||
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
// XXX start speculation
|
||||
mWaitingForScripts = PR_TRUE;
|
||||
return; // ContinueAfterScripts() will re-enable this parser
|
||||
}
|
||||
mTreeBuilder->SetOpSink(speculation);
|
||||
mSpeculations.AppendElement(speculation); // adopts the pointer
|
||||
mSpeculating = PR_TRUE;
|
||||
}
|
||||
if (IsTerminatedOrInterrupted()) {
|
||||
return;
|
||||
}
|
||||
mTreeBuilder->MaybeFlush();
|
||||
}
|
||||
|
@ -722,17 +757,18 @@ nsHtml5StreamParser::ParseUntilScript()
|
|||
}
|
||||
}
|
||||
|
||||
class nsHtml5ContinueAfterScript : public nsRunnable
|
||||
class nsHtml5StreamParserContinuation : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
|
||||
public:
|
||||
nsHtml5ContinueAfterScript(nsHtml5StreamParser* aStreamParser)
|
||||
nsHtml5StreamParserContinuation(nsHtml5StreamParser* aStreamParser)
|
||||
: mStreamParser(aStreamParser)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mStreamParser->ParseUntilScript();
|
||||
mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
|
||||
mStreamParser->ParseAvailableData();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
@ -743,40 +779,102 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
|
|||
PRBool aLastWasCR)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mExecutor->StartReadingFromStage();
|
||||
// TODO:
|
||||
// test if the state of the argument tokenizer and tree builder match
|
||||
// the earliest speculation.
|
||||
// If not, rendez-vous at barrier, zaps all speculations, rewind the stream
|
||||
// and copy over the state.
|
||||
// If yes:
|
||||
// If there are multiple speculations or the stream parser has terminated.
|
||||
// load the tree op queue from the earliest speculation into the tree op
|
||||
// executor and discard the stream data for that speculation. Return.
|
||||
// Otherwise, rendez-vous at barrier, load the tree op queue from the
|
||||
// speculation into the tree op executor, set the tree op executor to read
|
||||
// from the stage, set the stream parser tree builder to write to stage,
|
||||
// discard the stream data for the speculation.
|
||||
|
||||
#ifdef DEBUG
|
||||
mExecutor->AssertStageEmpty();
|
||||
#endif
|
||||
PRBool speculationFailed = PR_FALSE;
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mTokenizerMutex);
|
||||
mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
|
||||
if (mSpeculations.IsEmpty()) {
|
||||
// Not quite sure how exactly this happens...
|
||||
// Maybe an artifact of defer scripts?
|
||||
return;
|
||||
}
|
||||
nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
|
||||
if (aLastWasCR ||
|
||||
!aTokenizer->isInDataState() ||
|
||||
!aTreeBuilder->snapshotMatches(speculation->GetSnapshot())) {
|
||||
speculationFailed = PR_TRUE;
|
||||
// We've got a failed speculation :-(
|
||||
Interrupt(); // Make the parser thread release the tokenizer mutex sooner
|
||||
// now fall out of the speculationAutoLock into the tokenizerAutoLock block
|
||||
} else {
|
||||
// We've got a successful speculation!
|
||||
if (mSpeculations.Length() > 1) {
|
||||
// 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");
|
||||
}
|
||||
mSpeculations.RemoveElementAt(0);
|
||||
return;
|
||||
}
|
||||
// else
|
||||
Interrupt(); // Make the parser thread release the tokenizer mutex sooner
|
||||
|
||||
// now fall through
|
||||
// the first speculation is the current speculation. Need to
|
||||
// release the the speculation mutex and acquire the tokenizer
|
||||
// mutex. (Just acquiring the other mutex here would deadlock)
|
||||
}
|
||||
}
|
||||
{
|
||||
mozilla::MutexAutoLock tokenizerAutoLock(mTokenizerMutex);
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
mAtomTable.SetPermittedLookupThread(mainThread);
|
||||
#endif
|
||||
|
||||
// Approximation: Copy state over for now unconditionally.
|
||||
mLastWasCR = aLastWasCR;
|
||||
mTokenizer->loadState(aTokenizer);
|
||||
mTreeBuilder->loadState(aTreeBuilder, &mAtomTable);
|
||||
|
||||
// In principle, the speculation mutex should be acquired here,
|
||||
// but there's no point, because the parser thread only acquires it
|
||||
// when it has also acquired the tokenizer mutex and we are already
|
||||
// holding the tokenizer mutex.
|
||||
if (speculationFailed) {
|
||||
// Rewind the stream
|
||||
mAtEOF = PR_FALSE;
|
||||
nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
|
||||
mFirstBuffer = speculation->GetBuffer();
|
||||
mFirstBuffer->setStart(speculation->GetStart());
|
||||
nsHtml5UTF16Buffer* buffer = mFirstBuffer->next;
|
||||
while (buffer) {
|
||||
buffer->setStart(0);
|
||||
buffer = buffer->next;
|
||||
}
|
||||
|
||||
mSpeculations.Clear(); // potentially a huge number of destructors
|
||||
// run here synchronously on the main thread...
|
||||
|
||||
mTreeBuilder->SetOpSink(mExecutor->GetStage());
|
||||
mExecutor->StartReadingFromStage();
|
||||
mSpeculating = PR_FALSE;
|
||||
// Copy state over
|
||||
mLastWasCR = aLastWasCR;
|
||||
mTokenizer->loadState(aTokenizer);
|
||||
mTreeBuilder->loadState(aTreeBuilder, &mAtomTable);
|
||||
} else {
|
||||
// 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");
|
||||
}
|
||||
mSpeculations.RemoveElementAt(0);
|
||||
if (mSpeculations.IsEmpty()) {
|
||||
// yes, it was still the only speculation. Now stop speculating
|
||||
mTreeBuilder->SetOpSink(mExecutor->GetStage());
|
||||
mExecutor->StartReadingFromStage();
|
||||
mSpeculating = PR_FALSE;
|
||||
}
|
||||
}
|
||||
Uninterrupt();
|
||||
nsCOMPtr<nsIRunnable> event = new nsHtml5StreamParserContinuation(this);
|
||||
if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Failed to dispatch ParseAvailableData event");
|
||||
}
|
||||
// A stream event might run before this event runs, but that's harmless.
|
||||
#ifdef DEBUG
|
||||
mAtomTable.SetPermittedLookupThread(mThread);
|
||||
#endif
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> event = new nsHtml5ContinueAfterScript(this);
|
||||
if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Failed to dispatch ParseUntilScript event");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "nsICharsetAlias.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsHtml5AtomTable.h"
|
||||
#include "nsHtml5Speculation.h"
|
||||
|
||||
class nsHtml5Parser;
|
||||
|
||||
|
@ -104,7 +105,7 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
|
||||
friend class nsHtml5RequestStopper;
|
||||
friend class nsHtml5DataAvailable;
|
||||
friend class nsHtml5ContinueAfterScript;
|
||||
friend class nsHtml5StreamParserContinuation;
|
||||
|
||||
public:
|
||||
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
||||
|
@ -154,7 +155,9 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mObserver = aObserver;
|
||||
}
|
||||
|
||||
|
||||
void SetSpeculativeLoaderWithDocument(nsIDocument* aDocument);
|
||||
|
||||
nsresult GetChannel(nsIChannel** aChannel);
|
||||
|
||||
/**
|
||||
|
@ -169,8 +172,6 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
void Terminate() {
|
||||
mozilla::MutexAutoLock autoLock(mTerminatedMutex);
|
||||
mTerminated = PR_TRUE;
|
||||
// TODO: Make sure this object stays alive as long as there are
|
||||
// in-flight runnables coming this way
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -183,15 +184,34 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
}
|
||||
#endif
|
||||
|
||||
void ParseUntilScript();
|
||||
void Interrupt() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mozilla::MutexAutoLock autoLock(mTerminatedMutex);
|
||||
mInterrupted = PR_TRUE;
|
||||
}
|
||||
|
||||
void Uninterrupt() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mTokenizerMutex.AssertCurrentThreadOwns();
|
||||
// Not acquiring mTerminatedMutex because mTokenizerMutex is already
|
||||
// held at this point and is already stronger.
|
||||
mInterrupted = PR_FALSE;
|
||||
}
|
||||
|
||||
void ParseAvailableData();
|
||||
|
||||
void DoStopRequest();
|
||||
|
||||
void DoDataAvailable(PRUint8* aBuffer, PRUint32 aLength);
|
||||
|
||||
PRBool IsTerminatedOrInterrupted() {
|
||||
mozilla::MutexAutoLock autoLock(mTerminatedMutex);
|
||||
return mTerminated || mInterrupted;
|
||||
}
|
||||
|
||||
PRBool IsTerminated() {
|
||||
mozilla::MutexAutoLock autoLock(mTerminatedMutex);
|
||||
return mTerminated;
|
||||
return mTerminated;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,7 +344,7 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
/**
|
||||
* The first buffer in the pending UTF-16 buffer queue
|
||||
*/
|
||||
nsHtml5UTF16Buffer* mFirstBuffer; // manually managed strong ref
|
||||
nsRefPtr<nsHtml5UTF16Buffer> mFirstBuffer;
|
||||
|
||||
/**
|
||||
* The last buffer in the pending UTF-16 buffer queue
|
||||
|
@ -349,7 +369,7 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
|
||||
/**
|
||||
* Makes sure the main thread can't mess the tokenizer state while it's
|
||||
* tokenizing
|
||||
* tokenizing. This mutex also protects the current speculation.
|
||||
*/
|
||||
mozilla::Mutex mTokenizerMutex;
|
||||
|
||||
|
@ -374,14 +394,29 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
eHtml5StreamState mStreamState;
|
||||
|
||||
/**
|
||||
* Whether we are waiting for scripts to be done.
|
||||
* Whether we are speculating.
|
||||
*/
|
||||
PRBool mWaitingForScripts;
|
||||
PRBool mSpeculating;
|
||||
|
||||
/**
|
||||
* Whether the tokenizer has reached EOF. (Reset when stream rewinded.)
|
||||
*/
|
||||
PRBool mAtEOF;
|
||||
|
||||
/**
|
||||
* The speculations. The mutex protects the nsTArray itself.
|
||||
* To access the queue of current speculation, mTokenizerMutex must be
|
||||
* obtained.
|
||||
* The current speculation is the last element
|
||||
*/
|
||||
nsTArray<nsAutoPtr<nsHtml5Speculation> > mSpeculations;
|
||||
mozilla::Mutex mSpeculationMutex;
|
||||
|
||||
/**
|
||||
* True to terminate early; protected by mTerminatedMutex
|
||||
*/
|
||||
PRBool mTerminated;
|
||||
PRBool mInterrupted;
|
||||
mozilla::Mutex mTerminatedMutex;
|
||||
|
||||
/**
|
||||
|
@ -390,6 +425,11 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
nsCOMPtr<nsIThread> mThread;
|
||||
|
||||
nsCOMPtr<nsIRunnable> mExecutorFlusher;
|
||||
|
||||
/**
|
||||
* The document wrapped by the speculative loader.
|
||||
*/
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
};
|
||||
|
||||
#endif // nsHtml5StreamParser_h__
|
||||
|
|
|
@ -3489,6 +3489,7 @@ nsHtml5Tokenizer::initializeWithoutStarting()
|
|||
confident = PR_FALSE;
|
||||
strBuf = jArray<PRUnichar,PRInt32>(64);
|
||||
longStrBuf = jArray<PRUnichar,PRInt32>(1024);
|
||||
line = 1;
|
||||
resetToDataState();
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#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>();
|
||||
|
@ -72,9 +73,143 @@ 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;
|
||||
}
|
||||
};
|
||||
|
||||
nsIContent**
|
||||
nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes)
|
||||
{
|
||||
NS_PRECONDITION(aAttributes, "Got null attributes.");
|
||||
|
||||
// Start wall of code for speculative loading
|
||||
|
||||
if (mSpeculativeLoader) {
|
||||
switch (aNamespace) {
|
||||
case kNameSpaceID_XHTML:
|
||||
if (nsHtml5Atoms::img == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
|
||||
}
|
||||
} else if (nsHtml5Atoms::script == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
|
||||
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()));
|
||||
}
|
||||
} else if (nsHtml5Atoms::link == aName) {
|
||||
nsString* rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL);
|
||||
// Not splitting on space here is bogus but the old parser didn't even
|
||||
// do a case-insensitive check.
|
||||
if (rel && rel->LowerCaseEqualsASCII("stylesheet")) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
|
||||
if (url) {
|
||||
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
|
||||
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
|
||||
*url,
|
||||
(charset) ? *charset : EmptyString()));
|
||||
}
|
||||
}
|
||||
} else if (nsHtml5Atoms::video == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kNameSpaceID_SVG:
|
||||
if (nsHtml5Atoms::image == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
|
||||
}
|
||||
} else if (nsHtml5Atoms::script == aName) {
|
||||
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()));
|
||||
}
|
||||
} else if (nsHtml5Atoms::style == aName) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
|
||||
if (url) {
|
||||
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
|
||||
*url,
|
||||
EmptyString()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// End wall of code for speculative loading
|
||||
|
||||
nsIContent** content = AllocateContentHandle();
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
// XXX if null, OOM!
|
||||
|
@ -292,7 +427,7 @@ nsHtml5TreeBuilder::elementPopped(PRInt32 aNamespace, nsIAtom* aName, nsIContent
|
|||
requestSuspension();
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
// XXX if null, OOM!
|
||||
treeOp->Init(eTreeOpRunScript, aElement, nsnull);
|
||||
treeOp->InitScript(aElement);
|
||||
return;
|
||||
}
|
||||
if (aName == nsHtml5Atoms::title) {
|
||||
|
@ -426,7 +561,7 @@ nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset)
|
|||
void
|
||||
nsHtml5TreeBuilder::StreamEnded()
|
||||
{
|
||||
// The fragement mode calls DidBuildModel from nsHtml5Parser.
|
||||
// The fragment mode calls DidBuildModel from nsHtml5Parser.
|
||||
// Letting DidBuildModel be called from the executor in the fragment case
|
||||
// confuses the EndLoad logic of nsHTMLDocument, since nsHTMLDocument
|
||||
// thinks it is dealing with document.written content as opposed to
|
||||
|
@ -450,9 +585,21 @@ void
|
|||
nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot)
|
||||
{
|
||||
NS_PRECONDITION(HasScript(), "No script to add a snapshot to!");
|
||||
NS_PRECONDITION(aSnapshot, "Got null snapshot.");
|
||||
mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::SetSpeculativeLoaderWithDocument(nsIDocument* aDocument) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mSpeculativeLoader = new nsHtml5SpeculativeLoader(aDocument);
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::DropSpeculativeLoader() {
|
||||
mSpeculativeLoader = nsnull;
|
||||
}
|
||||
|
||||
// DocumentModeHandler
|
||||
void
|
||||
nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m)
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
nsAHtml5TreeOpSink* mOpSink;
|
||||
nsAutoArrayPtr<nsIContent*> mHandles;
|
||||
PRInt32 mHandlesUsed;
|
||||
nsTArray<nsAutoArrayPtr<nsIContent*> > mOldHandles;
|
||||
nsTArray<nsAutoArrayPtr<nsIContent*> > mOldHandles;
|
||||
nsRefPtr<nsHtml5SpeculativeLoader> mSpeculativeLoader;
|
||||
#ifdef DEBUG
|
||||
PRBool mActive;
|
||||
#endif
|
||||
|
@ -68,6 +69,10 @@
|
|||
mOpSink = aOpSink;
|
||||
}
|
||||
|
||||
void SetSpeculativeLoaderWithDocument(nsIDocument* aDocument);
|
||||
|
||||
void DropSpeculativeLoader();
|
||||
|
||||
void Flush();
|
||||
|
||||
void MaybeFlush();
|
||||
|
@ -79,3 +84,9 @@
|
|||
void NeedsCharsetSwitchTo(const nsACString& aEncoding);
|
||||
|
||||
void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot);
|
||||
|
||||
inline void Dispatch(nsIRunnable* aEvent) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(aEvent))) {
|
||||
NS_WARNING("Failed to dispatch speculative load runnable.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,10 +91,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContent
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
|
||||
: mHasProcessedBase(PR_FALSE)
|
||||
, mReadingFromStage(PR_FALSE)
|
||||
, mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
|
||||
: mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
|
||||
{
|
||||
// zeroing operator new for everything else
|
||||
}
|
||||
|
||||
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
|
||||
|
@ -124,9 +123,8 @@ nsHtml5TreeOpExecutor::WillParse()
|
|||
NS_IMETHODIMP
|
||||
nsHtml5TreeOpExecutor::DidBuildModel(PRBool aTerminated)
|
||||
{
|
||||
NS_PRECONDITION(mLifeCycle == PARSING,
|
||||
NS_PRECONDITION(mStarted && mParser,
|
||||
"Bad life cycle.");
|
||||
mLifeCycle = TERMINATED;
|
||||
// This is comes from nsXMLContentSink
|
||||
DidBuildModelImpl(aTerminated);
|
||||
mDocument->ScriptLoader()->RemoveObserver(this);
|
||||
|
@ -266,15 +264,13 @@ nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
|
|||
void
|
||||
nsHtml5TreeOpExecutor::Flush()
|
||||
{
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this); // avoid crashing near EOF
|
||||
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
|
||||
|
||||
if (mLifeCycle == TERMINATED) {
|
||||
if (!mParser) {
|
||||
mFlushTimer->Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mParser, "mParser was nulled out but life cycle wasn't terminated.");
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this); // avoid crashing near EOF
|
||||
nsCOMPtr<nsIParser> parserKungFuDeathGrip(mParser);
|
||||
|
||||
if (mReadingFromStage) {
|
||||
mStage.RetrieveOperations(mOpQueue);
|
||||
|
@ -420,7 +416,7 @@ nsHtml5TreeOpExecutor::ExecuteScript()
|
|||
NS_ASSERTION(mScriptElement, "No script to run");
|
||||
|
||||
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(mScriptElement);
|
||||
if (mLifeCycle == TERMINATED) {
|
||||
if (!mParser) {
|
||||
NS_ASSERTION(sele->IsMalformed(), "Script wasn't marked as malformed.");
|
||||
// We got here not because of an end tag but because the tree builder
|
||||
// popped an incomplete script element on EOF. Returning here to avoid
|
||||
|
@ -442,11 +438,12 @@ nsHtml5TreeOpExecutor::ExecuteScript()
|
|||
if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
|
||||
mScriptElements.AppendObject(sele);
|
||||
mParser->BlockParser();
|
||||
} else if (mLifeCycle != TERMINATED) {
|
||||
} else {
|
||||
// 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);
|
||||
static_cast<nsHtml5Parser*> (mParser.get())->MaybePostContinueEvent();
|
||||
// mParser may have been nulled out by now, but nsContentSink deals
|
||||
ContinueInterruptedParsingAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,8 +463,8 @@ nsHtml5TreeOpExecutor::Init(nsIDocument* aDoc,
|
|||
void
|
||||
nsHtml5TreeOpExecutor::Start()
|
||||
{
|
||||
NS_PRECONDITION(mLifeCycle == NOT_STARTED, "Tried to start when already started.");
|
||||
mLifeCycle = PARSING;
|
||||
NS_PRECONDITION(!mStarted, "Tried to start when already started.");
|
||||
mStarted = PR_TRUE;
|
||||
mScriptElement = nsnull;
|
||||
ScheduleTimer();
|
||||
}
|
||||
|
@ -505,7 +502,7 @@ nsHtml5TreeOpExecutor::Reset() {
|
|||
mHasProcessedBase = PR_FALSE;
|
||||
mReadingFromStage = PR_FALSE;
|
||||
mOpQueue.Clear();
|
||||
mLifeCycle = NOT_STARTED;
|
||||
mStarted = PR_FALSE;
|
||||
mScriptElement = nsnull;
|
||||
mCallDidBuildModel = PR_FALSE;
|
||||
}
|
||||
|
|
|
@ -61,24 +61,6 @@ class nsHtml5TreeBuilder;
|
|||
class nsHtml5Tokenizer;
|
||||
class nsHtml5StreamParser;
|
||||
|
||||
enum eHtml5ParserLifecycle {
|
||||
/**
|
||||
* The parser has told the tokenizer to start yet.
|
||||
*/
|
||||
NOT_STARTED = 0,
|
||||
|
||||
/**
|
||||
* The parser has started the tokenizer and as far as the executor is
|
||||
* aware, the stream hasn't ended.
|
||||
*/
|
||||
PARSING = 1,
|
||||
|
||||
/**
|
||||
* The parse has ended.
|
||||
*/
|
||||
TERMINATED = 2
|
||||
};
|
||||
|
||||
typedef nsIContent* nsIContentPtr;
|
||||
|
||||
class nsHtml5TreeOpExecutor : public nsContentSink,
|
||||
|
@ -119,9 +101,9 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
nsCOMArray<nsIContent> mOwnedNonElements;
|
||||
|
||||
/**
|
||||
* The current point on parser life cycle
|
||||
* Whether the parser has started
|
||||
*/
|
||||
eHtml5ParserLifecycle mLifeCycle;
|
||||
PRBool mStarted;
|
||||
|
||||
/**
|
||||
* Script to run ASAP
|
||||
|
@ -326,15 +308,11 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
#endif
|
||||
|
||||
PRBool IsComplete() {
|
||||
return (mLifeCycle == TERMINATED);
|
||||
return !mParser;
|
||||
}
|
||||
|
||||
eHtml5ParserLifecycle GetLifeCycle() {
|
||||
return mLifeCycle;
|
||||
}
|
||||
|
||||
void SetLifeCycle(eHtml5ParserLifecycle aLifeCycle) {
|
||||
mLifeCycle = aLifeCycle;
|
||||
PRBool HasStarted() {
|
||||
return mStarted;
|
||||
}
|
||||
|
||||
void ExecuteScript();
|
||||
|
@ -383,6 +361,12 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
|
||||
void ScheduleTimer();
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertStageEmpty() {
|
||||
mStage.AssertEmpty();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
nsHtml5Tokenizer* GetTokenizer();
|
||||
|
|
|
@ -76,3 +76,13 @@ nsHtml5TreeOpStage::RetrieveOperations(nsTArray<nsHtml5TreeOperation>& aOpQueue)
|
|||
}
|
||||
aOpQueue.MoveElementsFrom(mOpQueue);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
nsHtml5TreeOpStage::AssertEmpty()
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
// This shouldn't really need the mutex
|
||||
NS_ASSERTION(mOpQueue.IsEmpty(), "The stage was supposed to be empty.");
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -67,6 +67,10 @@ class nsHtml5TreeOpStage : public nsAHtml5TreeOpSink {
|
|||
*/
|
||||
void RetrieveOperations(nsTArray<nsHtml5TreeOperation>& aOpQueue);
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertEmpty();
|
||||
#endif
|
||||
|
||||
private:
|
||||
nsTArray<nsHtml5TreeOperation> mOpQueue;
|
||||
mozilla::Mutex mMutex;
|
||||
|
|
|
@ -88,9 +88,6 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation()
|
|||
case eTreeOpNeedsCharsetSwitchTo:
|
||||
delete[] mOne.charPtr;
|
||||
break;
|
||||
case eTreeOpRunScript:
|
||||
delete mTwo.state;
|
||||
break;
|
||||
default: // keep the compiler happy
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -148,6 +148,14 @@ class nsHtml5TreeOperation {
|
|||
mOne.mode = aMode;
|
||||
}
|
||||
|
||||
inline void InitScript(nsIContent** aNode) {
|
||||
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
|
||||
"Op code must be uninitialized when initializing.");
|
||||
mOpCode = eTreeOpRunScript;
|
||||
mOne.node = aNode;
|
||||
mTwo.state = nsnull;
|
||||
}
|
||||
|
||||
inline void Init(PRInt32 aNamespace,
|
||||
nsIAtom* aName,
|
||||
nsHtml5HtmlAttributes* aAttributes,
|
||||
|
|
|
@ -62,3 +62,29 @@ nsHtml5UTF16Buffer::~nsHtml5UTF16Buffer()
|
|||
MOZ_COUNT_DTOR(nsHtml5UTF16Buffer);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
// Not using macros for AddRef and Release in order to be able to refcount on
|
||||
// and create on different threads.
|
||||
|
||||
nsrefcnt
|
||||
nsHtml5UTF16Buffer::AddRef()
|
||||
{
|
||||
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "Illegal refcount.");
|
||||
++mRefCnt;
|
||||
NS_LOG_ADDREF(this, mRefCnt, "nsHtml5UTF16Buffer", sizeof(*this));
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
nsrefcnt
|
||||
nsHtml5UTF16Buffer::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "Release without AddRef.");
|
||||
--mRefCnt;
|
||||
NS_LOG_RELEASE(this, mRefCnt, "nsHtml5UTF16Buffer");
|
||||
if (mRefCnt == 0) {
|
||||
mRefCnt = 1; /* stabilize */
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return mRefCnt;
|
||||
}
|
||||
|
|
|
@ -39,5 +39,10 @@
|
|||
nsHtml5UTF16Buffer(PRInt32 size);
|
||||
nsHtml5UTF16Buffer(void* key);
|
||||
~nsHtml5UTF16Buffer();
|
||||
nsHtml5UTF16Buffer* next;
|
||||
nsRefPtr<nsHtml5UTF16Buffer> next;
|
||||
void* key;
|
||||
nsrefcnt AddRef();
|
||||
nsrefcnt Release();
|
||||
private:
|
||||
nsAutoRefCnt mRefCnt;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче