diff options
Diffstat (limited to 'Source/WebKit/chromium/src/WebWorkerClientImpl.cpp')
-rw-r--r-- | Source/WebKit/chromium/src/WebWorkerClientImpl.cpp | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/Source/WebKit/chromium/src/WebWorkerClientImpl.cpp b/Source/WebKit/chromium/src/WebWorkerClientImpl.cpp new file mode 100644 index 0000000..18282e3 --- /dev/null +++ b/Source/WebKit/chromium/src/WebWorkerClientImpl.cpp @@ -0,0 +1,420 @@ +/* + * 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 "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); +} + +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)); +} + +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); +} + +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); +} + +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 |