Manage MessagePort, MessagePortChannel and friends through std::unique_ptr
[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         WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
263         workerGlobalScope->dispatchEvent(Event::create(eventName, false, false));
264     }
265
266     bool m_isOnLine;
267 };
268
269
270 WorkerGlobalScopeProxy* WorkerGlobalScopeProxy::create(Worker* worker)
271 {
272     return new WorkerMessagingProxy(worker);
273 }
274
275 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject)
276     : m_scriptExecutionContext(workerObject->scriptExecutionContext())
277     , m_workerObject(workerObject)
278     , m_mayBeDestroyed(false)
279     , m_unconfirmedMessageCount(0)
280     , m_workerThreadHadPendingActivity(false)
281     , m_askedToTerminate(false)
282 #if ENABLE(INSPECTOR)
283     , m_pageInspector(0)
284 #endif
285 {
286     ASSERT(m_workerObject);
287     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
288            || (m_scriptExecutionContext->isWorkerGlobalScope() && currentThread() == static_cast<WorkerGlobalScope*>(m_scriptExecutionContext.get())->thread()->threadID()));
289 }
290
291 WorkerMessagingProxy::~WorkerMessagingProxy()
292 {
293     ASSERT(!m_workerObject);
294     ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
295            || (m_scriptExecutionContext->isWorkerGlobalScope() && currentThread() == static_cast<WorkerGlobalScope*>(m_scriptExecutionContext.get())->thread()->threadID()));
296 }
297
298 void WorkerMessagingProxy::startWorkerGlobalScope(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode)
299 {
300     // FIXME: This need to be revisited when we support nested worker one day
301     ASSERT_WITH_SECURITY_IMPLICATION(m_scriptExecutionContext->isDocument());
302     Document* document = static_cast<Document*>(m_scriptExecutionContext.get());
303     GroupSettings* settings = 0;
304     if (document->page())
305         settings = &document->page()->group().groupSettings();
306     RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, userAgent, settings, sourceCode, *this, *this, startMode, document->contentSecurityPolicy()->deprecatedHeader(), document->contentSecurityPolicy()->deprecatedHeaderType(), document->topOrigin());
307     workerThreadCreated(thread);
308     thread->start();
309     InspectorInstrumentation::didStartWorkerGlobalScope(m_scriptExecutionContext.get(), this, scriptURL);
310 }
311
312 void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels)
313 {
314     m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, std::move(channels), this));
315 }
316
317 void WorkerMessagingProxy::postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels)
318 {
319     if (m_askedToTerminate)
320         return;
321
322     if (m_workerThread) {
323         ++m_unconfirmedMessageCount;
324         m_workerThread->runLoop().postTask(MessageWorkerGlobalScopeTask::create(message, std::move(channels)));
325     } else
326         m_queuedEarlyTasks.append(MessageWorkerGlobalScopeTask::create(message, std::move(channels)));
327 }
328
329 bool WorkerMessagingProxy::postTaskForModeToWorkerGlobalScope(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
330 {
331     if (m_askedToTerminate)
332         return false;
333
334     ASSERT(m_workerThread);
335     m_workerThread->runLoop().postTaskForMode(task, mode);
336     return true;
337 }
338
339 void WorkerMessagingProxy::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
340 {
341     // FIXME: In case of nested workers, this should go directly to the root Document context.
342     ASSERT(m_scriptExecutionContext->isDocument());
343     m_scriptExecutionContext->postTask(task);
344 }
345
346 void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL)
347 {
348     m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, columnNumber, sourceURL, this));
349 }
350
351 static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceURL)
352 {
353     if (messagingProxy->askedToTerminate())
354         return;
355     context->addConsoleMessage(source, level, message, sourceURL, lineNumber, columnNumber);
356 }
357
358 void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageLevel level, const String& message, int lineNumber, int columnNumber, const String& sourceURL)
359 {
360     m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageTask, AllowCrossThreadAccess(this), source, level, message, lineNumber, columnNumber, sourceURL));
361 }
362
363 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread)
364 {
365     m_workerThread = workerThread;
366
367     if (m_askedToTerminate) {
368         // Worker.terminate() could be called from JS before the thread was created.
369         m_workerThread->stop();
370     } else {
371         unsigned taskCount = m_queuedEarlyTasks.size();
372         ASSERT(!m_unconfirmedMessageCount);
373         m_unconfirmedMessageCount = taskCount;
374         m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
375
376         for (unsigned i = 0; i < taskCount; ++i)
377             m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i].release());
378         m_queuedEarlyTasks.clear();
379     }
380 }
381
382 void WorkerMessagingProxy::workerObjectDestroyed()
383 {
384     m_workerObject = 0;
385     m_scriptExecutionContext->postTask(createCallbackTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this)));
386 }
387
388 void WorkerMessagingProxy::notifyNetworkStateChange(bool isOnline)
389 {
390     if (m_askedToTerminate)
391         return;
392
393     if (!m_workerThread)
394         return;
395
396     m_workerThread->runLoop().postTask(NotifyNetworkStateChangeTask::create(isOnline));
397 }
398
399 void WorkerMessagingProxy::workerObjectDestroyedInternal(ScriptExecutionContext*, WorkerMessagingProxy* proxy)
400 {
401     proxy->m_mayBeDestroyed = true;
402     if (proxy->m_workerThread)
403         proxy->terminateWorkerGlobalScope();
404     else
405         proxy->workerGlobalScopeDestroyedInternal();
406 }
407
408 #if ENABLE(INSPECTOR)
409 static void connectToWorkerGlobalScopeInspectorTask(ScriptExecutionContext* context, bool)
410 {
411     ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
412     static_cast<WorkerGlobalScope*>(context)->workerInspectorController().connectFrontend();
413 }
414
415 void WorkerMessagingProxy::connectToInspector(WorkerGlobalScopeProxy::PageInspector* pageInspector)
416 {
417     if (m_askedToTerminate)
418         return;
419     ASSERT(!m_pageInspector);
420     m_pageInspector = pageInspector;
421     m_workerThread->runLoop().postTaskForMode(createCallbackTask(connectToWorkerGlobalScopeInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
422 }
423
424 static void disconnectFromWorkerGlobalScopeInspectorTask(ScriptExecutionContext* context, bool)
425 {
426     ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
427     static_cast<WorkerGlobalScope*>(context)->workerInspectorController().disconnectFrontend(Inspector::InspectorDisconnectReason::InspectorDestroyed);
428 }
429
430 void WorkerMessagingProxy::disconnectFromInspector()
431 {
432     m_pageInspector = 0;
433     if (m_askedToTerminate)
434         return;
435     m_workerThread->runLoop().postTaskForMode(createCallbackTask(disconnectFromWorkerGlobalScopeInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
436 }
437
438 static void dispatchOnInspectorBackendTask(ScriptExecutionContext* context, const String& message)
439 {
440     ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
441     static_cast<WorkerGlobalScope*>(context)->workerInspectorController().dispatchMessageFromFrontend(message);
442 }
443
444 void WorkerMessagingProxy::sendMessageToInspector(const String& message)
445 {
446     if (m_askedToTerminate)
447         return;
448     m_workerThread->runLoop().postTaskForMode(createCallbackTask(dispatchOnInspectorBackendTask, String(message)), WorkerDebuggerAgent::debuggerTaskMode);
449     WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get());
450 }
451 #endif
452
453 void WorkerMessagingProxy::workerGlobalScopeDestroyed()
454 {
455     m_scriptExecutionContext->postTask(WorkerGlobalScopeDestroyedTask::create(this));
456     // Will execute workerGlobalScopeDestroyedInternal() on context's thread.
457 }
458
459 void WorkerMessagingProxy::workerGlobalScopeClosed()
460 {
461     // Executes terminateWorkerGlobalScope() on parent context's thread.
462     m_scriptExecutionContext->postTask(WorkerTerminateTask::create(this));
463 }
464
465 void WorkerMessagingProxy::workerGlobalScopeDestroyedInternal()
466 {
467     // WorkerGlobalScopeDestroyedTask is always the last to be performed, so the proxy is not needed for communication
468     // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
469     m_askedToTerminate = true;
470     m_workerThread = 0;
471
472     InspectorInstrumentation::workerGlobalScopeTerminated(m_scriptExecutionContext.get(), this);
473
474     if (m_mayBeDestroyed)
475         delete this;
476 }
477
478 void WorkerMessagingProxy::terminateWorkerGlobalScope()
479 {
480     if (m_askedToTerminate)
481         return;
482     m_askedToTerminate = true;
483
484     if (m_workerThread)
485         m_workerThread->stop();
486
487     InspectorInstrumentation::workerGlobalScopeTerminated(m_scriptExecutionContext.get(), this);
488 }
489
490 #if ENABLE(INSPECTOR)
491 void WorkerMessagingProxy::postMessageToPageInspector(const String& message)
492 {
493     m_scriptExecutionContext->postTask(PostMessageToPageInspectorTask::create(this, message));
494 }
495 #endif
496
497 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
498 {
499     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity));
500     // Will execute reportPendingActivityInternal() on context's thread.
501 }
502
503 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
504 {
505     m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity));
506     // Will execute reportPendingActivityInternal() on context's thread.
507 }
508
509 void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
510 {
511     if (confirmingMessage && !m_askedToTerminate) {
512         ASSERT(m_unconfirmedMessageCount);
513         --m_unconfirmedMessageCount;
514     }
515
516     m_workerThreadHadPendingActivity = hasPendingActivity;
517 }
518
519 bool WorkerMessagingProxy::hasPendingActivity() const
520 {
521     return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
522 }
523
524 } // namespace WebCore