SerializedScriptValue passed to postMessage() cannot be null
[WebKit-https.git] / Source / WebCore / workers / WorkerMessagingProxy.cpp
1 /*
2  * Copyright (C) 2008-2017 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 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 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 "DOMWindow.h"
33 #include "DedicatedWorkerGlobalScope.h"
34 #include "DedicatedWorkerThread.h"
35 #include "Document.h"
36 #include "ErrorEvent.h"
37 #include "EventNames.h"
38 #include "MessageEvent.h"
39 #include "ScriptExecutionContext.h"
40 #include "Worker.h"
41 #include "WorkerInspectorProxy.h"
42 #include <inspector/ScriptCallStack.h>
43 #include <runtime/ConsoleTypes.h>
44 #include <wtf/MainThread.h>
45 #include <wtf/RunLoop.h>
46
47 namespace WebCore {
48
49 WorkerGlobalScopeProxy& WorkerGlobalScopeProxy::create(Worker& worker)
50 {
51     return *new WorkerMessagingProxy(worker);
52 }
53
54 WorkerMessagingProxy::WorkerMessagingProxy(Worker& workerObject)
55     : m_scriptExecutionContext(workerObject.scriptExecutionContext())
56     , m_inspectorProxy(std::make_unique<WorkerInspectorProxy>(workerObject.identifier()))
57     , m_workerObject(&workerObject)
58 {
59     ASSERT((is<Document>(*m_scriptExecutionContext) && isMainThread())
60         || (is<WorkerGlobalScope>(*m_scriptExecutionContext) && currentThread() == downcast<WorkerGlobalScope>(*m_scriptExecutionContext).thread().threadID()));
61
62     // Nobody outside this class ref counts this object. The original ref
63     // is balanced by the deref in workerGlobalScopeDestroyedInternal.
64 }
65
66 WorkerMessagingProxy::~WorkerMessagingProxy()
67 {
68     ASSERT(!m_workerObject);
69     ASSERT((is<Document>(*m_scriptExecutionContext) && isMainThread())
70         || (is<WorkerGlobalScope>(*m_scriptExecutionContext) && currentThread() == downcast<WorkerGlobalScope>(*m_scriptExecutionContext).thread().threadID()));
71 }
72
73 void WorkerMessagingProxy::startWorkerGlobalScope(const URL& scriptURL, const String& userAgent, const String& sourceCode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, MonotonicTime timeOrigin, JSC::RuntimeFlags runtimeFlags, PAL::SessionID sessionID)
74 {
75     // FIXME: This need to be revisited when we support nested worker one day
76     ASSERT(m_scriptExecutionContext);
77     Document& document = downcast<Document>(*m_scriptExecutionContext);
78     WorkerThreadStartMode startMode = m_inspectorProxy->workerStartMode(*m_scriptExecutionContext.get());
79     String identifier = m_inspectorProxy->identifier();
80
81 #if ENABLE(INDEXED_DATABASE)
82     IDBClient::IDBConnectionProxy* proxy = document.idbConnectionProxy();
83 #else
84     IDBClient::IDBConnectionProxy* proxy = nullptr;
85 #endif
86
87     SocketProvider* socketProvider = document.socketProvider();
88
89     auto thread = DedicatedWorkerThread::create(scriptURL, identifier, userAgent, sourceCode, *this, *this, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, document.topOrigin(), timeOrigin, proxy, socketProvider, runtimeFlags, sessionID);
90
91     workerThreadCreated(thread.get());
92     thread->start();
93
94     m_inspectorProxy->workerStarted(m_scriptExecutionContext.get(), thread.ptr(), scriptURL);
95 }
96
97 void WorkerMessagingProxy::postMessageToWorkerObject(Ref<SerializedScriptValue>&& message, std::unique_ptr<MessagePortChannelArray>&& channels)
98 {
99     m_scriptExecutionContext->postTask([this, channels = WTFMove(channels), message = WTFMove(message)] (ScriptExecutionContext& context) mutable {
100         Worker* workerObject = this->workerObject();
101         if (!workerObject || askedToTerminate())
102             return;
103
104         auto ports = MessagePort::entanglePorts(context, WTFMove(channels));
105         workerObject->dispatchEvent(MessageEvent::create(WTFMove(ports), WTFMove(message)));
106     });
107 }
108
109 void WorkerMessagingProxy::postMessageToWorkerGlobalScope(Ref<SerializedScriptValue>&& message, std::unique_ptr<MessagePortChannelArray>&& channels)
110 {
111     if (m_askedToTerminate)
112         return;
113
114     ScriptExecutionContext::Task task([channels = WTFMove(channels), message = WTFMove(message)] (ScriptExecutionContext& scriptContext) mutable {
115         ASSERT_WITH_SECURITY_IMPLICATION(scriptContext.isWorkerGlobalScope());
116         auto& context = static_cast<DedicatedWorkerGlobalScope&>(scriptContext);
117         auto ports = MessagePort::entanglePorts(scriptContext, WTFMove(channels));
118         context.dispatchEvent(MessageEvent::create(WTFMove(ports), WTFMove(message)));
119         context.thread().workerObjectProxy().confirmMessageFromWorkerObject(context.hasPendingActivity());
120     });
121
122     if (m_workerThread) {
123         ++m_unconfirmedMessageCount;
124         m_workerThread->runLoop().postTask(WTFMove(task));
125     } else
126         m_queuedEarlyTasks.append(std::make_unique<ScriptExecutionContext::Task>(WTFMove(task)));
127 }
128
129 void WorkerMessagingProxy::postTaskToLoader(ScriptExecutionContext::Task&& task)
130 {
131     // FIXME: In case of nested workers, this should go directly to the root Document context.
132     ASSERT(m_scriptExecutionContext->isDocument());
133     m_scriptExecutionContext->postTask(WTFMove(task));
134 }
135
136 bool WorkerMessagingProxy::postTaskForModeToWorkerGlobalScope(ScriptExecutionContext::Task&& task, const String& mode)
137 {
138     if (m_askedToTerminate)
139         return false;
140
141     ASSERT(m_workerThread);
142     m_workerThread->runLoop().postTaskForMode(WTFMove(task), mode);
143     return true;
144 }
145
146 void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL)
147 {
148     m_scriptExecutionContext->postTask([this, errorMessage = errorMessage.isolatedCopy(), sourceURL = sourceURL.isolatedCopy(), lineNumber, columnNumber] (ScriptExecutionContext& context) {
149         Worker* workerObject = this->workerObject();
150         if (!workerObject)
151             return;
152
153         // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated.
154         // 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.
155
156         bool errorHandled = !workerObject->dispatchEvent(ErrorEvent::create(errorMessage, sourceURL, lineNumber, columnNumber, { }));
157         if (!errorHandled)
158             context.reportException(errorMessage, lineNumber, columnNumber, sourceURL, nullptr, nullptr);
159     });
160 }
161
162 void WorkerMessagingProxy::postMessageToPageInspector(const String& message)
163 {
164     RunLoop::main().dispatch([this, protectedThis = makeRef(*this), message = message.isolatedCopy()] {
165         if (!m_mayBeDestroyed)
166             m_inspectorProxy->sendMessageFromWorkerToFrontend(message);
167     });
168 }
169
170 void WorkerMessagingProxy::workerThreadCreated(DedicatedWorkerThread& workerThread)
171 {
172     m_workerThread = &workerThread;
173
174     if (m_askedToTerminate) {
175         // Worker.terminate() could be called from JS before the thread was created.
176         m_workerThread->stop();
177     } else {
178         ASSERT(!m_unconfirmedMessageCount);
179         m_unconfirmedMessageCount = m_queuedEarlyTasks.size();
180         m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
181
182         auto queuedEarlyTasks = WTFMove(m_queuedEarlyTasks);
183         for (auto& task : queuedEarlyTasks)
184             m_workerThread->runLoop().postTask(WTFMove(*task));
185     }
186 }
187
188 void WorkerMessagingProxy::workerObjectDestroyed()
189 {
190     m_workerObject = nullptr;
191     m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) {
192         m_mayBeDestroyed = true;
193         if (m_workerThread)
194             terminateWorkerGlobalScope();
195         else
196             workerGlobalScopeDestroyedInternal();
197     });
198 }
199
200 void WorkerMessagingProxy::notifyNetworkStateChange(bool isOnline)
201 {
202     if (m_askedToTerminate)
203         return;
204
205     if (!m_workerThread)
206         return;
207
208     m_workerThread->runLoop().postTask([isOnline] (ScriptExecutionContext& context) {
209         downcast<WorkerGlobalScope>(context).dispatchEvent(Event::create(isOnline ? eventNames().onlineEvent : eventNames().offlineEvent, false, false));
210     });
211 }
212
213 void WorkerMessagingProxy::workerGlobalScopeDestroyed()
214 {
215     m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) {
216         workerGlobalScopeDestroyedInternal();
217     });
218 }
219
220 void WorkerMessagingProxy::workerGlobalScopeClosed()
221 {
222     m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) {
223         terminateWorkerGlobalScope();
224     });
225 }
226
227 void WorkerMessagingProxy::workerGlobalScopeDestroyedInternal()
228 {
229     // This is always the last task to be performed, so the proxy is not needed for communication
230     // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
231     m_askedToTerminate = true;
232     m_workerThread = nullptr;
233
234     m_inspectorProxy->workerTerminated();
235
236     // This balances the original ref in construction.
237     if (m_mayBeDestroyed)
238         deref();
239 }
240
241 void WorkerMessagingProxy::terminateWorkerGlobalScope()
242 {
243     if (m_askedToTerminate)
244         return;
245     m_askedToTerminate = true;
246
247     m_inspectorProxy->workerTerminated();
248
249     if (m_workerThread)
250         m_workerThread->stop();
251 }
252
253 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
254 {
255     m_scriptExecutionContext->postTask([this, hasPendingActivity] (ScriptExecutionContext&) {
256         reportPendingActivityInternal(true, hasPendingActivity);
257     });
258 }
259
260 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
261 {
262     m_scriptExecutionContext->postTask([this, hasPendingActivity] (ScriptExecutionContext&) {
263         reportPendingActivityInternal(false, hasPendingActivity);
264     });
265 }
266
267 void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity)
268 {
269     if (confirmingMessage && !m_askedToTerminate) {
270         ASSERT(m_unconfirmedMessageCount);
271         --m_unconfirmedMessageCount;
272     }
273
274     m_workerThreadHadPendingActivity = hasPendingActivity;
275 }
276
277 bool WorkerMessagingProxy::hasPendingActivity() const
278 {
279     return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
280 }
281
282 } // namespace WebCore