Commit 98eba819 authored by eric@webkit.org's avatar eric@webkit.org

2010-05-29 Eric Seidel <eric@webkit.org>

        Reviewed by Adam Barth.

        HTML5 parser should block script execution until stylesheets load
        https://bugs.webkit.org/show_bug.cgi?id=39903

        All <script> tag execution now blocks on stylesheet load, including
        inline <script> content which the old parser doesn't support blocking.

        Hyatt says we could now get rid of updateLayoutIgnorePendingStylesheets
        once our primary parser knows how to wait for stylesheets
        before executing inline <script> content.

        All of http/tests/local passes in --html5-parser mode now.
        Also fixed fast/parser/tokenizer-close-during-document-write.html.

        * html/HTML5ScriptRunner.cpp:
        (WebCore::HTML5ScriptRunner::HTML5ScriptRunner):
         - Added a m_hasScriptsWaitingForStylesheets bool so that we can
           detect when we're actually waiting on stylesheets or not.
           If we're not waiting on stylesheets then we're still parsing and
           executing scripts would cause parser/script reentrancy and bad news bears.
        (WebCore::HTML5ScriptRunner::isPendingScriptReady):
         - Re-enable the check that the stylesheets have loaded.
        (WebCore::HTML5ScriptRunner::executePendingScript):
         - ASSERT that stylesheets have loaded.
        (WebCore::HTML5ScriptRunner::executeScriptsWaitingForLoad):
         - ASSERT that this is never called reentrantly.
        (WebCore::HTML5ScriptRunner::executeScriptsWaitingForStylesheets):
         - Execute any scripts which were blocked on stylesheet loads.
         - ASSERT (in two ways) that this is never called reentrantly.
        * html/HTML5ScriptRunner.h:
        (WebCore::HTML5ScriptRunner::hasScriptsWaitingForStylesheets):
         - Callers need to check this before calling executeScriptsWaitingForLoad.
        (WebCore::HTML5ScriptRunner::inScriptExecution):
         - Used by callers to ASSERT that we're not called re-entrantly.
        * html/HTML5Tokenizer.cpp:
        (WebCore::HTML5Tokenizer::HTML5Tokenizer):
         - Add m_hasScriptsWaitingForStylesheets for tracking if we've paused
           due to stylesheets or not.  Callers need to know this to know if they
           should ignore executeScriptsWaitingForStylesheets calls from
           Document (which may be generated when parsing </script> tags).
           We only care about executeScriptsWaitingForStylesheets calls when
           we've actually blocked the parser due to waiting on a stylesheet load.
        (WebCore::HTML5Tokenizer::end):
         - Move m_source.close() back to this method now that I understand more
           about when finish() is called.  This should fix several layout test ASSERTS.
        (WebCore::HTML5Tokenizer::finish):
         - This should not close m_source since scripts may still write to
           the document.  Set m_wasWaitingOnScriptsDuringFinish to indicate
           that we were not able to end parsing during finish.
        (WebCore::HTML5Tokenizer::resumeParsingAfterScriptExecution):
         - ASSERT that this is never caller re-entrantly.
        (WebCore::HTML5Tokenizer::executeScript):
         - ASSERT that the ScriptRunner always thinks we're running scripts when this is called.
        (WebCore::HTML5Tokenizer::executingScript):
         - Added implementation to fix fast/parser/tokenizer-close-during-document-write.html
        (WebCore::HTML5Tokenizer::notifyFinished):
         - ASSERT that this is never called re-entrantly.
        (WebCore::HTML5Tokenizer::executeScriptsWaitingForStylesheets):
         - Call the ScriptRunner to tell it that stylesheets have loaded if
           it is blocked on stylesheet loads.
         - ASSERT(m_hasScriptsWaitingForStylesheets).  We can't just assert
           isPaused() since we may be paused for script loads.
        * html/HTML5Tokenizer.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@60409 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 6c242be7
