#include "config.h"
#include "ScriptRunner.h"
#include "Element.h"
#include "PendingScript.h"
#include "ScriptElement.h"
namespace WebCore {
ScriptRunner::ScriptRunner(Document& document)
: m_document(document)
, m_timer(*this, &ScriptRunner::timerFired)
{
}
ScriptRunner::~ScriptRunner()
{
for (auto& pendingScript : m_scriptsToExecuteSoon) {
UNUSED_PARAM(pendingScript);
m_document.decrementLoadEventDelayCount();
}
for (auto& pendingScript : m_scriptsToExecuteInOrder) {
if (pendingScript->watchingForLoad())
pendingScript->clearClient();
m_document.decrementLoadEventDelayCount();
}
for (auto& pendingScript : m_pendingAsyncScripts) {
if (pendingScript->watchingForLoad())
pendingScript->clearClient();
m_document.decrementLoadEventDelayCount();
}
}
void ScriptRunner::queueScriptForExecution(ScriptElement& scriptElement, LoadableScript& loadableScript, ExecutionType executionType)
{
ASSERT(scriptElement.element().isConnected());
m_document.incrementLoadEventDelayCount();
auto pendingScript = PendingScript::create(scriptElement, loadableScript);
switch (executionType) {
case ASYNC_EXECUTION:
m_pendingAsyncScripts.add(pendingScript.copyRef());
break;
case IN_ORDER_EXECUTION:
m_scriptsToExecuteInOrder.append(pendingScript.copyRef());
break;
}
pendingScript->setClient(*this);
}
void ScriptRunner::suspend()
{
m_timer.stop();
}
void ScriptRunner::resume()
{
if (hasPendingScripts() && !m_document.hasActiveParserYieldToken())
m_timer.startOneShot(0_s);
}
void ScriptRunner::documentFinishedParsing()
{
if (!m_scriptsToExecuteSoon.isEmpty() && !m_timer.isActive())
resume();
}
void ScriptRunner::notifyFinished(PendingScript& pendingScript)
{
if (pendingScript.element().willExecuteInOrder())
ASSERT(!m_scriptsToExecuteInOrder.isEmpty());
else {
ASSERT(m_pendingAsyncScripts.contains(pendingScript));
m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(pendingScript)->ptr());
}
pendingScript.clearClient();
if (!m_document.hasActiveParserYieldToken())
m_timer.startOneShot(0_s);
}
void ScriptRunner::timerFired()
{
Ref<Document> protect(m_document);
Vector<RefPtr<PendingScript>> scripts;
if (m_document.shouldDeferAsynchronousScriptsUntilParsingFinishes()) {
m_scriptsToExecuteSoon.removeAllMatching([&](auto& pendingScript) {
if (pendingScript->element().hasAsyncAttribute())
return false;
scripts.append(WTFMove(pendingScript));
return true;
});
} else
scripts.swap(m_scriptsToExecuteSoon);
size_t numInOrderScriptsToExecute = 0;
for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]->isLoaded(); ++numInOrderScriptsToExecute)
scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].ptr());
if (numInOrderScriptsToExecute)
m_scriptsToExecuteInOrder.remove(0, numInOrderScriptsToExecute);
for (auto& currentScript : scripts) {
auto script = WTFMove(currentScript);
ASSERT(script);
if (!script)
continue;
ASSERT(script->needsLoading());
script->element().executePendingScript(*script);
m_document.decrementLoadEventDelayCount();
}
}
}