blob: f90e7e8ad0211f4a217ffa79aff091024f6b0c6b [file] [log] [blame]
/*
* 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
* OWNER 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 "WebWorkerClientImpl.h"
#if ENABLE(WORKERS)
#include "CrossThreadTask.h"
#include "DedicatedWorkerThread.h"
#include "ErrorEvent.h"
#include "Frame.h"
#include "FrameLoaderClient.h"
#include "MessageEvent.h"
#include "MessagePort.h"
#include "MessagePortChannel.h"
#include "ScriptCallStack.h"
#include "ScriptExecutionContext.h"
#include "Worker.h"
#include "WorkerContext.h"
#include "WorkerContextExecutionProxy.h"
#include "WorkerScriptController.h"
#include "WorkerMessagingProxy.h"
#include <wtf/Threading.h>
#include "FrameLoaderClientImpl.h"
#include "PlatformMessagePortChannel.h"
#include "WebFrameClient.h"
#include "WebFrameImpl.h"
#include "WebKit.h"
#include "WebKitClient.h"
#include "WebMessagePortChannel.h"
#include "WebString.h"
#include "WebURL.h"
#include "WebViewImpl.h"
#include "WebWorker.h"
#include "WebWorkerImpl.h"
using namespace WebCore;
namespace WebKit {
// When WebKit creates a WorkerContextProxy object, we check if we're in the
// renderer or worker process. If the latter, then we just use
// WorkerMessagingProxy.
//
// If we're in the renderer process, then we need use the glue provided
// WebWorker object to talk to the worker process over IPC. The worker process
// talks to Worker* using WorkerObjectProxy, which we implement on
// WebWorkerClientImpl.
//
// Note that if we're running each worker in a separate process, then nested
// workers end up using the same codepath as the renderer process.
// static
WorkerContextProxy* WebWorkerClientImpl::createWorkerContextProxy(Worker* worker)
{
// Special behavior for multiple workers per process.
// FIXME: v8 doesn't support more than one workers per process.
// if (!worker->scriptExecutionContext()->isDocument())
// return new WorkerMessagingProxy(worker);
WebWorker* webWorker = 0;
WebWorkerClientImpl* proxy = new WebWorkerClientImpl(worker);
if (worker->scriptExecutionContext()->isDocument()) {
Document* document = static_cast<Document*>(
worker->scriptExecutionContext());
WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame());
webWorker = webFrame->client()->createWorker(webFrame, proxy);
} else {
WorkerScriptController* controller = WorkerScriptController::controllerForContext();
if (!controller) {
ASSERT_NOT_REACHED();
return 0;
}
DedicatedWorkerThread* thread = static_cast<DedicatedWorkerThread*>(controller->workerContext()->thread());
WorkerObjectProxy* workerObjectProxy = &thread->workerObjectProxy();
WebWorkerImpl* impl = reinterpret_cast<WebWorkerImpl*>(workerObjectProxy);
webWorker = impl->client()->createWorker(proxy);
}
proxy->setWebWorker(webWorker);
return proxy;
}
WebWorkerClientImpl::WebWorkerClientImpl(Worker* worker)
: m_scriptExecutionContext(worker->scriptExecutionContext())
, m_worker(worker)
, m_askedToTerminate(false)
, m_unconfirmedMessageCount(0)
, m_workerContextHadPendingActivity(false)
, m_workerThreadId(currentThread())
{
}
WebWorkerClientImpl::~WebWorkerClientImpl()
{
}
void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker)
{
m_webWorker = webWorker;
}
void WebWorkerClientImpl::startWorkerContext(const KURL& scriptURL,
const String& userAgent,
const String& sourceCode)
{
// Worker.terminate() could be called from JS before the context is started.
if (m_askedToTerminate)
return;
if (!isMainThread()) {
WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(
&startWorkerContextTask,
this,
scriptURL.string(),
userAgent,
sourceCode));
return;
}
m_webWorker->startWorkerContext(scriptURL, userAgent, sourceCode);
}
void WebWorkerClientImpl::terminateWorkerContext()
{
if (m_askedToTerminate)
return;
m_askedToTerminate = true;
if (!isMainThread()) {
WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&terminateWorkerContextTask, this));
return;
}
m_webWorker->terminateWorkerContext();
}
void WebWorkerClientImpl::postMessageToWorkerContext(
PassRefPtr<SerializedScriptValue> message,
PassOwnPtr<MessagePortChannelArray> channels)
{
// Worker.terminate() could be called from JS before the context is started.
if (m_askedToTerminate)
return;
++m_unconfirmedMessageCount;
if (!isMainThread()) {
WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageToWorkerContextTask,
this,
message->toWireString(),
channels));
return;
}
WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
for (size_t i = 0; i < webChannels.size(); ++i) {
WebMessagePortChannel* webchannel =
(*channels)[i]->channel()->webChannelRelease();
webchannel->setClient(0);
webChannels[i] = webchannel;
}
m_webWorker->postMessageToWorkerContext(message->toWireString(), webChannels);
}
bool WebWorkerClientImpl::hasPendingActivity() const
{
return !m_askedToTerminate
&& (m_unconfirmedMessageCount || m_workerContextHadPendingActivity);
}
void WebWorkerClientImpl::workerObjectDestroyed()
{
if (isMainThread()) {
m_webWorker->workerObjectDestroyed();
m_worker = 0;
}
// Even if this is called on the main thread, there could be a queued task for
// this object, so don't delete it right away.
WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerObjectDestroyedTask,
this));
}
void WebWorkerClientImpl::postMessageToWorkerObject(const WebString& message,
const WebMessagePortChannelArray& channels)
{
OwnPtr<MessagePortChannelArray> channels2;
if (channels.size()) {
channels2 = new MessagePortChannelArray(channels.size());
for (size_t i = 0; i < channels.size(); ++i) {
RefPtr<PlatformMessagePortChannel> platform_channel =
PlatformMessagePortChannel::create(channels[i]);
channels[i]->setClient(platform_channel.get());
(*channels2)[i] = MessagePortChannel::create(platform_channel);
}
}
if (currentThread() != m_workerThreadId) {
m_scriptExecutionContext->postTask(createCallbackTask(&postMessageToWorkerObjectTask,
this,
String(message),
channels2.release()));
return;
}
postMessageToWorkerObjectTask(m_scriptExecutionContext.get(), this,
message, channels2.release());
}
void WebWorkerClientImpl::postExceptionToWorkerObject(const WebString& errorMessage,
int lineNumber,
const WebString& sourceURL)
{
if (currentThread() != m_workerThreadId) {
m_scriptExecutionContext->postTask(createCallbackTask(&postExceptionToWorkerObjectTask,
this,
String(errorMessage),
lineNumber,
String(sourceURL)));
return;
}
bool unhandled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
sourceURL,
lineNumber));
if (unhandled)
m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0);
}
void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int destination,
int sourceId,
int messageType,
int messageLevel,
const WebString& message,
int lineNumber,
const WebString& sourceURL)
{
if (currentThread() != m_workerThreadId) {
m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageToWorkerObjectTask,
this,
sourceId,
messageType,
messageLevel,
String(message),
lineNumber,
String(sourceURL)));
return;
}
m_scriptExecutionContext->addMessage(static_cast<MessageSource>(sourceId),
static_cast<MessageType>(messageType),
static_cast<MessageLevel>(messageLevel),
String(message), lineNumber,
String(sourceURL), 0);
}
void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int sourceId,
int messageType,
int messageLevel,
const WebString& message,
int lineNumber,
const WebString& sourceURL)
{
postConsoleMessageToWorkerObject(0, sourceId, messageType, messageLevel, message, lineNumber, sourceURL);
}
void WebWorkerClientImpl::confirmMessageFromWorkerObject(bool hasPendingActivity)
{
// unconfirmed_message_count_ can only be updated on the thread where it's
// accessed. Otherwise there are race conditions with v8's garbage
// collection.
m_scriptExecutionContext->postTask(createCallbackTask(&confirmMessageFromWorkerObjectTask,
this));
}
void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity)
{
// See above comment in confirmMessageFromWorkerObject.
m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask,
this,
hasPendingActivity));
}
void WebWorkerClientImpl::workerContextDestroyed()
{
}
void WebWorkerClientImpl::workerContextClosed()
{
}
void WebWorkerClientImpl::startWorkerContextTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr,
const String& scriptURL,
const String& userAgent,
const String& sourceCode)
{
thisPtr->m_webWorker->startWorkerContext(KURL(ParsedURLString, scriptURL),
userAgent, sourceCode);
}
void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr)
{
thisPtr->m_webWorker->terminateWorkerContext();
}
void WebWorkerClientImpl::postMessageToWorkerContextTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr,
const String& message,
PassOwnPtr<MessagePortChannelArray> channels)
{
WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
for (size_t i = 0; i < webChannels.size(); ++i) {
webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
webChannels[i]->setClient(0);
}
thisPtr->m_webWorker->postMessageToWorkerContext(message, webChannels);
}
void WebWorkerClientImpl::workerObjectDestroyedTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr)
{
if (thisPtr->m_worker) // Check we haven't alread called this.
thisPtr->m_webWorker->workerObjectDestroyed();
delete thisPtr;
}
void WebWorkerClientImpl::postMessageToWorkerObjectTask(
ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr,
const String& message,
PassOwnPtr<MessagePortChannelArray> channels)
{
if (thisPtr->m_worker) {
OwnPtr<MessagePortArray> ports =
MessagePort::entanglePorts(*context, channels);
RefPtr<SerializedScriptValue> serializedMessage =
SerializedScriptValue::createFromWire(message);
thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(),
serializedMessage.release()));
}
}
void WebWorkerClientImpl::postExceptionToWorkerObjectTask(
ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr,
const String& errorMessage,
int lineNumber,
const String& sourceURL)
{
bool handled = false;
if (thisPtr->m_worker)
handled = thisPtr->m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
sourceURL,
lineNumber));
if (!handled)
thisPtr->m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0);
}
void WebWorkerClientImpl::postConsoleMessageToWorkerObjectTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr,
int sourceId,
int messageType,
int messageLevel,
const String& message,
int lineNumber,
const String& sourceURL)
{
thisPtr->m_scriptExecutionContext->addMessage(static_cast<MessageSource>(sourceId),
static_cast<MessageType>(messageType),
static_cast<MessageLevel>(messageLevel),
message, lineNumber, sourceURL, 0);
}
void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr)
{
thisPtr->m_unconfirmedMessageCount--;
}
void WebWorkerClientImpl::reportPendingActivityTask(ScriptExecutionContext* context,
WebWorkerClientImpl* thisPtr,
bool hasPendingActivity)
{
thisPtr->m_workerContextHadPendingActivity = hasPendingActivity;
}
} // namespace WebKit
#endif