Index: webkit/api/src/WebWorkerClientImpl.cpp |
diff --git a/webkit/api/src/WebWorkerClientImpl.cpp b/webkit/api/src/WebWorkerClientImpl.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..16d7134a054647334ffe55bb8d90a8983a98b6f5 |
--- /dev/null |
+++ b/webkit/api/src/WebWorkerClientImpl.cpp |
@@ -0,0 +1,414 @@ |
+/* |
+ * 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 "DedicatedWorkerThread.h" |
+#include "ErrorEvent.h" |
+#include "Frame.h" |
+#include "FrameLoaderClient.h" |
+#include "GenericWorkerTask.h" |
+#include "MessageEvent.h" |
+#include "MessagePort.h" |
+#include "MessagePortChannel.h" |
+#include "ScriptExecutionContext.h" |
+#include "Worker.h" |
+#include "WorkerContext.h" |
+#include "WorkerContextExecutionProxy.h" |
+#include "WorkerMessagingProxy.h" |
+#include <wtf/Threading.h> |
+#undef LOG |
+ |
+#include "PlatformMessagePortChannel.h" |
+#include "WebFrameClient.h" |
+#include "WebKit.h" |
+#include "WebKitClient.h" |
+#include "WebMessagePortChannel.h" |
+#include "WebString.h" |
+#include "WebURL.h" |
+#include "WebWorker.h" |
+#include "WebWorkerImpl.h" |
+// FIXME: remove the includes below |
+#include "webkit/glue/webframeloaderclient_impl.h" |
+#include "webkit/glue/webframe_impl.h" |
+#include "webkit/glue/webview_impl.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 { |
+ WorkerContextExecutionProxy* currentContext = |
+ WorkerContextExecutionProxy::retrieve(); |
+ if (!currentContext) { |
+ ASSERT_NOT_REACHED(); |
+ return 0; |
+ } |
+ |
+ DedicatedWorkerThread* thread = |
+ static_cast<DedicatedWorkerThread*>(currentContext->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()) { |
+ WebWorkerImpl::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()) { |
+ WebWorkerImpl::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()) { |
+ WebWorkerImpl::dispatchTaskToMainThread(createCallbackTask(&postMessageToWorkerContextTask, |
+ this, |
+ message->toString(), |
+ 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->toString(), 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. |
+ WebWorkerImpl::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 handled = false; |
+ handled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage, |
+ sourceURL, |
+ lineNumber)); |
+ if (!handled) |
+ m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL); |
+} |
+ |
+void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int destinationId, |
+ int sourceId, |
+ int messageType, |
+ int messageLevel, |
+ const WebString& message, |
+ int lineNumber, |
+ const WebString& sourceURL) |
+{ |
+ if (currentThread() != m_workerThreadId) { |
+ m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageToWorkerObjectTask, |
+ this, |
+ destinationId, |
+ sourceId, |
+ messageType, |
+ messageLevel, |
+ String(message), |
+ lineNumber, |
+ String(sourceURL))); |
+ return; |
+ } |
+ |
+ m_scriptExecutionContext->addMessage(static_cast<MessageDestination>(destinationId), |
+ static_cast<MessageSource>(sourceId), |
+ static_cast<MessageType>(messageType), |
+ static_cast<MessageLevel>(messageLevel), |
+ String(message), lineNumber, |
+ String(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::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.release()); |
+ RefPtr<SerializedScriptValue> serializedMessage = |
+ SerializedScriptValue::create(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 destinationId, |
+ int sourceId, |
+ int messageType, |
+ int messageLevel, |
+ const String& message, |
+ int lineNumber, |
+ const String& sourceURL) |
+{ |
+ thisPtr->m_scriptExecutionContext->addMessage(static_cast<MessageDestination>(destinationId), |
+ 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 |