#include "config.h"
#include "MessagePort.h"
#include "Document.h"
#include "EventNames.h"
#include "ExceptionCode.h"
#include "MessageEvent.h"
#include "SecurityOrigin.h"
#include "WorkerGlobalScope.h"
namespace WebCore {
MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext)
: m_started(false)
, m_closed(false)
, m_scriptExecutionContext(&scriptExecutionContext)
{
m_scriptExecutionContext->createdMessagePort(*this);
}
MessagePort::~MessagePort()
{
close();
if (m_scriptExecutionContext)
m_scriptExecutionContext->destroyedMessagePort(*this);
}
void MessagePort::postMessage(RefPtr<SerializedScriptValue>&& message, MessagePort* port, ExceptionCode& ec)
{
MessagePortArray ports;
if (port)
ports.append(port);
postMessage(WTFMove(message), &ports, ec);
}
void MessagePort::postMessage(RefPtr<SerializedScriptValue>&& message, const MessagePortArray* ports, ExceptionCode& ec)
{
if (!isEntangled())
return;
ASSERT(m_scriptExecutionContext);
std::unique_ptr<MessagePortChannelArray> channels;
if (ports) {
for (auto& dataPort : *ports) {
if (dataPort == this || m_entangledChannel->isConnectedTo(dataPort.get())) {
ec = DATA_CLONE_ERR;
return;
}
}
channels = MessagePort::disentanglePorts(ports, ec);
if (ec)
return;
}
m_entangledChannel->postMessageToRemote(WTFMove(message), WTFMove(channels));
}
std::unique_ptr<MessagePortChannel> MessagePort::disentangle()
{
ASSERT(m_entangledChannel);
m_entangledChannel->disentangle();
ASSERT(m_scriptExecutionContext);
m_scriptExecutionContext->destroyedMessagePort(*this);
m_scriptExecutionContext = nullptr;
return WTFMove(m_entangledChannel);
}
void MessagePort::messageAvailable()
{
ASSERT(m_scriptExecutionContext);
m_scriptExecutionContext->processMessagePortMessagesSoon();
}
void MessagePort::start()
{
if (!isEntangled())
return;
ASSERT(m_scriptExecutionContext);
if (m_started)
return;
m_started = true;
m_scriptExecutionContext->processMessagePortMessagesSoon();
}
void MessagePort::close()
{
if (isEntangled())
m_entangledChannel->close();
m_closed = true;
}
void MessagePort::entangle(std::unique_ptr<MessagePortChannel> remote)
{
ASSERT(!m_entangledChannel);
ASSERT(m_scriptExecutionContext);
if (remote->entangleIfOpen(this))
m_entangledChannel = WTFMove(remote);
}
void MessagePort::contextDestroyed()
{
ASSERT(m_scriptExecutionContext);
ASSERT(m_closed);
m_scriptExecutionContext = nullptr;
}
void MessagePort::dispatchMessages()
{
ASSERT(started());
RefPtr<SerializedScriptValue> message;
std::unique_ptr<MessagePortChannelArray> channels;
while (m_entangledChannel && m_entangledChannel->tryGetMessageFromRemote(message, channels)) {
if (is<WorkerGlobalScope>(*m_scriptExecutionContext) && downcast<WorkerGlobalScope>(*m_scriptExecutionContext).isClosing())
return;
std::unique_ptr<MessagePortArray> ports = MessagePort::entanglePorts(*m_scriptExecutionContext, WTFMove(channels));
Ref<Event> event = MessageEvent::create(WTFMove(ports), WTFMove(message));
dispatchEvent(event);
}
}
bool MessagePort::hasPendingActivity()
{
if (m_started && m_entangledChannel && m_entangledChannel->hasPendingActivity())
return true;
if (isEntangled() && !locallyEntangledPort())
return true;
return false;
}
MessagePort* MessagePort::locallyEntangledPort()
{
return m_entangledChannel ? m_entangledChannel->locallyEntangledPort(m_scriptExecutionContext) : nullptr;
}
std::unique_ptr<MessagePortChannelArray> MessagePort::disentanglePorts(const MessagePortArray* ports, ExceptionCode& ec)
{
if (!ports || !ports->size())
return nullptr;
HashSet<MessagePort*> portSet;
for (auto& port : *ports) {
if (!port || port->isNeutered() || portSet.contains(port.get())) {
ec = DATA_CLONE_ERR;
return nullptr;
}
portSet.add(port.get());
}
auto portArray = std::make_unique<MessagePortChannelArray>(ports->size());
for (unsigned int i = 0 ; i < ports->size() ; ++i) {
std::unique_ptr<MessagePortChannel> channel = (*ports)[i]->disentangle();
(*portArray)[i] = WTFMove(channel);
}
return portArray;
}
std::unique_ptr<MessagePortArray> MessagePort::entanglePorts(ScriptExecutionContext& context, std::unique_ptr<MessagePortChannelArray> channels)
{
if (!channels || !channels->size())
return nullptr;
auto portArray = std::make_unique<MessagePortArray>(channels->size());
for (unsigned int i = 0; i < channels->size(); ++i) {
auto port = MessagePort::create(context);
port->entangle(WTFMove((*channels)[i]));
(*portArray)[i] = WTFMove(port);
}
return portArray;
}
bool MessagePort::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
if (listener->isAttribute() && eventType == eventNames().messageEvent)
start();
return EventTargetWithInlineData::addEventListener(eventType, WTFMove(listener), options);
}
}