WebCore:
[WebKit-https.git] / WebCore / workers / 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 "DedicatedWorkerContext.h"
35 #include "DOMWindow.h"
36 #include "Document.h"
37 #include "GenericWorkerTask.h"
38 #include "MessageEvent.h"
39 #include "ScriptExecutionContext.h"
40 #include "Worker.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, PassOwnPtr<MessagePortChannel> channel)
48     {
49         return adoptRef(new MessageWorkerContextTask(message, channel));
50     }
51
52 private:
53     MessageWorkerContextTask(const String& message, PassOwnPtr<MessagePortChannel> channel)
54         : m_message(message.copy())
55         , m_channel(channel)
56     {
57     }
58
59     virtual void performTask(ScriptExecutionContext* scriptContext)
60     {
61         ASSERT(scriptContext->isWorkerContext());
62         DedicatedWorkerContext* context = static_cast<DedicatedWorkerContext*>(scriptContext);
63         RefPtr<MessagePort> port;
64         if (m_channel) {
65             port = MessagePort::create(*scriptContext);
66             port->entangle(m_channel.release());
67         }
68         context->dispatchMessage(m_message, port.release());
69         context->thread()->workerObjectProxy().confirmMessageFromWorkerObject(context->hasPendingActivity());
70     }
71
72 private:
73     String m_message;
74     OwnPtr<MessagePortChannel> m_channel;
75 };
76
77 class MessageWorkerTask : public ScriptExecutionContext::Task {
78 public:
79     static PassRefPtr<MessageWorkerTask> create(const String& message, PassOwnPtr<MessagePortChannel> channel, WorkerMessagingProxy* messagingProxy)
80     {
81         return adoptRef(new MessageWorkerTask(message, channel, messagingProxy));
82     }
83
84 private:
85     MessageWorkerTask(const String& message, PassOwnPtr<MessagePortChannel> channel, WorkerMessagingProxy* messagingProxy)
86         : m_message(message.copy())
87         , m_channel(channel)
88         , m_messagingProxy(messagingProxy)
89     {
90     }
91
92     virtual void performTask(ScriptExecutionContext* scriptContext)
93     {
94         Worker* workerObject = m_messagingProxy->workerObject();
95         if (!workerObject || m_messagingProxy->askedToTerminate())
96             return;
97
98         RefPtr<MessagePort> port;
99         if (m_channel) {
100             port = MessagePort::create(*scriptContext);
101             port->entangle(m_channel.release());
102         }
103         workerObject->dispatchMessage(m_message, port.release());
104     }
105
106 private:
107     String m_message;
108     OwnPtr<MessagePortChannel> m_channel;
109     WorkerMessagingProxy* m_messagingProxy;
110 };
111
112 class WorkerExceptionTask : public ScriptExecutionContext::Task {
113 public:
114     static PassRefPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
115     {
116         return adoptRef(new WorkerExceptionTask(errorMessage, lineNumber, sourceURL, messagingProxy));
117     }
118
119 private:
120     WorkerExceptionTask(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
121         : m_errorMessage(errorMessage.copy())
122         , m_lineNumber(lineNumber)
123         , m_sourceURL(sourceURL.copy())
124         , m_messagingProxy(messagingProxy)
125     {
126     }
127
128     virtual void performTask(ScriptExecutionContext* context)
129     {
130         Worker* workerObject = m_messagingProxy->workerObject();
131         if (!workerObject || m_messagingProxy->askedToTerminate())
132             return;
133
134         bool errorHandled = false;
135         if (workerObject->onerror())
136             errorHandled = workerObject->dispatchScriptErrorEvent(m_errorMessage, m_sourceURL, m_lineNumber);
137
138         if (!errorHandled)
139             context->reportException(m_errorMessage, m_lineNumber, m_sourceURL);
140     }
141
142     String m_errorMessage;
143     int m_lineNumber;
144     String m_sourceURL;
145     WorkerMessagingProxy* m_messagingProxy;
146 };
147
148 class WorkerContextDestroyedTask : public ScriptExecutionContext::Task {
149 public:
150     static PassRefPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy)
151     {
152         return adoptRef(new WorkerContextDestroyedTask(messagingProxy));
153     }
154
155 private:
156     WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy)
157         : m_messagingProxy(messagingProxy)
158     {
159     }
160
161     virtual void performTask(ScriptExecutionContext*)
162     {
163         m_messagingProxy->workerContextDestroyedInternal();
164     }
165
166     WorkerMessagingProxy* m_messagingProxy;
167 };
168
169 class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task {
170 public:
171     static PassRefPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
172     {
173         return adoptRef(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity));
174     }
175
176 private:
177     WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
178         : m_messagingProxy(messagingProxy)
179         , m_confirmingMessage(confirmingMessage)
180         , m_hasPendingActivity(hasPendingActivity)
181     {
182     }
183
184     virtual void performTask(ScriptExecutionContext*)
185     {
186         m_messagingProxy->reportPendingActivityInternal(m_confirmingMessage, m_hasPendingActivity);
187     }
188
189     WorkerMessagingProxy* m_messagingProxy;
190     bool m_confirmingMessage;
191     bool m_hasPendingActivity;
192 };
193
194
195 #if !PLATFORM(CHROMIUM)
196 WorkerContextProxy* WorkerContextProxy::create(Worker* worker)
197 {
198     return new WorkerMessagingProxy(worker);
199 }
200 #endif
201
202 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject)
203     : m_scriptExecutionContext(workerObject->scriptExecutionContext())
204     , m_workerObject(workerObject)
205     , m_unconfirmedMessageCount(0)
206     , m_workerThreadHadPendingActivity(false)
207     , m_askedToTerminate(false)
208 {
209     ASSERT(m_workerObject);
210     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
211            || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
212 }
213
214 WorkerMessagingProxy::~WorkerMessagingProxy()
215 {
216     ASSERT(!m_workerObject);
217     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
218            || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
219 }
220
221 void WorkerMessagingProxy::startWorkerContext(const KURL& scriptURL, const String& userAgent, const String& sourceCode)
222 {
223     RefPtr<WorkerThread> thread = WorkerThread::create(scriptURL, userAgent, sourceCode, *this, *this);
224     workerThreadCreated(thread);
225     thread->start();
226 }
227
228 void WorkerMessagingProxy::postMessageToWorkerObject(const String& message, PassOwnPtr<MessagePortChannel> channel)
229 {
230     m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, channel, this));
231 }
232
233 void WorkerMessagingProxy::postMessageToWorkerContext(const String& message, PassOwnPtr<MessagePortChannel> channel)
234 {
235     if (m_askedToTerminate)
236         return;
237
238     if (m_workerThread) {
239         ++m_unconfirmedMessageCount;
240         m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message, channel));
241     } else
242         m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message, channel));
243 }
244
245 void WorkerMessagingProxy::postTaskForModeToWorkerContext(PassRefPtr<ScriptExecutionContext::Task> task, const String& mode)
246 {
247     if (m_askedToTerminate)
248         return;
249
250     ASSERT(m_workerThread);
251     m_workerThread->runLoop().postTaskForMode(task, mode);
252 }
253
254 void WorkerMessagingProxy::postTaskToLoader(PassRefPtr<ScriptExecutionContext::Task> task)
255 {
256     // FIXME: In case of nested workers, this should go directly to the root Document context.
257     ASSERT(m_scriptExecutionContext->isDocument());
258     m_scriptExecutionContext->postTask(task);
259 }
260
261 void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, const String& sourceURL)
262 {
263     m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, sourceURL, this));
264 }
265     
266 static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageDestination destination, MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
267 {
268     if (messagingProxy->askedToTerminate())
269         return;
270     context->addMessage(destination, source, type, level, message, lineNumber, sourceURL);
271 }
272
273 void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageDestination destination, MessageSource source, MessageType type, MessageLevel level, const String& message, int lineNumber, const String& sourceURL)
274 {
275     m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageTask, this, destination, source, type, level, message, lineNumber, sourceURL));
276 }
277
278 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<WorkerThread> workerThread)
279 {
280     m_workerThread = workerThread;
281
282     if (m_askedToTerminate) {
283         // Worker.terminate() could be called from JS before the thread was created.
284         m_workerThread->stop();
285     } else {
286         unsigned taskCount = m_queuedEarlyTasks.size();
287         ASSERT(!m_unconfirmedMessageCount);
288         m_unconfirmedMessageCount = taskCount;
289         m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
290
291         for (unsigned i = 0; i < taskCount; ++i)
292             m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i]);
293         m_queuedEarlyTasks.clear();
294     }
295 }
296
297 void WorkerMessagingProxy::workerObjectDestroyed()
298 {
299     m_workerObject = 0;
300     if (m_workerThread)
301         terminateWorkerContext();
302     else
303         workerContextDestroyedInternal();
304 }
305
306 void WorkerMessagingProxy::workerContextDestroyed()
307 {
308     m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this));
309     // Will execute workerContextDestroyedInternal() on context's thread.
310 }
311
312 void WorkerMessagingProxy::workerContextDestroyedInternal()
313 {
314     // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication
315     // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
316     m_askedToTerminate = true;
317     m_workerThread = 0;
318     if (!m_workerObject)
319         delete this;
320 }
321
322 void WorkerMessagingProxy::terminateWorkerContext()
323 {
324     if (m_askedToTerminate)
325         return;
326     m_askedToTerminate = true;
327
328     if (m_workerThread)
329         m_workerThread->stop();
330 }
331
332 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
333 {
334     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
335     // Will execute reportPendingActivityInternal() on context's thread.
336 }
337
338 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
339 {
340     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
341     // Will execute reportPendingActivityInternal() on context's thread.
342 }
343
344 void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
345 {
346     if (confirmingMessage && !m_askedToTerminate) {
347         ASSERT(m_unconfirmedMessageCount);
348         --m_unconfirmedMessageCount;
349     }
350
351     m_workerThreadHadPendingActivity = hasPendingActivity;
352 }
353
354 bool WorkerMessagingProxy::hasPendingActivity() const
355 {
356     return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
357 }
358
359 } // namespace WebCore
360
361 #endif // ENABLE(WORKERS)