2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Google Inc. All Rights Reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "WorkerMessagingProxy.h"
34 #include "CrossThreadTask.h"
35 #include "DedicatedWorkerContext.h"
36 #include "DedicatedWorkerThread.h"
37 #include "DOMWindow.h"
39 #include "ErrorEvent.h"
40 #include "ExceptionCode.h"
41 #include "InspectorInstrumentation.h"
42 #include "MessageEvent.h"
43 #include "NotImplemented.h"
44 #include "ScriptCallStack.h"
45 #include "ScriptExecutionContext.h"
47 #include "WorkerDebuggerAgent.h"
48 #include "WorkerInspectorController.h"
49 #include <wtf/MainThread.h>
53 class MessageWorkerContextTask : public ScriptExecutionContext::Task {
55 static PassOwnPtr<MessageWorkerContextTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
57 return adoptPtr(new MessageWorkerContextTask(message, channels));
61 MessageWorkerContextTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
63 , m_channels(channels)
67 virtual void performTask(ScriptExecutionContext* scriptContext)
69 ASSERT(scriptContext->isWorkerContext());
70 DedicatedWorkerContext* context = static_cast<DedicatedWorkerContext*>(scriptContext);
71 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release());
72 context->dispatchEvent(MessageEvent::create(ports.release(), m_message));
73 context->thread()->workerObjectProxy().confirmMessageFromWorkerObject(context->hasPendingActivity());
77 RefPtr<SerializedScriptValue> m_message;
78 OwnPtr<MessagePortChannelArray> m_channels;
81 class MessageWorkerTask : public ScriptExecutionContext::Task {
83 static PassOwnPtr<MessageWorkerTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels, WorkerMessagingProxy* messagingProxy)
85 return adoptPtr(new MessageWorkerTask(message, channels, messagingProxy));
89 MessageWorkerTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels, WorkerMessagingProxy* messagingProxy)
91 , m_channels(channels)
92 , m_messagingProxy(messagingProxy)
96 virtual void performTask(ScriptExecutionContext* scriptContext)
98 Worker* workerObject = m_messagingProxy->workerObject();
99 if (!workerObject || m_messagingProxy->askedToTerminate())
102 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release());
103 workerObject->dispatchEvent(MessageEvent::create(ports.release(), m_message));
107 RefPtr<SerializedScriptValue> m_message;
108 OwnPtr<MessagePortChannelArray> m_channels;
109 WorkerMessagingProxy* m_messagingProxy;
112 class WorkerExceptionTask : public ScriptExecutionContext::Task {
114 static PassOwnPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
116 return adoptPtr(new WorkerExceptionTask(errorMessage, lineNumber, sourceURL, messagingProxy));
120 WorkerExceptionTask(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
121 : m_errorMessage(errorMessage.isolatedCopy())
122 , m_lineNumber(lineNumber)
123 , m_sourceURL(sourceURL.isolatedCopy())
124 , m_messagingProxy(messagingProxy)
128 virtual void performTask(ScriptExecutionContext* context)
130 Worker* workerObject = m_messagingProxy->workerObject();
134 // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated.
135 // 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.
137 bool errorHandled = !workerObject->dispatchEvent(ErrorEvent::create(m_errorMessage, m_sourceURL, m_lineNumber));
139 context->reportException(m_errorMessage, m_lineNumber, m_sourceURL, 0);
142 String m_errorMessage;
145 WorkerMessagingProxy* m_messagingProxy;
148 class WorkerContextDestroyedTask : public ScriptExecutionContext::Task {
150 static PassOwnPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy)
152 return adoptPtr(new WorkerContextDestroyedTask(messagingProxy));
156 WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy)
157 : m_messagingProxy(messagingProxy)
161 virtual void performTask(ScriptExecutionContext*)
163 m_messagingProxy->workerContextDestroyedInternal();
166 WorkerMessagingProxy* m_messagingProxy;
169 class WorkerTerminateTask : public ScriptExecutionContext::Task {
171 static PassOwnPtr<WorkerTerminateTask> create(WorkerMessagingProxy* messagingProxy)
173 return adoptPtr(new WorkerTerminateTask(messagingProxy));
177 WorkerTerminateTask(WorkerMessagingProxy* messagingProxy)
178 : m_messagingProxy(messagingProxy)
182 virtual void performTask(ScriptExecutionContext*)
184 m_messagingProxy->terminateWorkerContext();
187 WorkerMessagingProxy* m_messagingProxy;
190 class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task {
192 static PassOwnPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
194 return adoptPtr(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity));
198 WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
199 : m_messagingProxy(messagingProxy)
200 , m_confirmingMessage(confirmingMessage)
201 , m_hasPendingActivity(hasPendingActivity)
205 virtual void performTask(ScriptExecutionContext*)
207 m_messagingProxy->reportPendingActivityInternal(m_confirmingMessage, m_hasPendingActivity);
210 WorkerMessagingProxy* m_messagingProxy;
211 bool m_confirmingMessage;
212 bool m_hasPendingActivity;
215 class PostMessageToPageInspectorTask : public ScriptExecutionContext::Task {
217 static PassOwnPtr<PostMessageToPageInspectorTask> create(WorkerMessagingProxy* messagingProxy, const String& message)
219 return adoptPtr(new PostMessageToPageInspectorTask(messagingProxy, message));
223 PostMessageToPageInspectorTask(WorkerMessagingProxy* messagingProxy, const String& message)
224 : m_messagingProxy(messagingProxy)
225 , m_message(message.isolatedCopy())
229 virtual void performTask(ScriptExecutionContext*)
231 #if ENABLE(INSPECTOR)
232 if (WorkerContextProxy::PageInspector* pageInspector = m_messagingProxy->m_pageInspector)
233 pageInspector->dispatchMessageFromWorker(m_message);
237 WorkerMessagingProxy* m_messagingProxy;
242 #if !PLATFORM(CHROMIUM)
243 WorkerContextProxy* WorkerContextProxy::create(Worker* worker)
245 return new WorkerMessagingProxy(worker);
249 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject)
250 : m_scriptExecutionContext(workerObject->scriptExecutionContext())
251 , m_workerObject(workerObject)
252 , m_unconfirmedMessageCount(0)
253 , m_workerThreadHadPendingActivity(false)
254 , m_askedToTerminate(false)
255 #if ENABLE(INSPECTOR)
259 ASSERT(m_workerObject);
260 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
261 || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
264 WorkerMessagingProxy::~WorkerMessagingProxy()
266 ASSERT(!m_workerObject);
267 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
268 || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
271 void WorkerMessagingProxy::startWorkerContext(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode)
273 RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, userAgent, sourceCode, *this, *this, startMode);
274 workerThreadCreated(thread);
278 void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
280 m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, channels, this));
283 void WorkerMessagingProxy::postMessageToWorkerContext(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
285 if (m_askedToTerminate)
288 if (m_workerThread) {
289 ++m_unconfirmedMessageCount;
290 m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message, channels));
292 m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message, channels));
295 void WorkerMessagingProxy::postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
297 if (m_askedToTerminate)
300 ASSERT(m_workerThread);
301 m_workerThread->runLoop().postTaskForMode(task, mode);
304 void WorkerMessagingProxy::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
306 // FIXME: In case of nested workers, this should go directly to the root Document context.
307 ASSERT(m_scriptExecutionContext->isDocument());
308 m_scriptExecutionContext->postTask(task);
311 void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, const String& sourceURL)
313 m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, sourceURL, this));
316 static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
318 if (messagingProxy->askedToTerminate())
320 context->addMessage(source, type, level, message, lineNumber, sourceURL, 0);
323 void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageType type, MessageLevel level, const String& message, int lineNumber, const String& sourceURL)
325 m_scriptExecutionContext->postTask(
326 createCallbackTask(&postConsoleMessageTask, AllowCrossThreadAccess(this),
327 source, type, level, message, lineNumber, sourceURL));
330 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread)
332 m_workerThread = workerThread;
334 if (m_askedToTerminate) {
335 // Worker.terminate() could be called from JS before the thread was created.
336 m_workerThread->stop();
338 unsigned taskCount = m_queuedEarlyTasks.size();
339 ASSERT(!m_unconfirmedMessageCount);
340 m_unconfirmedMessageCount = taskCount;
341 m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
343 for (unsigned i = 0; i < taskCount; ++i)
344 m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i].release());
345 m_queuedEarlyTasks.clear();
349 void WorkerMessagingProxy::workerObjectDestroyed()
353 terminateWorkerContext();
355 workerContextDestroyedInternal();
358 #if ENABLE(INSPECTOR)
359 #if ENABLE(JAVASCRIPT_DEBUGGER)
360 static void connectToWorkerContextInspectorTask(ScriptExecutionContext* context, bool)
362 ASSERT(context->isWorkerContext());
363 static_cast<WorkerContext*>(context)->workerInspectorController()->connectFrontend();
367 void WorkerMessagingProxy::connectToInspector(WorkerContextProxy::PageInspector* pageInspector)
369 if (m_askedToTerminate)
371 ASSERT(!m_pageInspector);
372 m_pageInspector = pageInspector;
373 #if ENABLE(JAVASCRIPT_DEBUGGER)
374 m_workerThread->runLoop().postTaskForMode(createCallbackTask(connectToWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
378 #if ENABLE(JAVASCRIPT_DEBUGGER)
379 static void disconnectFromWorkerContextInspectorTask(ScriptExecutionContext* context, bool)
381 ASSERT(context->isWorkerContext());
382 static_cast<WorkerContext*>(context)->workerInspectorController()->disconnectFrontend();
386 void WorkerMessagingProxy::disconnectFromInspector()
389 if (m_askedToTerminate)
391 #if ENABLE(JAVASCRIPT_DEBUGGER)
392 m_workerThread->runLoop().postTaskForMode(createCallbackTask(disconnectFromWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
396 #if ENABLE(JAVASCRIPT_DEBUGGER)
397 static void dispatchOnInspectorBackendTask(ScriptExecutionContext* context, const String& message)
399 ASSERT(context->isWorkerContext());
400 static_cast<WorkerContext*>(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
404 void WorkerMessagingProxy::sendMessageToInspector(const String& message)
406 if (m_askedToTerminate)
408 #if ENABLE(JAVASCRIPT_DEBUGGER)
409 m_workerThread->runLoop().postTaskForMode(createCallbackTask(dispatchOnInspectorBackendTask, String(message)), WorkerDebuggerAgent::debuggerTaskMode);
414 void WorkerMessagingProxy::workerContextDestroyed()
416 m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this));
417 // Will execute workerContextDestroyedInternal() on context's thread.
420 void WorkerMessagingProxy::workerContextClosed()
422 // Executes terminateWorkerContext() on parent context's thread.
423 m_scriptExecutionContext->postTask(WorkerTerminateTask::create(this));
426 void WorkerMessagingProxy::workerContextDestroyedInternal()
428 // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication
429 // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
430 m_askedToTerminate = true;
433 InspectorInstrumentation::workerContextTerminated(m_scriptExecutionContext.get(), this);
439 void WorkerMessagingProxy::terminateWorkerContext()
441 if (m_askedToTerminate)
443 m_askedToTerminate = true;
446 m_workerThread->stop();
448 InspectorInstrumentation::workerContextTerminated(m_scriptExecutionContext.get(), this);
451 #if ENABLE(INSPECTOR)
452 void WorkerMessagingProxy::postMessageToPageInspector(const String& message)
454 m_scriptExecutionContext->postTask(PostMessageToPageInspectorTask::create(this, message));
457 void WorkerMessagingProxy::updateInspectorStateCookie(const String&)
463 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
465 m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
466 // Will execute reportPendingActivityInternal() on context's thread.
469 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
471 m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
472 // Will execute reportPendingActivityInternal() on context's thread.
475 void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
477 if (confirmingMessage && !m_askedToTerminate) {
478 ASSERT(m_unconfirmedMessageCount);
479 --m_unconfirmedMessageCount;
482 m_workerThreadHadPendingActivity = hasPendingActivity;
485 bool WorkerMessagingProxy::hasPendingActivity() const
487 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
490 } // namespace WebCore
492 #endif // ENABLE(WORKERS)