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