2009-02-24 David Levin <levin@chromium.org>
[WebKit-https.git] / WebCore / dom / WorkerMessagingProxy.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009 Google Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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. 
25  *
26  */
27
28 #include "config.h"
29
30 #if ENABLE(WORKERS)
31
32 #include "WorkerMessagingProxy.h"
33
34 #include "DOMWindow.h"
35 #include "Document.h"
36 #include "GenericWorkerTask.h"
37 #include "MessageEvent.h"
38 #include "ScriptExecutionContext.h"
39 #include "Worker.h"
40 #include "WorkerContext.h"
41 #include "WorkerThread.h"
42
43 namespace WebCore {
44
45 class MessageWorkerContextTask : public ScriptExecutionContext::Task {
46 public:
47     static PassRefPtr<MessageWorkerContextTask> create(const String& message)
48     {
49         return adoptRef(new MessageWorkerContextTask(message));
50     }
51
52 private:
53     MessageWorkerContextTask(const String& message)
54         : m_message(message.copy())
55     {
56     }
57
58     virtual void performTask(ScriptExecutionContext* scriptContext)
59     {
60         ASSERT(scriptContext->isWorkerContext());
61         WorkerContext* context = static_cast<WorkerContext*>(scriptContext);
62
63         context->dispatchMessage(m_message);
64
65         static_cast<WorkerMessagingProxy*>(context->thread()->workerObjectProxy())->confirmWorkerThreadMessage(context->hasPendingActivity());
66     }
67
68 private:
69     String m_message;
70 };
71
72 class MessageWorkerTask : public ScriptExecutionContext::Task {
73 public:
74     static PassRefPtr<MessageWorkerTask> create(const String& message, WorkerMessagingProxy* messagingProxy)
75     {
76         return adoptRef(new MessageWorkerTask(message, messagingProxy));
77     }
78
79 private:
80     MessageWorkerTask(const String& message, WorkerMessagingProxy* messagingProxy)
81         : m_message(message.copy())
82         , m_messagingProxy(messagingProxy)
83     {
84     }
85
86     virtual void performTask(ScriptExecutionContext*)
87     {
88         Worker* workerObject = m_messagingProxy->workerObject();
89         if (!workerObject || m_messagingProxy->askedToTerminate())
90             return;
91
92         workerObject->dispatchMessage(m_message);
93     }
94
95 private:
96     String m_message;
97     WorkerMessagingProxy* m_messagingProxy;
98 };
99
100 class WorkerExceptionTask : public ScriptExecutionContext::Task {
101 public:
102     static PassRefPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
103     {
104         return adoptRef(new WorkerExceptionTask(errorMessage, lineNumber, sourceURL, messagingProxy));
105     }
106
107 private:
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)
113     {
114     }
115
116     virtual void performTask(ScriptExecutionContext* context)
117     {
118         if (!m_messagingProxy->askedToTerminate())
119             context->reportException(m_errorMessage, m_lineNumber, m_sourceURL);
120     }
121
122     String m_errorMessage;
123     int m_lineNumber;
124     String m_sourceURL;
125     WorkerMessagingProxy* m_messagingProxy;
126 };
127
128 class WorkerContextDestroyedTask : public ScriptExecutionContext::Task {
129 public:
130     static PassRefPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy)
131     {
132         return adoptRef(new WorkerContextDestroyedTask(messagingProxy));
133     }
134
135 private:
136     WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy)
137         : m_messagingProxy(messagingProxy)
138     {
139     }
140
141     virtual void performTask(ScriptExecutionContext*)
142     {
143         m_messagingProxy->workerContextDestroyedInternal();
144     }
145
146     WorkerMessagingProxy* m_messagingProxy;
147 };
148
149 class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task {
150 public:
151     static PassRefPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
152     {
153         return adoptRef(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity));
154     }
155
156 private:
157     WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
158         : m_messagingProxy(messagingProxy)
159         , m_confirmingMessage(confirmingMessage)
160         , m_hasPendingActivity(hasPendingActivity)
161     {
162     }
163
164     virtual void performTask(ScriptExecutionContext*)
165     {
166         m_messagingProxy->reportPendingActivityInternal(m_confirmingMessage, m_hasPendingActivity);
167     }
168
169     WorkerMessagingProxy* m_messagingProxy;
170     bool m_confirmingMessage;
171     bool m_hasPendingActivity;
172 };
173
174
175 WorkerContextProxy* WorkerContextProxy::create(Worker* worker)
176 {
177     return new WorkerMessagingProxy(worker);
178 }
179
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)
186 {
187     ASSERT(m_workerObject);
188     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
189            || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
190 }
191
192 WorkerMessagingProxy::~WorkerMessagingProxy()
193 {
194     ASSERT(!m_workerObject);
195     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
196            || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
197 }
198
199 void WorkerMessagingProxy::startWorkerContext(const KURL& scriptURL, const String& userAgent, const String& sourceCode)
200 {
201     RefPtr<WorkerThread> thread = WorkerThread::create(scriptURL, userAgent, sourceCode, this);
202     workerThreadCreated(thread);
203     thread->start();
204 }
205
206 void WorkerMessagingProxy::postMessageToWorkerObject(const String& message)
207 {
208     m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, this));
209 }
210
211 void WorkerMessagingProxy::postMessageToWorkerContext(const String& message)
212 {
213     if (m_askedToTerminate)
214         return;
215
216     if (m_workerThread) {
217         ++m_unconfirmedMessageCount;
218         m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message));
219     } else
220         m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message));
221 }
222
223 void WorkerMessagingProxy::postTaskToWorkerContext(PassRefPtr<ScriptExecutionContext::Task> task)
224 {
225     postTaskForModeToWorkerContext(task, WorkerRunLoop::defaultMode());
226 }
227
228 void WorkerMessagingProxy::postTaskForModeToWorkerContext(PassRefPtr<ScriptExecutionContext::Task> task, const String& mode)
229 {
230     if (m_askedToTerminate)
231         return;
232
233     ASSERT(m_workerThread);
234     m_workerThread->runLoop().postTaskForMode(task, mode);
235 }
236
237 void WorkerMessagingProxy::postTaskToWorkerObject(PassRefPtr<ScriptExecutionContext::Task> task)
238 {
239     m_scriptExecutionContext->postTask(task);
240 }
241
242 void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, const String& sourceURL)
243 {
244     m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, sourceURL, this));
245 }
246     
247 static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageDestination destination, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
248 {
249     if (messagingProxy->askedToTerminate())
250         return;
251     context->addMessage(destination, source, level, message, lineNumber, sourceURL);
252 }
253
254 void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageDestination destination, MessageSource source, MessageLevel level, const String& message, int lineNumber, const String& sourceURL)
255 {
256     m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageTask, this, destination, source, level, message, lineNumber, sourceURL));
257 }
258
259 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<WorkerThread> workerThread)
260 {
261     m_workerThread = workerThread;
262
263     if (m_askedToTerminate) {
264         // Worker.terminate() could be called from JS before the thread was created.
265         m_workerThread->stop();
266     } else {
267         unsigned taskCount = m_queuedEarlyTasks.size();
268         ASSERT(!m_unconfirmedMessageCount);
269         m_unconfirmedMessageCount = taskCount;
270         m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
271
272         for (unsigned i = 0; i < taskCount; ++i)
273             m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i]);
274         m_queuedEarlyTasks.clear();
275     }
276 }
277
278 void WorkerMessagingProxy::workerObjectDestroyed()
279 {
280     m_workerObject = 0;
281     if (m_workerThread)
282         terminateWorkerContext();
283     else
284         workerContextDestroyedInternal();
285 }
286
287 void WorkerMessagingProxy::workerContextDestroyed()
288 {
289     m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this));
290     // Will execute workerContextDestroyedInternal() on context's thread.
291 }
292
293 void WorkerMessagingProxy::workerContextDestroyedInternal()
294 {
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.
297     m_workerThread = 0;
298     if (!m_workerObject)
299         delete this;
300 }
301
302 void WorkerMessagingProxy::terminateWorkerContext()
303 {
304     if (m_askedToTerminate)
305         return;
306     m_askedToTerminate = true;
307
308     if (m_workerThread)
309         m_workerThread->stop();
310 }
311
312 void WorkerMessagingProxy::confirmWorkerThreadMessage(bool hasPendingActivity)
313 {
314     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
315     // Will execute reportPendingActivityInternal() on context's thread.
316 }
317
318 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
319 {
320     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
321     // Will execute reportPendingActivityInternal() on context's thread.
322 }
323
324 void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
325 {
326     if (confirmingMessage && !m_askedToTerminate) {
327         ASSERT(m_unconfirmedMessageCount);
328         --m_unconfirmedMessageCount;
329     }
330
331     m_workerThreadHadPendingActivity = hasPendingActivity;
332 }
333
334 bool WorkerMessagingProxy::hasPendingActivity() const
335 {
336     return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
337 }
338
339 } // namespace WebCore
340
341 #endif // ENABLE(WORKERS)