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 "ContentSecurityPolicy.h"
35#include "CrossThreadTask.h"
36#include "DedicatedWorkerContext.h"
37#include "DedicatedWorkerThread.h"
38#include "DOMWindow.h"
39#include "Document.h"
40#include "ErrorEvent.h"
41#include "ExceptionCode.h"
42#include "InspectorInstrumentation.h"
43#include "MessageEvent.h"
44#include "NotImplemented.h"
45#include "PageGroup.h"
46#include "ScriptCallStack.h"
47#include "ScriptExecutionContext.h"
48#include "Worker.h"
49#include "WorkerDebuggerAgent.h"
50#include "WorkerInspectorController.h"
51#include <wtf/MainThread.h>
52
53namespace WebCore {
54
55class MessageWorkerContextTask : public ScriptExecutionContext::Task {
56public:
57    static PassOwnPtr<MessageWorkerContextTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
58    {
59        return adoptPtr(new MessageWorkerContextTask(message, channels));
60    }
61
62private:
63    MessageWorkerContextTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
64        : m_message(message)
65        , m_channels(channels)
66    {
67    }
68
69    virtual void performTask(ScriptExecutionContext* scriptContext)
70    {
71        ASSERT_WITH_SECURITY_IMPLICATION(scriptContext->isWorkerContext());
72        DedicatedWorkerContext* context = static_cast<DedicatedWorkerContext*>(scriptContext);
73        OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release());
74        context->dispatchEvent(MessageEvent::create(ports.release(), m_message));
75        context->thread()->workerObjectProxy().confirmMessageFromWorkerObject(context->hasPendingActivity());
76    }
77
78private:
79    RefPtr<SerializedScriptValue> m_message;
80    OwnPtr<MessagePortChannelArray> m_channels;
81};
82
83class MessageWorkerTask : public ScriptExecutionContext::Task {
84public:
85    static PassOwnPtr<MessageWorkerTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels, WorkerMessagingProxy* messagingProxy)
86    {
87        return adoptPtr(new MessageWorkerTask(message, channels, messagingProxy));
88    }
89
90private:
91    MessageWorkerTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels, WorkerMessagingProxy* messagingProxy)
92        : m_message(message)
93        , m_channels(channels)
94        , m_messagingProxy(messagingProxy)
95    {
96    }
97
98    virtual void performTask(ScriptExecutionContext* scriptContext)
99    {
100        Worker* workerObject = m_messagingProxy->workerObject();
101        if (!workerObject || m_messagingProxy->askedToTerminate())
102            return;
103
104        OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release());
105        workerObject->dispatchEvent(MessageEvent::create(ports.release(), m_message));
106    }
107
108private:
109    RefPtr<SerializedScriptValue> m_message;
110    OwnPtr<MessagePortChannelArray> m_channels;
111    WorkerMessagingProxy* m_messagingProxy;
112};
113
114class WorkerExceptionTask : public ScriptExecutionContext::Task {
115public:
116    static PassOwnPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
117    {
118        return adoptPtr(new WorkerExceptionTask(errorMessage, lineNumber, columnNumber, sourceURL, messagingProxy));
119    }
120
121private:
122    WorkerExceptionTask(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy)
123        : m_errorMessage(errorMessage.isolatedCopy())
124        , m_lineNumber(lineNumber)
125        , m_columnNumber(columnNumber)
126        , m_sourceURL(sourceURL.isolatedCopy())
127        , m_messagingProxy(messagingProxy)
128    {
129    }
130
131    virtual void performTask(ScriptExecutionContext* context)
132    {
133        Worker* workerObject = m_messagingProxy->workerObject();
134        if (!workerObject)
135            return;
136
137        // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated.
138        // 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.
139
140        bool errorHandled = !workerObject->dispatchEvent(ErrorEvent::create(m_errorMessage, m_sourceURL, m_lineNumber));
141        if (!errorHandled)
142            context->reportException(m_errorMessage, m_lineNumber, m_columnNumber, m_sourceURL, 0);
143    }
144
145    String m_errorMessage;
146    int m_lineNumber;
147    int m_columnNumber;
148    String m_sourceURL;
149    WorkerMessagingProxy* m_messagingProxy;
150};
151
152class WorkerContextDestroyedTask : public ScriptExecutionContext::Task {
153public:
154    static PassOwnPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy)
155    {
156        return adoptPtr(new WorkerContextDestroyedTask(messagingProxy));
157    }
158
159private:
160    WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy)
161        : m_messagingProxy(messagingProxy)
162    {
163    }
164
165    virtual void performTask(ScriptExecutionContext*)
166    {
167        m_messagingProxy->workerContextDestroyedInternal();
168    }
169
170    WorkerMessagingProxy* m_messagingProxy;
171};
172
173class WorkerTerminateTask : public ScriptExecutionContext::Task {
174public:
175    static PassOwnPtr<WorkerTerminateTask> create(WorkerMessagingProxy* messagingProxy)
176    {
177        return adoptPtr(new WorkerTerminateTask(messagingProxy));
178    }
179
180private:
181    WorkerTerminateTask(WorkerMessagingProxy* messagingProxy)
182        : m_messagingProxy(messagingProxy)
183    {
184    }
185
186    virtual void performTask(ScriptExecutionContext*)
187    {
188        m_messagingProxy->terminateWorkerContext();
189    }
190
191    WorkerMessagingProxy* m_messagingProxy;
192};
193
194class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task {
195public:
196    static PassOwnPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
197    {
198        return adoptPtr(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity));
199    }
200
201private:
202    WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity)
203        : m_messagingProxy(messagingProxy)
204        , m_confirmingMessage(confirmingMessage)
205        , m_hasPendingActivity(hasPendingActivity)
206    {
207    }
208
209    virtual void performTask(ScriptExecutionContext*)
210    {
211        m_messagingProxy->reportPendingActivityInternal(m_confirmingMessage, m_hasPendingActivity);
212    }
213
214    WorkerMessagingProxy* m_messagingProxy;
215    bool m_confirmingMessage;
216    bool m_hasPendingActivity;
217};
218
219class PostMessageToPageInspectorTask : public ScriptExecutionContext::Task {
220public:
221    static PassOwnPtr<PostMessageToPageInspectorTask> create(WorkerMessagingProxy* messagingProxy, const String& message)
222    {
223        return adoptPtr(new PostMessageToPageInspectorTask(messagingProxy, message));
224    }
225
226private:
227    PostMessageToPageInspectorTask(WorkerMessagingProxy* messagingProxy, const String& message)
228        : m_messagingProxy(messagingProxy)
229        , m_message(message.isolatedCopy())
230    {
231    }
232
233    virtual void performTask(ScriptExecutionContext*)
234    {
235#if ENABLE(INSPECTOR)
236        if (WorkerContextProxy::PageInspector* pageInspector = m_messagingProxy->m_pageInspector)
237            pageInspector->dispatchMessageFromWorker(m_message);
238#endif
239    }
240
241    WorkerMessagingProxy* m_messagingProxy;
242    String m_message;
243};
244
245
246WorkerContextProxy* WorkerContextProxy::create(Worker* worker)
247{
248    return new WorkerMessagingProxy(worker);
249}
250
251WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject)
252    : m_scriptExecutionContext(workerObject->scriptExecutionContext())
253    , m_workerObject(workerObject)
254    , m_mayBeDestroyed(false)
255    , m_unconfirmedMessageCount(0)
256    , m_workerThreadHadPendingActivity(false)
257    , m_askedToTerminate(false)
258#if ENABLE(INSPECTOR)
259    , m_pageInspector(0)
260#endif
261{
262    ASSERT(m_workerObject);
263    ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
264           || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
265}
266
267WorkerMessagingProxy::~WorkerMessagingProxy()
268{
269    ASSERT(!m_workerObject);
270    ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
271           || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID()));
272}
273
274void WorkerMessagingProxy::startWorkerContext(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode)
275{
276    // FIXME: This need to be revisited when we support nested worker one day
277    ASSERT(m_scriptExecutionContext->isDocument());
278    Document* document = static_cast<Document*>(m_scriptExecutionContext.get());
279    GroupSettings* settings = 0;
280    if (document->page())
281        settings = document->page()->group().groupSettings();
282    RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, userAgent, settings, sourceCode, *this, *this, startMode, document->contentSecurityPolicy()->deprecatedHeader(), document->contentSecurityPolicy()->deprecatedHeaderType(), document->topOrigin());
283    workerThreadCreated(thread);
284    thread->start();
285    InspectorInstrumentation::didStartWorkerContext(m_scriptExecutionContext.get(), this, scriptURL);
286}
287
288void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
289{
290    m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, channels, this));
291}
292
293void WorkerMessagingProxy::postMessageToWorkerContext(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
294{
295    if (m_askedToTerminate)
296        return;
297
298    if (m_workerThread) {
299        ++m_unconfirmedMessageCount;
300        m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message, channels));
301    } else
302        m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message, channels));
303}
304
305bool WorkerMessagingProxy::postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
306{
307    if (m_askedToTerminate)
308        return false;
309
310    ASSERT(m_workerThread);
311    m_workerThread->runLoop().postTaskForMode(task, mode);
312    return true;
313}
314
315void WorkerMessagingProxy::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
316{
317    // FIXME: In case of nested workers, this should go directly to the root Document context.
318    ASSERT(m_scriptExecutionContext->isDocument());
319    m_scriptExecutionContext->postTask(task);
320}
321
322void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL)
323{
324    m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, columnNumber, sourceURL, this));
325}
326
327static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceURL)
328{
329    if (messagingProxy->askedToTerminate())
330        return;
331    context->addConsoleMessage(source, level, message, sourceURL, lineNumber, columnNumber);
332}
333
334void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageLevel level, const String& message, int lineNumber, int columnNumber, const String& sourceURL)
335{
336    m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageTask, AllowCrossThreadAccess(this), source, level, message, lineNumber, columnNumber, sourceURL));
337}
338
339void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread)
340{
341    m_workerThread = workerThread;
342
343    if (m_askedToTerminate) {
344        // Worker.terminate() could be called from JS before the thread was created.
345        m_workerThread->stop();
346    } else {
347        unsigned taskCount = m_queuedEarlyTasks.size();
348        ASSERT(!m_unconfirmedMessageCount);
349        m_unconfirmedMessageCount = taskCount;
350        m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
351
352        for (unsigned i = 0; i < taskCount; ++i)
353            m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i].release());
354        m_queuedEarlyTasks.clear();
355    }
356}
357
358void WorkerMessagingProxy::workerObjectDestroyed()
359{
360    m_workerObject = 0;
361    m_scriptExecutionContext->postTask(createCallbackTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this)));
362}
363
364void WorkerMessagingProxy::workerObjectDestroyedInternal(ScriptExecutionContext*, WorkerMessagingProxy* proxy)
365{
366    proxy->m_mayBeDestroyed = true;
367    if (proxy->m_workerThread)
368        proxy->terminateWorkerContext();
369    else
370        proxy->workerContextDestroyedInternal();
371}
372
373#if ENABLE(INSPECTOR)
374#if ENABLE(JAVASCRIPT_DEBUGGER)
375static void connectToWorkerContextInspectorTask(ScriptExecutionContext* context, bool)
376{
377    ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerContext());
378    static_cast<WorkerContext*>(context)->workerInspectorController()->connectFrontend();
379}
380#endif
381
382void WorkerMessagingProxy::connectToInspector(WorkerContextProxy::PageInspector* pageInspector)
383{
384    if (m_askedToTerminate)
385        return;
386    ASSERT(!m_pageInspector);
387    m_pageInspector = pageInspector;
388#if ENABLE(JAVASCRIPT_DEBUGGER)
389    m_workerThread->runLoop().postTaskForMode(createCallbackTask(connectToWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
390#endif
391}
392
393#if ENABLE(JAVASCRIPT_DEBUGGER)
394static void disconnectFromWorkerContextInspectorTask(ScriptExecutionContext* context, bool)
395{
396    ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerContext());
397    static_cast<WorkerContext*>(context)->workerInspectorController()->disconnectFrontend();
398}
399#endif
400
401void WorkerMessagingProxy::disconnectFromInspector()
402{
403    m_pageInspector = 0;
404    if (m_askedToTerminate)
405        return;
406#if ENABLE(JAVASCRIPT_DEBUGGER)
407    m_workerThread->runLoop().postTaskForMode(createCallbackTask(disconnectFromWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
408#endif
409}
410
411#if ENABLE(JAVASCRIPT_DEBUGGER)
412static void dispatchOnInspectorBackendTask(ScriptExecutionContext* context, const String& message)
413{
414    ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerContext());
415    static_cast<WorkerContext*>(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
416}
417#endif
418
419void WorkerMessagingProxy::sendMessageToInspector(const String& message)
420{
421    if (m_askedToTerminate)
422        return;
423#if ENABLE(JAVASCRIPT_DEBUGGER)
424    m_workerThread->runLoop().postTaskForMode(createCallbackTask(dispatchOnInspectorBackendTask, String(message)), WorkerDebuggerAgent::debuggerTaskMode);
425    WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get());
426#endif
427}
428#endif
429
430void WorkerMessagingProxy::workerContextDestroyed()
431{
432    m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this));
433    // Will execute workerContextDestroyedInternal() on context's thread.
434}
435
436void WorkerMessagingProxy::workerContextClosed()
437{
438    // Executes terminateWorkerContext() on parent context's thread.
439    m_scriptExecutionContext->postTask(WorkerTerminateTask::create(this));
440}
441
442void WorkerMessagingProxy::workerContextDestroyedInternal()
443{
444    // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication
445    // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
446    m_askedToTerminate = true;
447    m_workerThread = 0;
448
449    InspectorInstrumentation::workerContextTerminated(m_scriptExecutionContext.get(), this);
450
451    if (m_mayBeDestroyed)
452        delete this;
453}
454
455void WorkerMessagingProxy::terminateWorkerContext()
456{
457    if (m_askedToTerminate)
458        return;
459    m_askedToTerminate = true;
460
461    if (m_workerThread)
462        m_workerThread->stop();
463
464    InspectorInstrumentation::workerContextTerminated(m_scriptExecutionContext.get(), this);
465}
466
467#if ENABLE(INSPECTOR)
468void WorkerMessagingProxy::postMessageToPageInspector(const String& message)
469{
470    m_scriptExecutionContext->postTask(PostMessageToPageInspectorTask::create(this, message));
471}
472
473void WorkerMessagingProxy::updateInspectorStateCookie(const String&)
474{
475    notImplemented();
476}
477#endif
478
479void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
480{
481    m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
482    // Will execute reportPendingActivityInternal() on context's thread.
483}
484
485void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
486{
487    m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
488    // Will execute reportPendingActivityInternal() on context's thread.
489}
490
491void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
492{
493    if (confirmingMessage && !m_askedToTerminate) {
494        ASSERT(m_unconfirmedMessageCount);
495        --m_unconfirmedMessageCount;
496    }
497
498    m_workerThreadHadPendingActivity = hasPendingActivity;
499}
500
501bool WorkerMessagingProxy::hasPendingActivity() const
502{
503    return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
504}
505
506} // namespace WebCore
507
508#endif // ENABLE(WORKERS)
509