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
30 #include "core/workers/WorkerMessagingProxy.h"
31
32 #include "core/dom/CrossThreadTask.h"
33 #include "core/dom/Document.h"
34 #include "core/events/ErrorEvent.h"
35 #include "core/events/MessageEvent.h"
36 #include "core/frame/LocalDOMWindow.h"
37 #include "core/frame/csp/ContentSecurityPolicy.h"
38 #include "core/inspector/InspectorInstrumentation.h"
39 #include "core/inspector/ScriptCallStack.h"
40 #include "core/inspector/WorkerDebuggerAgent.h"
41 #include "core/inspector/WorkerInspectorController.h"
42 #include "core/loader/DocumentLoadTiming.h"
43 #include "core/loader/DocumentLoader.h"
44 #include "core/workers/DedicatedWorkerGlobalScope.h"
45 #include "core/workers/DedicatedWorkerThread.h"
46 #include "core/workers/Worker.h"
47 #include "core/workers/WorkerClients.h"
48 #include "core/workers/WorkerObjectProxy.h"
49 #include "core/workers/WorkerThreadStartupData.h"
50 #include "platform/NotImplemented.h"
51 #include "platform/heap/Handle.h"
52 #include "wtf/Functional.h"
53 #include "wtf/MainThread.h"
54
55 namespace WebCore {
56
57 class MessageWorkerGlobalScopeTask : public ExecutionContextTask {
58 public:
create(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)59 static PassOwnPtr<MessageWorkerGlobalScopeTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
60 {
61 return adoptPtr(new MessageWorkerGlobalScopeTask(message, channels));
62 }
63
64 private:
MessageWorkerGlobalScopeTask(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)65 MessageWorkerGlobalScopeTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
66 : m_message(message)
67 , m_channels(channels)
68 {
69 }
70
performTask(ExecutionContext * scriptContext)71 virtual void performTask(ExecutionContext* scriptContext)
72 {
73 ASSERT_WITH_SECURITY_IMPLICATION(scriptContext->isWorkerGlobalScope());
74 DedicatedWorkerGlobalScope* context = static_cast<DedicatedWorkerGlobalScope*>(scriptContext);
75 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release());
76 context->dispatchEvent(MessageEvent::create(ports.release(), m_message));
77 context->thread()->workerObjectProxy().confirmMessageFromWorkerObject(context->hasPendingActivity());
78 }
79
80 private:
81 RefPtr<SerializedScriptValue> m_message;
82 OwnPtr<MessagePortChannelArray> m_channels;
83 };
84
WorkerMessagingProxy(Worker * workerObject,PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)85 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)
86 : m_executionContext(workerObject->executionContext())
87 , m_workerObjectProxy(WorkerObjectProxy::create(m_executionContext.get(), this))
88 , m_workerObject(workerObject)
89 , m_mayBeDestroyed(false)
90 , m_unconfirmedMessageCount(0)
91 , m_workerThreadHadPendingActivity(false)
92 , m_askedToTerminate(false)
93 , m_pageInspector(0)
94 , m_workerClients(workerClients)
95 {
96 ASSERT(m_workerObject);
97 ASSERT((m_executionContext->isDocument() && isMainThread())
98 || (m_executionContext->isWorkerGlobalScope() && toWorkerGlobalScope(m_executionContext.get())->thread()->isCurrentThread()));
99 }
100
~WorkerMessagingProxy()101 WorkerMessagingProxy::~WorkerMessagingProxy()
102 {
103 ASSERT(!m_workerObject);
104 ASSERT((m_executionContext->isDocument() && isMainThread())
105 || (m_executionContext->isWorkerGlobalScope() && toWorkerGlobalScope(m_executionContext.get())->thread()->isCurrentThread()));
106 }
107
startWorkerGlobalScope(const KURL & scriptURL,const String & userAgent,const String & sourceCode,WorkerThreadStartMode startMode)108 void WorkerMessagingProxy::startWorkerGlobalScope(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode)
109 {
110 // FIXME: This need to be revisited when we support nested worker one day
111 ASSERT(m_executionContext->isDocument());
112 Document* document = toDocument(m_executionContext.get());
113
114 OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(scriptURL, userAgent, sourceCode, startMode, document->contentSecurityPolicy()->deprecatedHeader(), document->contentSecurityPolicy()->deprecatedHeaderType(), m_workerClients.release());
115 double originTime = document->loader() ? document->loader()->timing()->referenceMonotonicTime() : monotonicallyIncreasingTime();
116
117 RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(*this, *m_workerObjectProxy.get(), originTime, startupData.release());
118 workerThreadCreated(thread);
119 thread->start();
120 InspectorInstrumentation::didStartWorkerGlobalScope(m_executionContext.get(), this, scriptURL);
121 }
122
postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)123 void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
124 {
125 if (!m_workerObject || m_askedToTerminate)
126 return;
127
128 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*m_executionContext.get(), channels);
129 m_workerObject->dispatchEvent(MessageEvent::create(ports.release(), message));
130 }
131
postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)132 void WorkerMessagingProxy::postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
133 {
134 if (m_askedToTerminate)
135 return;
136
137 if (m_workerThread) {
138 ++m_unconfirmedMessageCount;
139 m_workerThread->runLoop().postTask(MessageWorkerGlobalScopeTask::create(message, channels));
140 } else
141 m_queuedEarlyTasks.append(MessageWorkerGlobalScopeTask::create(message, channels));
142 }
143
postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task)144 bool WorkerMessagingProxy::postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task)
145 {
146 if (m_askedToTerminate)
147 return false;
148
149 ASSERT(m_workerThread);
150 m_workerThread->runLoop().postTask(task);
151 return true;
152 }
153
postTaskToLoader(PassOwnPtr<ExecutionContextTask> task)154 void WorkerMessagingProxy::postTaskToLoader(PassOwnPtr<ExecutionContextTask> task)
155 {
156 // FIXME: In case of nested workers, this should go directly to the root Document context.
157 ASSERT(m_executionContext->isDocument());
158 m_executionContext->postTask(task);
159 }
160
reportException(const String & errorMessage,int lineNumber,int columnNumber,const String & sourceURL)161 void WorkerMessagingProxy::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL)
162 {
163 if (!m_workerObject)
164 return;
165
166 // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated.
167 // 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.
168
169 RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, sourceURL, lineNumber, columnNumber, 0);
170 bool errorHandled = !m_workerObject->dispatchEvent(event);
171 if (!errorHandled)
172 m_executionContext->reportException(event, nullptr, NotSharableCrossOrigin);
173 }
174
reportConsoleMessage(MessageSource source,MessageLevel level,const String & message,int lineNumber,const String & sourceURL)175 void WorkerMessagingProxy::reportConsoleMessage(MessageSource source, MessageLevel level, const String& message, int lineNumber, const String& sourceURL)
176 {
177 if (m_askedToTerminate)
178 return;
179 m_executionContext->addConsoleMessage(source, level, message, sourceURL, lineNumber);
180 }
181
workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread)182 void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread)
183 {
184 m_workerThread = workerThread;
185
186 if (m_askedToTerminate) {
187 // Worker.terminate() could be called from JS before the thread was created.
188 m_workerThread->stop();
189 } else {
190 unsigned taskCount = m_queuedEarlyTasks.size();
191 ASSERT(!m_unconfirmedMessageCount);
192 m_unconfirmedMessageCount = taskCount;
193 m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity.
194
195 for (unsigned i = 0; i < taskCount; ++i)
196 m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i].release());
197 m_queuedEarlyTasks.clear();
198 }
199 }
200
workerObjectDestroyed()201 void WorkerMessagingProxy::workerObjectDestroyed()
202 {
203 m_workerObject = 0;
204 m_executionContext->postTask(createCallbackTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this)));
205 }
206
workerObjectDestroyedInternal(ExecutionContext *,WorkerMessagingProxy * proxy)207 void WorkerMessagingProxy::workerObjectDestroyedInternal(ExecutionContext*, WorkerMessagingProxy* proxy)
208 {
209 proxy->m_mayBeDestroyed = true;
210 if (proxy->m_workerThread)
211 proxy->terminateWorkerGlobalScope();
212 else
213 proxy->workerGlobalScopeDestroyed();
214 }
215
connectToWorkerGlobalScopeInspectorTask(ExecutionContext * context,bool)216 static void connectToWorkerGlobalScopeInspectorTask(ExecutionContext* context, bool)
217 {
218 toWorkerGlobalScope(context)->workerInspectorController()->connectFrontend();
219 }
220
connectToInspector(WorkerGlobalScopeProxy::PageInspector * pageInspector)221 void WorkerMessagingProxy::connectToInspector(WorkerGlobalScopeProxy::PageInspector* pageInspector)
222 {
223 if (m_askedToTerminate)
224 return;
225 ASSERT(!m_pageInspector);
226 m_pageInspector = pageInspector;
227 m_workerThread->runLoop().postDebuggerTask(createCallbackTask(connectToWorkerGlobalScopeInspectorTask, true));
228 }
229
disconnectFromWorkerGlobalScopeInspectorTask(ExecutionContext * context,bool)230 static void disconnectFromWorkerGlobalScopeInspectorTask(ExecutionContext* context, bool)
231 {
232 toWorkerGlobalScope(context)->workerInspectorController()->disconnectFrontend();
233 }
234
disconnectFromInspector()235 void WorkerMessagingProxy::disconnectFromInspector()
236 {
237 m_pageInspector = 0;
238 if (m_askedToTerminate)
239 return;
240 m_workerThread->runLoop().postDebuggerTask(createCallbackTask(disconnectFromWorkerGlobalScopeInspectorTask, true));
241 }
242
dispatchOnInspectorBackendTask(ExecutionContext * context,const String & message)243 static void dispatchOnInspectorBackendTask(ExecutionContext* context, const String& message)
244 {
245 toWorkerGlobalScope(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
246 }
247
sendMessageToInspector(const String & message)248 void WorkerMessagingProxy::sendMessageToInspector(const String& message)
249 {
250 if (m_askedToTerminate)
251 return;
252 m_workerThread->runLoop().postDebuggerTask(createCallbackTask(dispatchOnInspectorBackendTask, String(message)));
253 WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get());
254 }
255
workerGlobalScopeDestroyed()256 void WorkerMessagingProxy::workerGlobalScopeDestroyed()
257 {
258 // This method is always the last to be performed, so the proxy is not needed for communication
259 // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too.
260 m_askedToTerminate = true;
261 m_workerThread = nullptr;
262
263 InspectorInstrumentation::workerGlobalScopeTerminated(m_executionContext.get(), this);
264
265 if (m_mayBeDestroyed)
266 delete this;
267 }
268
terminateWorkerGlobalScope()269 void WorkerMessagingProxy::terminateWorkerGlobalScope()
270 {
271 if (m_askedToTerminate)
272 return;
273 m_askedToTerminate = true;
274
275 if (m_workerThread)
276 m_workerThread->stop();
277
278 InspectorInstrumentation::workerGlobalScopeTerminated(m_executionContext.get(), this);
279 }
280
postMessageToPageInspector(const String & message)281 void WorkerMessagingProxy::postMessageToPageInspector(const String& message)
282 {
283 if (m_pageInspector)
284 m_pageInspector->dispatchMessageFromWorker(message);
285 }
286
confirmMessageFromWorkerObject(bool hasPendingActivity)287 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
288 {
289 if (!m_askedToTerminate) {
290 ASSERT(m_unconfirmedMessageCount);
291 --m_unconfirmedMessageCount;
292 }
293 reportPendingActivity(hasPendingActivity);
294 }
295
reportPendingActivity(bool hasPendingActivity)296 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
297 {
298 m_workerThreadHadPendingActivity = hasPendingActivity;
299 }
300
hasPendingActivity() const301 bool WorkerMessagingProxy::hasPendingActivity() const
302 {
303 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
304 }
305
306 } // namespace WebCore
307