2010-05-29 Eric Seidel <eric@webkit.org>
Reviewed by Adam Barth.
HTML5 parser should block script execution until stylesheets load
https://bugs.webkit.org/show_bug.cgi?id=39903
All <script> tag execution now blocks on stylesheet load, including
inline <script> content which the old parser doesn't support blocking.
Hyatt says we could now get rid of updateLayoutIgnorePendingStylesheets
once our primary parser knows how to wait for stylesheets
before executing inline <script> content.
All of http/tests/local passes in --html5-parser mode now.
Also fixed fast/parser/tokenizer-close-during-document-write.html.
* html/HTML5ScriptRunner.cpp:
(WebCore::HTML5ScriptRunner::HTML5ScriptRunner):
- Added a m_hasScriptsWaitingForStylesheets bool so that we can
detect when we're actually waiting on stylesheets or not.
If we're not waiting on stylesheets then we're still parsing and
executing scripts would cause parser/script reentrancy and bad news bears.
(WebCore::HTML5ScriptRunner::isPendingScriptReady):
- Re-enable the check that the stylesheets have loaded.
(WebCore::HTML5ScriptRunner::executePendingScript):
- ASSERT that stylesheets have loaded.
(WebCore::HTML5ScriptRunner::executeScriptsWaitingForLoad):
- ASSERT that this is never called reentrantly.
(WebCore::HTML5ScriptRunner::executeScriptsWaitingForStylesheets):
- Execute any scripts which were blocked on stylesheet loads.
- ASSERT (in two ways) that this is never called reentrantly.
* html/HTML5ScriptRunner.h:
(WebCore::HTML5ScriptRunner::hasScriptsWaitingForStylesheets):
- Callers need to check this before calling executeScriptsWaitingForLoad.
(WebCore::HTML5ScriptRunner::inScriptExecution):
- Used by callers to ASSERT that we're not called re-entrantly.
* html/HTML5Tokenizer.cpp:
(WebCore::HTML5Tokenizer::HTML5Tokenizer):
- Add m_hasScriptsWaitingForStylesheets for tracking if we've paused
due to stylesheets or not. Callers need to know this to know if they
should ignore executeScriptsWaitingForStylesheets calls from
Document (which may be generated when parsing </script> tags).
We only care about executeScriptsWaitingForStylesheets calls when
we've actually blocked the parser due to waiting on a stylesheet load.
(WebCore::HTML5Tokenizer::end):
- Move m_source.close() back to this method now that I understand more
about when finish() is called. This should fix several layout test ASSERTS.
(WebCore::HTML5Tokenizer::finish):
- This should not close m_source since scripts may still write to
the document. Set m_wasWaitingOnScriptsDuringFinish to indicate
that we were not able to end parsing during finish.
(WebCore::HTML5Tokenizer::resumeParsingAfterScriptExecution):
- ASSERT that this is never caller re-entrantly.
(WebCore::HTML5Tokenizer::executeScript):
- ASSERT that the ScriptRunner always thinks we're running scripts when this is called.
(WebCore::HTML5Tokenizer::executingScript):
- Added implementation to fix fast/parser/tokenizer-close-during-document-write.html
(WebCore::HTML5Tokenizer::notifyFinished):
- ASSERT that this is never called re-entrantly.
(WebCore::HTML5Tokenizer::executeScriptsWaitingForStylesheets):
- Call the ScriptRunner to tell it that stylesheets have loaded if
it is blocked on stylesheet loads.
- ASSERT(m_hasScriptsWaitingForStylesheets). We can't just assert
isPaused() since we may be paused for script loads.
* html/HTML5Tokenizer.h:
2010-05-29 Laszlo Gombos <laszlo.1.gombos@nokia.com>
Reviewed by Darin Adler.
......
......@@ -46,6 +46,7 @@ HTML5ScriptRunner::HTML5ScriptRunner(Document* document, HTML5ScriptRunnerHost*
: m_document(document)
, m_host(host)
, m_scriptNestingLevel(0)
, m_hasScriptsWaitingForStylesheets(false)
{
}
......@@ -89,10 +90,9 @@ ScriptSourceCode HTML5ScriptRunner::sourceFromPendingScript(const PendingScript&
bool HTML5ScriptRunner::isPendingScriptReady(const PendingScript& script)
{
// FIXME: We can't block for stylesheets yet, because that causes us to re-enter
// the parser from executeScriptsWaitingForStylesheets when parsing style tags.
// if (!m_document->haveStylesheetsLoaded())
// return false;
m_hasScriptsWaitingForStylesheets = !m_document->haveStylesheetsLoaded();
if (m_hasScriptsWaitingForStylesheets)
return false;
if (script.cachedScript && !script.cachedScript->isLoaded())
return false;
return true;
......@@ -101,9 +101,7 @@ bool HTML5ScriptRunner::isPendingScriptReady(const PendingScript& script)
void HTML5ScriptRunner::executePendingScript()
{
ASSERT(!m_scriptNestingLevel);
// FIXME: We can't block for stylesheets yet, because that causes us to re-enter
// the parser from executeScriptsWaitingForStylesheets when parsing style tags.
// ASSERT(m_document->haveStylesheetsLoaded());
ASSERT(m_document->haveStylesheetsLoaded());
bool errorOccurred = false;
ASSERT(isPendingScriptReady(m_parsingBlockingScript));
ScriptSourceCode sourceCode = sourceFromPendingScript(m_parsingBlockingScript, errorOccurred);
......@@ -185,11 +183,22 @@ bool HTML5ScriptRunner::executeParsingBlockingScripts()
bool HTML5ScriptRunner::executeScriptsWaitingForLoad(CachedResource*)
{
ASSERT(!m_scriptNestingLevel);
ASSERT(m_parsingBlockingScript.element);
ASSERT(m_parsingBlockingScript.cachedScript->isLoaded());
return executeParsingBlockingScripts();
}
bool HTML5ScriptRunner::executeScriptsWaitingForStylesheets()
{
// Callers should check hasScriptsWaitingForStylesheets() before calling
// to prevent parser or script re-entry during </style> parsing.
ASSERT(m_hasScriptsWaitingForStylesheets);
ASSERT(!m_scriptNestingLevel);
ASSERT(m_document->haveStylesheetsLoaded());
return executeParsingBlockingScripts();
}
void HTML5ScriptRunner::requestScript(Element* script)
{
ASSERT(!m_parsingBlockingScript.element);
......
......@@ -50,6 +50,10 @@ public:
bool execute(PassRefPtr<Element> scriptToProcess);
// Processes any pending scripts.
bool executeScriptsWaitingForLoad(CachedResource*);
bool hasScriptsWaitingForStylesheets() const { return m_hasScriptsWaitingForStylesheets; }
bool executeScriptsWaitingForStylesheets();
bool inScriptExecution() { return !!m_scriptNestingLevel; }
private:
struct PendingScript {
......@@ -85,6 +89,12 @@ private:
HTML5ScriptRunnerHost* m_host;
PendingScript m_parsingBlockingScript;
unsigned m_scriptNestingLevel;
// We only want stylesheet loads to trigger script execution if script
// execution is currently stopped due to stylesheet loads, otherwise we'd
// cause nested sript execution when parsing <style> tags since </style>
// tags can cause Document to call executeScriptsWaitingForStylesheets.
bool m_hasScriptsWaitingForStylesheets;
};
}
......
......@@ -43,6 +43,7 @@ HTML5Tokenizer::HTML5Tokenizer(HTMLDocument* document, bool reportErrors)
, m_lexer(new HTML5Lexer)
, m_scriptRunner(new HTML5ScriptRunner(document, this))
, m_treeBuilder(new HTML5TreeBuilder(m_lexer.get(), document, reportErrors))
, m_wasWaitingOnScriptsDuringFinish(false)
{
begin();
}
......@@ -84,6 +85,7 @@ void HTML5Tokenizer::write(const SegmentedString& source, bool)
void HTML5Tokenizer::end()
{
m_source.close();
if (!m_treeBuilder->isPaused())
pumpLexer();
m_treeBuilder->finished();
......@@ -93,9 +95,18 @@ void HTML5Tokenizer::finish()
{
// finish() indicates we will not receive any more data. If we are waiting on
// an external script to load, we can't finish parsing quite yet.
m_source.close();
if (!m_treeBuilder->isPaused())
// We can't call m_source.close() yet as we may have a <script> execution
// pending which will call document.write(). No more data off the network though.
if (!m_treeBuilder->isPaused()) {
// FIXME: We might want to use real state enum instead of a bool here.
m_wasWaitingOnScriptsDuringFinish = true;
end();
}
}
int HTML5Tokenizer::executingScript() const
{
return m_scriptRunner->inScriptExecution();
}
bool HTML5Tokenizer::isWaitingForScripts() const
......@@ -105,10 +116,11 @@ bool HTML5Tokenizer::isWaitingForScripts() const
void HTML5Tokenizer::resumeParsingAfterScriptExecution()
{
ASSERT(!m_scriptRunner->inScriptExecution());
ASSERT(!m_treeBuilder->isPaused());
// FIXME: This is the wrong write in the case of document.write re-entry.
pumpLexer();
if (m_source.isEmpty() && m_source.isClosed())
if (m_source.isEmpty() && m_wasWaitingOnScriptsDuringFinish)
end(); // The document already finished parsing we were just waiting on scripts when finished() was called.
}
......@@ -128,6 +140,7 @@ void HTML5Tokenizer::stopWatchingForLoad(CachedResource* cachedScript)
void HTML5Tokenizer::executeScript(const ScriptSourceCode& sourceCode)
{
ASSERT(m_scriptRunner->inScriptExecution());
if (!m_document->frame())
return;
......@@ -141,17 +154,27 @@ void HTML5Tokenizer::executeScript(const ScriptSourceCode& sourceCode)
void HTML5Tokenizer::notifyFinished(CachedResource* cachedResource)
{
ASSERT(!m_scriptRunner->inScriptExecution());
ASSERT(m_treeBuilder->isPaused());
bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
if (shouldContinueParsing) {
m_treeBuilder->setPaused(false);
m_treeBuilder->setPaused(!shouldContinueParsing);
if (shouldContinueParsing)
resumeParsingAfterScriptExecution();
}
}
void HTML5Tokenizer::executeScriptsWaitingForStylesheets()
{
// FIXME: We can't block for stylesheets yet, because that causes us to re-enter
// the parser from executeScriptsWaitingForStylesheets when parsing style tags.
// Ignore calls unless we have a script blocking the parser waiting on a
// stylesheet load. Otherwise we are currently parsing and this
// is a re-entrant call from encountering a </ style> tag.
if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
return;
ASSERT(!m_scriptRunner->inScriptExecution());
ASSERT(m_treeBuilder->isPaused());
bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForStylesheets();
m_treeBuilder->setPaused(!shouldContinueParsing);
if (shouldContinueParsing)
resumeParsingAfterScriptExecution();
}
}
......@@ -54,6 +54,7 @@ public:
virtual void write(const SegmentedString&, bool appendData);
virtual void end();
virtual void finish();
virtual int executingScript() const;
virtual bool isWaitingForScripts() const;
virtual void executeScriptsWaitingForStylesheets();
......@@ -78,6 +79,7 @@ private:
OwnPtr<HTML5Lexer> m_lexer;
OwnPtr<HTML5ScriptRunner> m_scriptRunner;
OwnPtr<HTML5TreeBuilder> m_treeBuilder;
bool m_wasWaitingOnScriptsDuringFinish;
};
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment