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