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