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
49namespace WebCore {
50
51WorkerGlobalScopeProxy& WorkerGlobalScopeProxy::create(Worker& worker)
52{
53 return *new WorkerMessagingProxy(worker);
54}
55
56WorkerMessagingProxy::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
68WorkerMessagingProxy::~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
75void 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
99void 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
111void 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
131void 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
138Ref<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
145bool 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
155void 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
172void 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
180void 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
189void 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
207void 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
219void 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
234void WorkerMessagingProxy::workerGlobalScopeDestroyed()
235{
236 m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) {
237 workerGlobalScopeDestroyedInternal();
238 });
239}
240
241void WorkerMessagingProxy::workerGlobalScopeClosed()
242{
243 m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) {
244 terminateWorkerGlobalScope();
245 });
246}
247
248void 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
262void 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
274void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
275{
276 m_scriptExecutionContext->postTask([this, hasPendingActivity] (ScriptExecutionContext&) {
277 reportPendingActivityInternal(true, hasPendingActivity);
278 });
279}
280
281void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
282{
283 m_scriptExecutionContext->postTask([this, hasPendingActivity] (ScriptExecutionContext&) {
284 reportPendingActivityInternal(false, hasPendingActivity);
285 });
286}
287
288void 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
298bool WorkerMessagingProxy::hasPendingActivity() const
299{
300 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
301}
302
303} // namespace WebCore
304