blob: b9c1c192cfa42a1ff1a2ec3db95f00b1ced9d480 [file] [log] [blame]
/*
* Copyright (C) 2019 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. AND ITS CONTRIBUTORS ``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 ITS 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 "WindowEventLoop.h"
#include "CommonVM.h"
#include "CustomElementReactionQueue.h"
#include "Document.h"
#include "HTMLSlotElement.h"
#include "Microtasks.h"
#include "MutationObserver.h"
#include "SecurityOrigin.h"
#include "ThreadGlobalData.h"
#include <wtf/RunLoop.h>
namespace WebCore {
static HashMap<String, WindowEventLoop*>& windowEventLoopMap()
{
RELEASE_ASSERT(isMainThread());
static NeverDestroyed<HashMap<String, WindowEventLoop*>> map;
return map.get();
}
static String agentClusterKeyOrNullIfUnique(const SecurityOrigin& origin)
{
auto computeKey = [&] {
// https://html.spec.whatwg.org/multipage/webappapis.html#obtain-agent-cluster-key
if (origin.isUnique())
return origin.toString();
RegistrableDomain registrableDomain { origin.data() };
if (registrableDomain.isEmpty())
return origin.toString();
return makeString(origin.protocol(), "://", registrableDomain.string());
};
auto key = computeKey();
if (key.isEmpty() || key == "null"_s)
return { };
return key;
}
Ref<WindowEventLoop> WindowEventLoop::eventLoopForSecurityOrigin(const SecurityOrigin& origin)
{
auto key = agentClusterKeyOrNullIfUnique(origin);
if (key.isNull())
return create({ });
auto addResult = windowEventLoopMap().add(key, nullptr);
if (UNLIKELY(addResult.isNewEntry)) {
auto newEventLoop = create(key);
addResult.iterator->value = newEventLoop.ptr();
return newEventLoop;
}
return *addResult.iterator->value;
}
inline Ref<WindowEventLoop> WindowEventLoop::create(const String& agentClusterKey)
{
return adoptRef(*new WindowEventLoop(agentClusterKey));
}
inline WindowEventLoop::WindowEventLoop(const String& agentClusterKey)
: m_agentClusterKey(agentClusterKey)
, m_timer(*this, &WindowEventLoop::didReachTimeToRun)
, m_perpetualTaskGroupForSimilarOriginWindowAgents(*this)
{
}
WindowEventLoop::~WindowEventLoop()
{
if (m_agentClusterKey.isNull())
return;
auto didRemove = windowEventLoopMap().remove(m_agentClusterKey);
RELEASE_ASSERT(didRemove);
}
void WindowEventLoop::scheduleToRun()
{
m_timer.startOneShot(0_s);
}
bool WindowEventLoop::isContextThread() const
{
return isMainThread();
}
MicrotaskQueue& WindowEventLoop::microtaskQueue()
{
if (!m_microtaskQueue)
m_microtaskQueue = makeUnique<MicrotaskQueue>(commonVM());
return *m_microtaskQueue;
}
void WindowEventLoop::didReachTimeToRun()
{
Ref protectedThis { *this }; // Executing tasks may remove the last reference to this WindowEventLoop.
run();
}
void WindowEventLoop::queueMutationObserverCompoundMicrotask()
{
if (m_mutationObserverCompoundMicrotaskQueuedFlag)
return;
m_mutationObserverCompoundMicrotaskQueuedFlag = true;
m_perpetualTaskGroupForSimilarOriginWindowAgents.queueMicrotask([this] {
// We can't make a Ref to WindowEventLoop in the lambda capture as that would result in a reference cycle & leak.
Ref protectedThis { *this };
m_mutationObserverCompoundMicrotaskQueuedFlag = false;
// FIXME: This check doesn't exist in the spec.
if (m_deliveringMutationRecords)
return;
m_deliveringMutationRecords = true;
MutationObserver::notifyMutationObservers(*this);
m_deliveringMutationRecords = false;
});
}
CustomElementQueue& WindowEventLoop::backupElementQueue()
{
if (!m_processingBackupElementQueue) {
m_processingBackupElementQueue = true;
m_perpetualTaskGroupForSimilarOriginWindowAgents.queueMicrotask([this] {
// We can't make a Ref to WindowEventLoop in the lambda capture as that would result in a reference cycle & leak.
Ref protectedThis { *this };
m_processingBackupElementQueue = false;
ASSERT(m_customElementQueue);
CustomElementReactionQueue::processBackupQueue(*m_customElementQueue);
});
}
if (!m_customElementQueue)
m_customElementQueue = makeUnique<CustomElementQueue>();
return *m_customElementQueue;
}
void WindowEventLoop::breakToAllowRenderingUpdate()
{
#if PLATFORM(MAC)
// On Mac rendering updates happen in a runloop observer.
// Avoid running timers and doing other work (like processing asyncronous IPC) until it is completed.
// FIXME: Also bail out from the task loop in EventLoop::run().
threadGlobalData().threadTimers().breakFireLoopForRenderingUpdate();
RunLoop::main().suspendFunctionDispatchForCurrentCycle();
#endif
}
} // namespace WebCore