2009-02-02 David Levin <levin@chromium.org>
[WebKit-https.git] / WebCore / dom / WorkerMessagingProxy.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  *
25  */
26
27 #include "config.h"
28
29 #if ENABLE(WORKERS)
30
31 #include "WorkerMessagingProxy.h"
32
33 #include "DOMWindow.h"
34 #include "Document.h"
35 #include "MessageEvent.h"
36 #include "ScriptExecutionContext.h"
37 #include "Worker.h"
38 #include "WorkerContext.h"
39 #include "WorkerThread.h"
40
41 namespace WebCore {
42
43 class MessageWorkerContextTask : public ScriptExecutionContext::Task {
44 public:
45     static PassRefPtr<MessageWorkerContextTask> create(const String& message)
46     {
47         return adoptRef(new MessageWorkerContextTask(message));
48     }
49
50 private:
51     MessageWorkerContextTask(const String& message)
52         : m_message(message.copy())
53     {
54     }
55
56     virtual void performTask(ScriptExecutionContext* scriptContext)
57     {
58         ASSERT(scriptContext->isWorkerContext());
59         WorkerContext* context = static_cast<WorkerContext*>(scriptContext);
60
61         RefPtr<Event> evt = MessageEvent::create(m_message, "", "", 0, 0);
62
63         if (context->onmessage()) {
64             evt->setTarget(context);
65             evt->setCurrentTarget(context);
66             context->onmessage()->handleEvent(evt.get(), false);
67         }
68
69         ExceptionCode ec = 0;
70         context->dispatchEvent(evt.release(), ec);
71         ASSERT(!ec);
72
73         context->thread()->messagingProxy()->confirmWorkerThreadMessage(context->hasPendingActivity());
74     }
75
76 private:
77     String m_message;
78 };
79
80 class MessageWorkerTask : public ScriptExecutionContext::Task {
81 public:
82     static PassRefPtr<MessageWorkerTask> create(const String& message, WorkerMessagingProxy* messagingProxy)
83     {
84         return adoptRef(new MessageWorkerTask(message, messagingProxy));
85     }
86
87 private:
88     MessageWorkerTask(const String& message, WorkerMessagingProxy* messagingProxy)
89         : m_message(message.copy())
90         , m_messagingProxy(messagingProxy)
91     {
92     }
93
94     virtual void performTask(ScriptExecutionContext*)
95     {
96         Worker* workerObject = m_messagingProxy->workerObject();
97         if (!workerObject || m_messagingProxy->askedToTerminate())
98             return;
99
100         RefPtr<Event> evt = MessageEvent::create(m_message, "", "", 0, 0);
101
102         if (workerObject->onmessage()) {
103             evt->setTarget(workerObject);
104             evt->setCurrentTarget(workerObject);
105             workerObject->onmessage()->handleEvent(evt.get(), false);
106         }
107
108         ExceptionCode ec = 0;
109         workerObject->dispatchEvent(evt.release(), ec);
110         ASSERT(!ec);
111     }
112
113 private:
114     String m_message;
115     WorkerMessagingProxy* m_messagingProxy;
116 };
117
118 class WorkerExceptionTask : public ScriptExecutionContext::Task {
119 public:
120     static PassRefPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
121     {
122         return adoptRef(new WorkerExceptionTask(errorMessage, lineNumber, sourceURL, messagingProxy));
123     }
124
125 private:
126     WorkerExceptionTask(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
127         : m_errorMessage(errorMessage.copy())
128         , m_lineNumber(lineNumber)
129         , m_sourceURL(sourceURL.copy())
130         , m_messagingProxy(messagingProxy)
131     {
132     }
133
134     virtual void performTask(ScriptExecutionContext* context)
135     {
136         if (!m_messagingProxy->askedToTerminate())
137             context->reportException(m_errorMessage, m_lineNumber, m_sourceURL);
138     }
139
140     String m_errorMessage;
141     int m_lineNumber;
142     String m_sourceURL;
143     WorkerMessagingProxy* m_messagingProxy;
144 };
145
146 class WorkerContextDestroyedTask : public ScriptExecutionContext::Task {
147 public:
148     static PassRefPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy)
149     {
150         return adoptRef(new WorkerContextDestroyedTask(messagingProxy));
151     }
152
153 private:
154     WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy)
155         : m_messagingProxy(messagingProxy)
156     {
157     }
158
159     virtual void performTask(ScriptExecutionContext*)
160     {
161         m_messagingProxy->workerContextDestroyedInternal();
162     }
163
164     WorkerMessagingProxy* m_messagingProxy;
165 };
166
167 class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task {
168 public:
169     static PassRefPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
170     {
171         return adoptRef(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity));
172     }
173
174 private:
175     WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
176         : m_messagingProxy(messagingProxy)
177         , m_confirmingMessage(confirmingMessage)
178         , m_hasPendingActivity(hasPendingActivity)
179     {
180     }
181
182     virtual void performTask(ScriptExecutionContext*)
183     {
184         m_messagingProxy->reportWorkerThreadActivityInternal(m_confirmingMessage, m_hasPendingActivity);
185     }
186
187     WorkerMessagingProxy* m_messagingProxy;
188     bool m_confirmingMessage;
189     bool m_hasPendingActivity;
190 };
191
192
193 WorkerMessagingProxy::WorkerMessagingProxy(PassRefPtr<ScriptExecutionContext> scriptExecutionContext, Worker* workerObject)
194     : m_scriptExecutionContext(scriptExecutionContext)
195     , m_workerObject(workerObject)
196     , m_unconfirmedMessageCount(0)
197     , m_workerThreadHadPendingActivity(false)
198     , m_askedToTerminate(false)
199 {
200     ASSERT(m_workerObject);
201     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
202         || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
203 }
204
205 WorkerMessagingProxy::~WorkerMessagingProxy()
206 {
207     ASSERT(!m_workerObject);
208     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
209         || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
210 }
211
212 void WorkerMessagingProxy::postMessageToWorkerObject(const String& message)
213 {
214     m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, this));
215 }
216
217 void WorkerMessagingProxy::postMessageToWorkerContext(const String& message)
218 {
219     postTaskToWorkerContext(MessageWorkerContextTask::create(message));
220 }
221
222 void WorkerMessagingProxy::postTaskToWorkerContext(PassRefPtr<ScriptExecutionContext::Task> task)
223 {
224     if (m_askedToTerminate)
225         return;
226
227     if (m_workerThread) {
228         ++m_unconfirmedMessageCount;
229         m_workerThread->runLoop().postTask(task);
230     } else
231         m_queuedEarlyTasks.append(task);
232 }
233
234 void WorkerMessagingProxy::postTaskToWorkerObject(PassRefPtr<ScriptExecutionContext::Task> task)
235 {
236     m_scriptExecutionContext->postTask(task);
237 }
238
239 void WorkerMessagingProxy::postWorkerException(const String& errorMessage, int lineNumber, const String& sourceURL)
240 {
241     m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, sourceURL, this));
242 }
243
244 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<WorkerThread> workerThread)
245 {
246     m_workerThread = workerThread;
247
248     if (m_askedToTerminate) {
249         // Worker.terminate() could be called from JS before the thread was created.
250         m_workerThread->stop();
251     } else {
252         unsigned taskCount = m_queuedEarlyTasks.size();
253         ASSERT(!m_unconfirmedMessageCount);
254         m_unconfirmedMessageCount = taskCount + 1; // Worker initialization counts as a pending message.
255
256         for (unsigned i = 0; i < taskCount; ++i)
257             m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i]);
258         m_queuedEarlyTasks.clear();
259     }
260 }
261
262 void WorkerMessagingProxy::workerObjectDestroyed()
263 {
264     m_workerObject = 0;
265     if (m_workerThread)
266         terminate();
267     else
268         workerContextDestroyedInternal();
269 }
270
271 void WorkerMessagingProxy::workerContextDestroyed()
272 {
273     m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this));
274     // Will execute workerContextDestroyedInternal() on context's thread.
275 }
276
277 void WorkerMessagingProxy::workerContextDestroyedInternal()
278 {
279     // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication
280     // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
281     m_workerThread = 0;
282     if (!m_workerObject)
283         delete this;
284 }
285
286 void WorkerMessagingProxy::terminate()
287 {
288     if (m_askedToTerminate)
289         return;
290     m_askedToTerminate = true;
291
292     if (m_workerThread)
293         m_workerThread->stop();
294 }
295
296 void WorkerMessagingProxy::confirmWorkerThreadMessage(bool hasPendingActivity)
297 {
298     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
299     // Will execute reportWorkerThreadActivityInternal() on context's thread.
300 }
301
302 void WorkerMessagingProxy::reportWorkerThreadActivity(bool hasPendingActivity)
303 {
304     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
305     // Will execute reportWorkerThreadActivityInternal() on context's thread.
306 }
307
308 void WorkerMessagingProxy::reportWorkerThreadActivityInternal(bool confirmingMessage, bool hasPendingActivity)
309 {
310     if (confirmingMessage && !m_askedToTerminate) {
311         ASSERT(m_unconfirmedMessageCount);
312         --m_unconfirmedMessageCount;
313     }
314
315     m_workerThreadHadPendingActivity = hasPendingActivity;
316 }
317
318 bool WorkerMessagingProxy::workerThreadHasPendingActivity() const
319 {
320     return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
321 }
322
323 } // namespace WebCore
324
325 #endif // ENABLE(WORKERS)