summaryrefslogtreecommitdiffstats
path: root/WebCore/html/HTML5Tokenizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/html/HTML5Tokenizer.cpp')
-rw-r--r--WebCore/html/HTML5Tokenizer.cpp122
1 files changed, 111 insertions, 11 deletions
diff --git a/WebCore/html/HTML5Tokenizer.cpp b/WebCore/html/HTML5Tokenizer.cpp
index c531c7e..0feace5 100644
--- a/WebCore/html/HTML5Tokenizer.cpp
+++ b/WebCore/html/HTML5Tokenizer.cpp
@@ -26,9 +26,12 @@
#include "config.h"
#include "HTML5Tokenizer.h"
+#include "Element.h"
+#include "Frame.h"
#include "HTML5Lexer.h"
-#include "HTML5Token.h"
+#include "HTML5ScriptRunner.h"
#include "HTML5TreeBuilder.h"
+#include "HTMLDocument.h"
#include "Node.h"
#include "NotImplemented.h"
@@ -36,8 +39,11 @@ namespace WebCore {
HTML5Tokenizer::HTML5Tokenizer(HTMLDocument* document, bool reportErrors)
: Tokenizer()
+ , m_document(document)
, m_lexer(new HTML5Lexer)
+ , m_scriptRunner(new HTML5ScriptRunner(document, this))
, m_treeBuilder(new HTML5TreeBuilder(m_lexer.get(), document, reportErrors))
+ , m_wasWaitingOnScriptsDuringFinish(false)
{
begin();
}
@@ -50,33 +56,127 @@ void HTML5Tokenizer::begin()
{
}
-void HTML5Tokenizer::write(const SegmentedString& source, bool)
+void HTML5Tokenizer::pumpLexer()
{
- m_source.append(source);
+ ASSERT(!m_treeBuilder->isPaused());
+ while (m_lexer->nextToken(m_source, m_token)) {
+ m_treeBuilder->constructTreeFromToken(m_token);
+ m_token.clear();
- HTML5Token token;
- while (!m_source.isEmpty()) {
- if (m_lexer->nextToken(m_source, token)) {
- m_treeBuilder->constructTreeFromToken(token);
- token.clear();
- }
+ if (!m_treeBuilder->isPaused())
+ continue;
+
+ // The parser will pause itself when waiting on a script to load or run.
+ // ScriptRunner executes scripts at the right times and handles reentrancy.
+ bool shouldContinueParsing = m_scriptRunner->execute(m_treeBuilder->takeScriptToProcess());
+ m_treeBuilder->setPaused(!shouldContinueParsing);
+ if (!shouldContinueParsing)
+ return;
}
}
+void HTML5Tokenizer::write(const SegmentedString& source, bool)
+{
+ // HTML5Tokenizer::executeScript is responsible for handling saving m_source before re-entry.
+ m_source.append(source);
+ if (!m_treeBuilder->isPaused())
+ pumpLexer();
+}
+
void HTML5Tokenizer::end()
{
+ m_source.close();
+ if (!m_treeBuilder->isPaused())
+ pumpLexer();
m_treeBuilder->finished();
}
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.
+ if (isWaitingForScripts()) {
+ // FIXME: We might want to use real state enum instead of a bool here.
+ m_wasWaitingOnScriptsDuringFinish = true;
+ return;
+ }
+ // 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.
end();
}
+int HTML5Tokenizer::executingScript() const
+{
+ return m_scriptRunner->inScriptExecution();
+}
+
bool HTML5Tokenizer::isWaitingForScripts() const
{
- notImplemented();
- return false;
+ return m_treeBuilder->isPaused();
+}
+
+void HTML5Tokenizer::resumeParsingAfterScriptExecution()
+{
+ ASSERT(!m_scriptRunner->inScriptExecution());
+ ASSERT(!m_treeBuilder->isPaused());
+ pumpLexer();
+ ASSERT(m_treeBuilder->isPaused() || m_source.isEmpty());
+ if (m_source.isEmpty() && m_wasWaitingOnScriptsDuringFinish)
+ end(); // The document already finished parsing we were just waiting on scripts when finished() was called.
+}
+
+void HTML5Tokenizer::watchForLoad(CachedResource* cachedScript)
+{
+ ASSERT(!cachedScript->isLoaded());
+ // addClient would call notifyFinished if the load were complete.
+ // Callers do not expect to be re-entered from this call, so they should
+ // not an already-loaded CachedResource.
+ cachedScript->addClient(this);
+}
+
+void HTML5Tokenizer::stopWatchingForLoad(CachedResource* cachedScript)
+{
+ cachedScript->removeClient(this);
+}
+
+void HTML5Tokenizer::executeScript(const ScriptSourceCode& sourceCode)
+{
+ ASSERT(m_scriptRunner->inScriptExecution());
+ if (!m_document->frame())
+ return;
+
+ SegmentedString oldInsertionPoint = m_source;
+ m_source = SegmentedString();
+ m_document->frame()->script()->executeScript(sourceCode);
+ // Append oldInsertionPoint onto the new (likely empty) m_source instead of
+ // oldInsertionPoint.prepent(m_source) as that would ASSERT if
+ // m_source.escaped() (it had characters pushed back onto it).
+ m_source.append(oldInsertionPoint);
+}
+
+void HTML5Tokenizer::notifyFinished(CachedResource* cachedResource)
+{
+ ASSERT(!m_scriptRunner->inScriptExecution());
+ ASSERT(m_treeBuilder->isPaused());
+ bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
+ m_treeBuilder->setPaused(!shouldContinueParsing);
+ if (shouldContinueParsing)
+ resumeParsingAfterScriptExecution();
+}
+
+void HTML5Tokenizer::executeScriptsWaitingForStylesheets()
+{
+ // 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();
}
}