Bug 715103 - Move parser unblocking management from nsContentSink to nsScriptLoader. r=smaug.

This commit is contained in:
Henri Sivonen 2012-01-20 13:16:27 +02:00
parent b2d65a0277
commit 3fee03c38e
15 changed files with 117 additions and 202 deletions

View File

@ -49,8 +49,8 @@
#include "nsIDOMHTMLScriptElement.h"
#define NS_ISCRIPTELEMENT_IID \
{ 0x5bb3b905, 0x5988, 0x476f, \
{ 0x95, 0x4f, 0x99, 0x02, 0x59, 0x82, 0x24, 0x67 } }
{ 0x24ab3ff2, 0xd75e, 0x4be4, \
{ 0x8d, 0x50, 0xd6, 0x75, 0x31, 0x29, 0xab, 0x65 } }
/**
* Internal interface implemented by script elements
@ -186,6 +186,28 @@ public:
mCreatorParser = getter_AddRefs(NS_GetWeakReference(aParser));
}
/**
* Unblocks the creator parser
*/
void UnblockParser()
{
nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser);
if (parser) {
parser->UnblockParser();
}
}
/**
* Attempts to resume parsing asynchronously
*/
void ContinueParserAsync()
{
nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser);
if (parser) {
parser->ContinueInterruptedParsingAsync();
}
}
/**
* Informs the creator parser that the evaluation of this script is starting
*/

View File

@ -109,68 +109,16 @@ using namespace mozilla;
PRLogModuleInfo* gContentSinkLogModuleInfo;
class nsScriptLoaderObserverProxy : public nsIScriptLoaderObserver
{
public:
nsScriptLoaderObserverProxy(nsIScriptLoaderObserver* aInner)
: mInner(do_GetWeakReference(aInner))
{
}
virtual ~nsScriptLoaderObserverProxy()
{
}
NS_DECL_ISUPPORTS
NS_DECL_NSISCRIPTLOADEROBSERVER
nsWeakPtr mInner;
};
NS_IMPL_ISUPPORTS1(nsScriptLoaderObserverProxy, nsIScriptLoaderObserver)
NS_IMETHODIMP
nsScriptLoaderObserverProxy::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline,
nsIURI *aURI,
PRInt32 aLineNo)
{
nsCOMPtr<nsIScriptLoaderObserver> inner = do_QueryReferent(mInner);
if (inner) {
return inner->ScriptAvailable(aResult, aElement, aIsInline, aURI,
aLineNo);
}
return NS_OK;
}
NS_IMETHODIMP
nsScriptLoaderObserverProxy::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline)
{
nsCOMPtr<nsIScriptLoaderObserver> inner = do_QueryReferent(mInner);
if (inner) {
return inner->ScriptEvaluated(aResult, aElement, aIsInline);
}
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink)
NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIScriptLoaderObserver)
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptLoaderObserver)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
@ -182,7 +130,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParser)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNodeInfoManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mScriptElements)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
@ -190,7 +137,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNodeInfoManager,
nsNodeInfoManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mScriptElements)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -303,13 +249,6 @@ nsContentSink::Init(nsIDocument* aDoc,
(loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0);
}
// use this to avoid a circular reference sink->document->scriptloader->sink
nsCOMPtr<nsIScriptLoaderObserver> proxy =
new nsScriptLoaderObserverProxy(this);
NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
mScriptLoader->AddObserver(proxy);
ProcessHTTPHeaders(aChannel);
}
@ -366,90 +305,6 @@ nsContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
return NS_OK;
}
NS_IMETHODIMP
nsContentSink::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline,
nsIURI *aURI,
PRInt32 aLineNo)
{
PRUint32 count = mScriptElements.Count();
// aElement will not be in mScriptElements if a <script> was added
// using the DOM during loading or if DoneAddingChildren did not return
// NS_ERROR_HTMLPARSER_BLOCK.
NS_ASSERTION(count == 0 ||
mScriptElements.IndexOf(aElement) == PRInt32(count - 1) ||
mScriptElements.IndexOf(aElement) == -1,
"script found at unexpected position");
// Check if this is the element we were waiting for
if (count == 0 || aElement != mScriptElements[count - 1]) {
return NS_OK;
}
NS_ASSERTION(!aElement->GetScriptDeferred(), "defer script was in mScriptElements");
NS_ASSERTION(!aElement->GetScriptAsync(), "async script was in mScriptElements");
if (mParser && !mParser->IsParserEnabled()) {
// make sure to unblock the parser before evaluating the script,
// we must unblock the parser even if loading the script failed or
// if the script was empty, if we don't, the parser will never be
// unblocked.
mParser->UnblockParser();
}
if (NS_SUCCEEDED(aResult)) {
PreEvaluateScript();
} else {
mScriptElements.RemoveObjectAt(count - 1);
if (mParser && aResult != NS_BINDING_ABORTED) {
// Loading external script failed!. So, resume parsing since the parser
// got blocked when loading external script. See
// http://bugzilla.mozilla.org/show_bug.cgi?id=94903.
//
// XXX We don't resume parsing if we get NS_BINDING_ABORTED from the
// script load, assuming that that error code means that the user
// stopped the load through some action (like clicking a link). See
// http://bugzilla.mozilla.org/show_bug.cgi?id=243392.
ContinueInterruptedParsingAsync();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsContentSink::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline)
{
mDeflectedCount = sPerfDeflectCount;
// Check if this is the element we were waiting for
PRInt32 count = mScriptElements.Count();
if (count == 0 || aElement != mScriptElements[count - 1]) {
return NS_OK;
}
NS_ASSERTION(!aElement->GetScriptDeferred(), "defer script was in mScriptElements");
NS_ASSERTION(!aElement->GetScriptAsync(), "async script was in mScriptElements");
// Pop the script element stack
mScriptElements.RemoveObjectAt(count - 1);
if (NS_SUCCEEDED(aResult)) {
PostEvaluateScript(aElement);
}
if (mParser && mParser->IsParserEnabled()) {
ContinueInterruptedParsingAsync();
}
return NS_OK;
}
nsresult
nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel)
{
@ -1704,25 +1559,6 @@ nsContentSink::WillBuildModelImpl()
}
}
void
nsContentSink::ContinueInterruptedParsingIfEnabled()
{
// This shouldn't be called in the HTML5 case.
if (mParser && mParser->IsParserEnabled()) {
mParser->ContinueInterruptedParsing();
}
}
// Overridden in the HTML5 case
void
nsContentSink::ContinueInterruptedParsingAsync()
{
nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
&nsContentSink::ContinueInterruptedParsingIfEnabled);
NS_DispatchToCurrentThread(ev);
}
/* static */
void
nsContentSink::NotifyDocElementCreated(nsIDocument* aDoc)

View File

@ -46,7 +46,6 @@
// Base class for contentsink implementations.
#include "nsICSSLoaderObserver.h"
#include "nsIScriptLoaderObserver.h"
#include "nsWeakReference.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
@ -64,6 +63,7 @@
#include "nsIRequest.h"
#include "nsCycleCollectionParticipant.h"
#include "nsThreadUtils.h"
#include "nsIScriptElement.h"
class nsIDocument;
class nsIURI;
@ -113,16 +113,13 @@ extern PRLogModuleInfo* gContentSinkLogModuleInfo;
#define NS_DELAY_FOR_WINDOW_CREATION 500000
class nsContentSink : public nsICSSLoaderObserver,
public nsIScriptLoaderObserver,
public nsSupportsWeakReference,
public nsStubDocumentObserver,
public nsITimerCallback
{
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsContentSink,
nsIScriptLoaderObserver)
NS_DECL_NSISCRIPTLOADEROBSERVER
nsICSSLoaderObserver)
// nsITimerCallback
NS_DECL_NSITIMERCALLBACK
@ -290,10 +287,6 @@ protected:
return sNotificationInterval;
}
// Overridable hooks into script evaluation
virtual void PreEvaluateScript() {return;}
virtual void PostEvaluateScript(nsIScriptElement *aElement) {return;}
virtual nsresult FlushTags() = 0;
// Later on we might want to make this more involved somehow
@ -302,6 +295,10 @@ protected:
void DoProcessLinkHeader();
void StopDeflecting() {
mDeflectedCount = sPerfDeflectCount;
}
private:
// People shouldn't be allocating this class directly. All subclasses should
// be allocated using a zeroing operator new.
@ -309,9 +306,6 @@ private:
protected:
virtual void ContinueInterruptedParsingAsync();
void ContinueInterruptedParsingIfEnabled();
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsIParser> mParser;
nsCOMPtr<nsIURI> mDocumentURI;
@ -320,8 +314,6 @@ protected:
nsRefPtr<nsNodeInfoManager> mNodeInfoManager;
nsRefPtr<nsScriptLoader> mScriptLoader;
nsCOMArray<nsIScriptElement> mScriptElements;
// back off timer notification after count
PRInt32 mBackoffCount;

View File

@ -942,8 +942,9 @@ nsScriptLoader::ProcessPendingRequests()
!mParserBlockingRequest->mLoading &&
ReadyToExecuteScripts()) {
request.swap(mParserBlockingRequest);
// nsContentSink::ScriptAvailable unblocks the parser
UnblockParser(request);
ProcessRequest(request);
ContinueParserAsync(request);
}
while (ReadyToExecuteScripts() &&
@ -1169,8 +1170,9 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
FireScriptAvailable(rv, request);
} else if (mParserBlockingRequest == request) {
mParserBlockingRequest = nsnull;
// nsContentSink::ScriptAvailable unblocks the parser
UnblockParser(request);
FireScriptAvailable(rv, request);
ContinueParserAsync(request);
} else {
mPreloads.RemoveElement(request, PreloadRequestComparator());
}
@ -1182,6 +1184,18 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
return NS_OK;
}
void
nsScriptLoader::UnblockParser(nsScriptLoadRequest* aParserBlockingRequest)
{
aParserBlockingRequest->mElement->UnblockParser();
}
void
nsScriptLoader::ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest)
{
aParserBlockingRequest->mElement->ContinueParserAsync();
}
nsresult
nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIStreamLoader* aLoader,

View File

@ -243,6 +243,17 @@ public:
const nsAString &aType);
private:
/**
* Unblocks the creator parser of the parser-blocking scripts.
*/
void UnblockParser(nsScriptLoadRequest* aParserBlockingRequest);
/**
* Asynchronously resumes the creator parser of the parser-blocking scripts.
*/
void ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest);
/**
* Helper function to check the content policy for a given request.
*/

View File

@ -1699,8 +1699,6 @@ HTMLContentSink::DidBuildModel(bool aTerminated)
ScrollToRef();
mDocument->ScriptLoader()->RemoveObserver(this);
// Make sure we no longer respond to document mutations. We've flushed all
// our notifications out, so there's no need to do anything else here.

View File

@ -327,7 +327,6 @@ nsXMLContentSink::DidBuildModel(bool aTerminated)
}
else {
// Kick off layout for non-XSLT transformed documents.
mDocument->ScriptLoader()->RemoveObserver(this);
// Check if we want to prettyprint
MaybePrettyPrint();
@ -419,8 +418,6 @@ nsXMLContentSink::OnTransformDone(nsresult aResult,
}
}
originalDocument->ScriptLoader()->RemoveObserver(this);
// Notify document observers that all the content has been stuck
// into the document.
// XXX do we need to notify for things like PIs? Or just the
@ -596,16 +593,13 @@ nsXMLContentSink::CloseElement(nsIContent* aContent)
return NS_OK;
}
// Always check the clock in nsContentSink right after a script
StopDeflecting();
// Now tell the script that it's ready to go. This may execute the script
// or return true, or neither if the script doesn't need executing.
bool block = sele->AttemptToExecute();
// If the act of insertion evaluated the script, we're fine.
// Else, block the parser till the script has loaded.
if (block) {
mScriptElements.AppendObject(sele);
}
// If the parser got blocked, make sure to return the appropriate rv.
// I'm not sure if this is actually needed or not.
if (mParser && !mParser->IsParserEnabled()) {
@ -1681,3 +1675,21 @@ nsXMLContentSink::IsMonolithicContainer(nsINodeInfo* aNodeInfo)
(aNodeInfo->NameAtom() == nsGkAtoms::math))
);
}
void
nsXMLContentSink::ContinueInterruptedParsingIfEnabled()
{
if (mParser && mParser->IsParserEnabled()) {
mParser->ContinueInterruptedParsing();
}
}
void
nsXMLContentSink::ContinueInterruptedParsingAsync()
{
nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
&nsXMLContentSink::ContinueInterruptedParsingIfEnabled);
NS_DispatchToCurrentThread(ev);
}

View File

@ -102,6 +102,7 @@ public:
NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
virtual nsISupports *GetTarget();
virtual bool IsScriptExecuting();
virtual void ContinueInterruptedParsingAsync();
// nsITransformObserver
NS_IMETHOD OnDocumentCreated(nsIDocument *aResultDocument);
@ -115,6 +116,9 @@ public:
bool &aIsAlternate);
protected:
void ContinueInterruptedParsingIfEnabled();
// Start layout. If aIgnorePendingSheets is true, this will happen even if
// we still have stylesheet loads pending. Otherwise, we'll wait until the
// stylesheets are all done loading.

