blob: e695e1eeaea214464cef45a7f39bd72e00db9f0a [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
* Copyright (C) 2009 Google 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 COMPUTER, 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 COMPUTER, 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 "core/workers/InProcessWorkerMessagingProxy.h"
#include "core/dom/ExecutionContextTask.h"
#include "core/dom/SecurityContext.h"
#include "core/events/ErrorEvent.h"
#include "core/events/MessageEvent.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/loader/DocumentLoadTiming.h"
#include "core/loader/DocumentLoader.h"
#include "core/origin_trials/OriginTrialContext.h"
#include "core/workers/InProcessWorkerBase.h"
#include "core/workers/InProcessWorkerObjectProxy.h"
#include "core/workers/WorkerClients.h"
#include "core/workers/WorkerGlobalScope.h"
#include "core/workers/WorkerInspectorProxy.h"
#include "core/workers/WorkerThreadStartupData.h"
#include "public/platform/WebTaskRunner.h"
#include "wtf/WTF.h"
#include <memory>
namespace blink {
namespace {
void processUnhandledExceptionOnWorkerGlobalScope(int exceptionId, ExecutionContext* scriptContext)
{
WorkerGlobalScope* globalScope = toWorkerGlobalScope(scriptContext);
globalScope->exceptionUnhandled(exceptionId);
}
void processMessageOnWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels, InProcessWorkerObjectProxy* workerObjectProxy, ExecutionContext* scriptContext)
{
WorkerGlobalScope* globalScope = toWorkerGlobalScope(scriptContext);
MessagePortArray* ports = MessagePort::entanglePorts(*scriptContext, std::move(channels));
globalScope->dispatchEvent(MessageEvent::create(ports, std::move(message)));
workerObjectProxy->confirmMessageFromWorkerObject();
workerObjectProxy->startPendingActivityTimer();
}
} // namespace
InProcessWorkerMessagingProxy::InProcessWorkerMessagingProxy(InProcessWorkerBase* workerObject, WorkerClients* workerClients)
: InProcessWorkerMessagingProxy(workerObject->getExecutionContext(), workerObject, workerClients)
{
DCHECK(m_workerObject);
}
InProcessWorkerMessagingProxy::InProcessWorkerMessagingProxy(ExecutionContext* executionContext, InProcessWorkerBase* workerObject, WorkerClients* workerClients)
: ThreadedMessagingProxyBase(executionContext)
, m_workerObjectProxy(InProcessWorkerObjectProxy::create(this))
, m_workerObject(workerObject)
, m_workerClients(workerClients)
, m_unconfirmedMessageCount(0)
, m_workerGlobalScopeMayHavePendingActivity(false)
{
}
InProcessWorkerMessagingProxy::~InProcessWorkerMessagingProxy()
{
DCHECK(!m_workerObject);
}
void InProcessWorkerMessagingProxy::startWorkerGlobalScope(const KURL& scriptURL, const String& userAgent, const String& sourceCode)
{
DCHECK(isParentContextThread());
if (askedToTerminate()) {
// Worker.terminate() could be called from JS before the thread was
// created.
return;
}
Document* document = toDocument(getExecutionContext());
SecurityOrigin* starterOrigin = document->getSecurityOrigin();
ContentSecurityPolicy* csp = m_workerObject->contentSecurityPolicy() ? m_workerObject->contentSecurityPolicy() : document->contentSecurityPolicy();
DCHECK(csp);
WorkerThreadStartMode startMode = workerInspectorProxy()->workerStartMode(document);
std::unique_ptr<WorkerSettings> workerSettings = wrapUnique(new WorkerSettings(document->settings()));
std::unique_ptr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(scriptURL, userAgent, sourceCode, nullptr, startMode, csp->headers().get(), m_workerObject->referrerPolicy(), starterOrigin, m_workerClients.release(), document->addressSpace(), OriginTrialContext::getTokens(document).get(), std::move(workerSettings));
initializeWorkerThread(std::move(startupData));
workerInspectorProxy()->workerThreadCreated(document, workerThread(), scriptURL);
}
void InProcessWorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels)
{
DCHECK(isParentContextThread());
if (!m_workerObject || askedToTerminate())
return;
MessagePortArray* ports = MessagePort::entanglePorts(*getExecutionContext(), std::move(channels));
m_workerObject->dispatchEvent(MessageEvent::create(ports, std::move(message)));
}
void InProcessWorkerMessagingProxy::postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels)
{
DCHECK(isParentContextThread());
if (askedToTerminate())
return;
std::unique_ptr<ExecutionContextTask> task = createCrossThreadTask(&processMessageOnWorkerGlobalScope, message, passed(std::move(channels)), crossThreadUnretained(&workerObjectProxy()));
if (workerThread()) {
// A message event is an activity and may initiate another activity.
m_workerGlobalScopeMayHavePendingActivity = true;
++m_unconfirmedMessageCount;
workerThread()->postTask(BLINK_FROM_HERE, std::move(task));
} else {
m_queuedEarlyTasks.append(std::move(task));
}
}
void InProcessWorkerMessagingProxy::dispatchErrorEvent(const String& errorMessage, std::unique_ptr<SourceLocation> location, int exceptionId)
{
DCHECK(isParentContextThread());
if (!m_workerObject)
return;
// We don't bother checking the askedToTerminate() flag here, because
// exceptions should *always* be reported even if the thread is terminated.
// This is intentionally different than the behavior in MessageWorkerTask,
// because terminated workers no longer deliver messages (section 4.6 of the
// WebWorker spec), but they do report exceptions.
ErrorEvent* event = ErrorEvent::create(errorMessage, location->clone(), nullptr);
if (m_workerObject->dispatchEvent(event) == DispatchEventResult::NotCanceled)
postTaskToWorkerGlobalScope(BLINK_FROM_HERE, createCrossThreadTask(&processUnhandledExceptionOnWorkerGlobalScope, exceptionId));
}
void InProcessWorkerMessagingProxy::workerThreadCreated()
{
ThreadedMessagingProxyBase::workerThreadCreated();
DCHECK(isParentContextThread());
DCHECK(!m_unconfirmedMessageCount);
m_unconfirmedMessageCount = m_queuedEarlyTasks.size();
// Worker initialization means a pending activity.
m_workerGlobalScopeMayHavePendingActivity = true;
for (auto& earlyTask : m_queuedEarlyTasks)
workerThread()->postTask(BLINK_FROM_HERE, std::move(earlyTask));
m_queuedEarlyTasks.clear();
}
void InProcessWorkerMessagingProxy::parentObjectDestroyed()
{
DCHECK(isParentContextThread());
// parentObjectDestroyed() is called in InProcessWorkerBase's destructor.
// Thus it should be guaranteed that a weak pointer m_workerObject has been
// cleared before this method gets called.
DCHECK(!m_workerObject);
ThreadedMessagingProxyBase::parentObjectDestroyed();
}
void InProcessWorkerMessagingProxy::confirmMessageFromWorkerObject()
{
DCHECK(isParentContextThread());
if (askedToTerminate())
return;
DCHECK(m_unconfirmedMessageCount);
--m_unconfirmedMessageCount;
}
void InProcessWorkerMessagingProxy::pendingActivityFinished()
{
DCHECK(isParentContextThread());
DCHECK(m_workerGlobalScopeMayHavePendingActivity);
if (m_unconfirmedMessageCount > 0) {
// Ignore the report because an inflight message event may initiate a
// new activity.
return;
}
m_workerGlobalScopeMayHavePendingActivity = false;
}
bool InProcessWorkerMessagingProxy::hasPendingActivity() const
{
DCHECK(isParentContextThread());
if (askedToTerminate())
return false;
return m_unconfirmedMessageCount || m_workerGlobalScopeMayHavePendingActivity;
}
} // namespace blink