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 "DOMWindow.h"
36 #include "GenericWorkerTask.h"
37 #include "MessageEvent.h"
38 #include "ScriptExecutionContext.h"
40 #include "WorkerContext.h"
41 #include "WorkerThread.h"
45 class MessageWorkerContextTask : public ScriptExecutionContext::Task {
47 static PassRefPtr<MessageWorkerContextTask> create(const String& message)
49 return adoptRef(new MessageWorkerContextTask(message));
53 MessageWorkerContextTask(const String& message)
54 : m_message(message.copy())
58 virtual void performTask(ScriptExecutionContext* scriptContext)
60 ASSERT(scriptContext->isWorkerContext());
61 WorkerContext* context = static_cast<WorkerContext*>(scriptContext);
63 context->dispatchMessage(m_message);
65 static_cast<WorkerMessagingProxy*>(context->thread()->workerObjectProxy())->confirmWorkerThreadMessage(context->hasPendingActivity());
72 class MessageWorkerTask : public ScriptExecutionContext::Task {
74 static PassRefPtr<MessageWorkerTask> create(const String& message, WorkerMessagingProxy* messagingProxy)
76 return adoptRef(new MessageWorkerTask(message, messagingProxy));
80 MessageWorkerTask(const String& message, WorkerMessagingProxy* messagingProxy)
81 : m_message(message.copy())
82 , m_messagingProxy(messagingProxy)
86 virtual void performTask(ScriptExecutionContext*)
88 Worker* workerObject = m_messagingProxy->workerObject();
89 if (!workerObject || m_messagingProxy->askedToTerminate())
92 workerObject->dispatchMessage(m_message);
97 WorkerMessagingProxy* m_messagingProxy;
100 class WorkerExceptionTask : public ScriptExecutionContext::Task {
102 static PassRefPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
104 return adoptRef(new WorkerExceptionTask(errorMessage, lineNumber, sourceURL, messagingProxy));
108 WorkerExceptionTask(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
109 : m_errorMessage(errorMessage.copy())
110 , m_lineNumber(lineNumber)
111 , m_sourceURL(sourceURL.copy())
112 , m_messagingProxy(messagingProxy)
116 virtual void performTask(ScriptExecutionContext* context)
118 if (!m_messagingProxy->askedToTerminate())
119 context->reportException(m_errorMessage, m_lineNumber, m_sourceURL);
122 String m_errorMessage;
125 WorkerMessagingProxy* m_messagingProxy;
128 class WorkerContextDestroyedTask : public ScriptExecutionContext::Task {
130 static PassRefPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy)
132 return adoptRef(new WorkerContextDestroyedTask(messagingProxy));
136 WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy)
137 : m_messagingProxy(messagingProxy)
141 virtual void performTask(ScriptExecutionContext*)
143 m_messagingProxy->workerContextDestroyedInternal();
146 WorkerMessagingProxy* m_messagingProxy;
149 class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task {
151 static PassRefPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
153 return adoptRef(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity));
157 WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
158 : m_messagingProxy(messagingProxy)
159 , m_confirmingMessage(confirmingMessage)
160 , m_hasPendingActivity(hasPendingActivity)
164 virtual void performTask(ScriptExecutionContext*)
166 m_messagingProxy->reportPendingActivityInternal(m_confirmingMessage, m_hasPendingActivity);
169 WorkerMessagingProxy* m_messagingProxy;
170 bool m_confirmingMessage;
171 bool m_hasPendingActivity;
175 WorkerContextProxy* WorkerContextProxy::create(Worker* worker)
177 return new WorkerMessagingProxy(worker);
180 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject)
181 : m_scriptExecutionContext(workerObject->scriptExecutionContext())
182 , m_workerObject(workerObject)
183 , m_unconfirmedMessageCount(0)
184 , m_workerThreadHadPendingActivity(false)
185 , m_askedToTerminate(false)
187 ASSERT(m_workerObject);
188 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
189 || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
192 WorkerMessagingProxy::~WorkerMessagingProxy()
194 ASSERT(!m_workerObject);
195 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
196 || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
199 void WorkerMessagingProxy::startWorkerContext(const KURL& scriptURL, const String& userAgent, const String& sourceCode)
201 RefPtr<WorkerThread> thread = WorkerThread::create(scriptURL, userAgent, sourceCode, this);
202 workerThreadCreated(thread);
206 void WorkerMessagingProxy::postMessageToWorkerObject(const String& message)
208 m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, this));
211 void WorkerMessagingProxy::postMessageToWorkerContext(const String& message)
213 if (m_askedToTerminate)
216 if (m_workerThread) {
217 ++m_unconfirmedMessageCount;
218 m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message));
220 m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message));
223 void WorkerMessagingProxy::postTaskToWorkerContext(PassRefPtr<ScriptExecutionContext::Task> task)
225 postTaskForModeToWorkerContext(task, WorkerRunLoop::defaultMode());
228 void WorkerMessagingProxy::postTaskForModeToWorkerContext(PassRefPtr<ScriptExecutionContext::Task> task, const String& mode)
230 if (m_askedToTerminate)
233 ASSERT(m_workerThread);
234 m_workerThread->runLoop().postTaskForMode(task, mode);
237 void WorkerMessagingProxy::postTaskToWorkerObject(PassRefPtr<ScriptExecutionContext::Task> task)
239 m_scriptExecutionContext->postTask(task);
242 void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, const String& sourceURL)
244 m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, sourceURL, this));
247 static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageDestination destination, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
249 if (messagingProxy->askedToTerminate())
251 context->addMessage(destination, source, level, message, lineNumber, sourceURL);
254 void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageDestination destination, MessageSource source, MessageLevel level, const String& message, int lineNumber, const String& sourceURL)
256 m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageTask, this, destination, source, level, message, lineNumber, sourceURL));
259 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<WorkerThread> workerThread)
261 m_workerThread = workerThread;
263 if (m_askedToTerminate) {
264 // Worker.terminate() could be called from JS before the thread was created.
265 m_workerThread->stop();
267 unsigned taskCount = m_queuedEarlyTasks.size();
268 ASSERT(!m_unconfirmedMessageCount);
269 m_unconfirmedMessageCount = taskCount;
270 m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
272 for (unsigned i = 0; i < taskCount; ++i)
273 m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i]);
274 m_queuedEarlyTasks.clear();
278 void WorkerMessagingProxy::workerObjectDestroyed()
282 terminateWorkerContext();
284 workerContextDestroyedInternal();
287 void WorkerMessagingProxy::workerContextDestroyed()
289 m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this));
290 // Will execute workerContextDestroyedInternal() on context's thread.
293 void WorkerMessagingProxy::workerContextDestroyedInternal()
295 // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication
296 // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
302 void WorkerMessagingProxy::terminateWorkerContext()
304 if (m_askedToTerminate)
306 m_askedToTerminate = true;
309 m_workerThread->stop();
312 void WorkerMessagingProxy::confirmWorkerThreadMessage(bool hasPendingActivity)
314 m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
315 // Will execute reportPendingActivityInternal() on context's thread.
318 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
320 m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
321 // Will execute reportPendingActivityInternal() on context's thread.
324 void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
326 if (confirmingMessage && !m_askedToTerminate) {
327 ASSERT(m_unconfirmedMessageCount);
328 --m_unconfirmedMessageCount;
331 m_workerThreadHadPendingActivity = hasPendingActivity;
334 bool WorkerMessagingProxy::hasPendingActivity() const
336 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
339 } // namespace WebCore
341 #endif // ENABLE(WORKERS)