blob: 1e6f13225f037cebbe50a3eae856a86b5719c272 [file] [log] [blame]
/*
* Copyright (C) 2010 Google, Inc. All rights reserved.
* Copyright (C) 2011-2025 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "ScriptRunner.h"
#include "Document.h"
#include "Element.h"
#include "PendingScript.h"
#include "ScriptElement.h"
#include <wtf/TZoneMallocInlines.h>
namespace WebCore {
WTF_MAKE_TZONE_ALLOCATED_IMPL(ScriptRunner);
ScriptRunner::ScriptRunner(Document& document)
: m_document(document)
, m_timer(*this, &ScriptRunner::timerFired)
{
}
ScriptRunner::~ScriptRunner()
{
Ref document = m_document.get();
for (auto& pendingScript : m_scriptsToExecuteSoon) {
UNUSED_PARAM(pendingScript);
document->decrementLoadEventDelayCount();
}
for (auto& pendingScript : m_scriptsToExecuteInOrder) {
if (pendingScript->watchingForLoad())
pendingScript->clearClient();
document->decrementLoadEventDelayCount();
}
for (auto& pendingScript : m_pendingAsyncScripts) {
if (pendingScript->watchingForLoad())
pendingScript->clearClient();
document->decrementLoadEventDelayCount();
}
}
void ScriptRunner::ref() const
{
m_document->ref();
}
void ScriptRunner::deref() const
{
m_document->deref();
}
void ScriptRunner::queueScriptForExecution(ScriptElement& scriptElement, LoadableScript& loadableScript, ExecutionType executionType)
{
ASSERT(scriptElement.element().isConnected());
m_document->incrementLoadEventDelayCount();
Ref 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
m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(pendingScript).releaseNonNull());
pendingScript.clearClient();
if (!m_document->hasActiveParserYieldToken())
m_timer.startOneShot(0_s);
}
void ScriptRunner::timerFired()
{
Ref document = m_document.get();
Vector<Ref<PendingScript>> scripts;
if (document->shouldDeferAsynchronousScriptsUntilParsingFinishes()) {
// Scripts not added by the parser are executed asynchronously and yet do not have the 'async' attribute set.
// We only want to delay scripts that were explicitly marked as 'async' by the developer.
m_scriptsToExecuteSoon.removeAllMatching([&](auto& pendingScript) {
if (pendingScript->element().hasAsyncAttribute())
return false;
scripts.append(WTF::move(pendingScript));
return true;
});
} else
scripts.swap(m_scriptsToExecuteSoon);
while (!m_scriptsToExecuteInOrder.isEmpty() && Ref { m_scriptsToExecuteInOrder.first() }->isLoaded())
scripts.append(m_scriptsToExecuteInOrder.takeFirst());
for (Ref script : scripts) {
ASSERT(script->needsLoading());
script->element().executePendingScript(script);
document->decrementLoadEventDelayCount();
}
}
void ScriptRunner::clearPendingScripts()
{
m_scriptsToExecuteInOrder.clear();
m_scriptsToExecuteSoon.clear();
m_pendingAsyncScripts.clear();
}
} // namespace WebCore