View File

@ -197,6 +197,13 @@ NS_IMETHODIMP_(void)
nsHtml5Parser::UnblockParser()
{
mBlocked = false;
mExecutor->ContinueInterruptedParsingAsync();
}
NS_IMETHODIMP_(void)
nsHtml5Parser::ContinueInterruptedParsingAsync()
{
mExecutor->ContinueInterruptedParsingAsync();
}
NS_IMETHODIMP_(bool)

View File

@ -148,6 +148,11 @@ class nsHtml5Parser : public nsIParser,
*/
NS_IMETHOD_(void) UnblockParser();
/**
* Asynchronously continues parsing.
*/
NS_IMETHOD_(void) ContinueInterruptedParsingAsync();
/**
* Query whether the parser is enabled (i.e. not blocked) or not.
*/

View File

@ -159,7 +159,6 @@ nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated)
}
ScrollToRef();
mDocument->ScriptLoader()->RemoveObserver(this);
mDocument->RemoveObserver(this);
if (!mParser) {
// DidBuildModelImpl may cause mParser to be nulled out
@ -558,7 +557,8 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
// must be tail call when mFlushState is eNotFlushing
RunScript(scriptElement);
// The script execution machinery makes sure this doesn't get deflected.
// Always check the clock in nsContentSink right after a script
StopDeflecting();
if (nsContentSink::DidProcessATokenImpl() ==
NS_ERROR_HTMLPARSER_INTERRUPTED) {
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
@ -756,7 +756,6 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
// If the act of insertion evaluated the script, we're fine.
// Else, block the parser till the script has loaded.
if (block) {
mScriptElements.AppendObject(sele);
if (mParser) {
mParser->BlockParser();
}

View File

@ -56,10 +56,9 @@
class nsIParser;
// 57b395ad-4276-408c-9f98-7044b5025c3d
#define NS_ICONTENT_SINK_IID \
{ 0x57b395ad, 0x4276, 0x408c, \
{ 0x9f, 0x98, 0x70, 0x44, 0xb5, 0x02, 0x5c, 0x3d } }
{ 0xcf9a7cbb, 0xfcbc, 0x4e13, \
{ 0x8e, 0xf5, 0x18, 0xef, 0x2d, 0x3d, 0x58, 0x29 } }
class nsIContentSink : public nsISupports {
public:
@ -155,6 +154,10 @@ public:
return false;
}
/**
* Posts a runnable that continues parsing.
*/
virtual void ContinueInterruptedParsingAsync() {};
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentSink, NS_ICONTENT_SINK_IID)

View File

@ -55,8 +55,8 @@
#include "nsIAtom.h"
#define NS_IPARSER_IID \
{ 0x30ffba62, 0x0928, 0x4503, \
{ 0xa8, 0x95, 0xd1, 0x32, 0x76, 0x40, 0x2a, 0x2a } }
{ 0x11a4f41f, 0x7044, 0x4c57, \
{ 0xb3, 0xa4, 0xed, 0x79, 0xc7, 0xc4, 0x61, 0x99 } }
// {41421C60-310A-11d4-816F-000064657374}
#define NS_IDEBUG_DUMP_CONTENT_IID \
@ -219,6 +219,11 @@ class nsIParser : public nsISupports {
// the parsing engine.
NS_IMETHOD_(void) UnblockParser() = 0;
/**
* Asynchronously continues parsing.
*/
NS_IMETHOD_(void) ContinueInterruptedParsingAsync() = 0;
NS_IMETHOD_(bool) IsParserEnabled() = 0;
NS_IMETHOD_(bool) IsComplete() = 0;

View File

@ -1192,6 +1192,12 @@ nsParser::UnblockParser()
}
}
NS_IMETHODIMP_(void)
nsParser::ContinueInterruptedParsingAsync()
{
mSink->ContinueInterruptedParsingAsync();
}
/**
* Call this to query whether the parser is enabled or not.
*/

View File

@ -216,6 +216,7 @@ class nsParser : public nsIParser,
NS_IMETHOD ContinueInterruptedParsing();
NS_IMETHOD_(void) BlockParser();
NS_IMETHOD_(void) UnblockParser();
NS_IMETHOD_(void) ContinueInterruptedParsingAsync();
NS_IMETHOD Terminate(void);
/**