1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "WebWorkerClientImpl.h"
33
34 #if ENABLE(WORKERS)
35
36 #include "CrossThreadTask.h"
37 #include "DedicatedWorkerThread.h"
38 #include "ErrorEvent.h"
39 #include "Frame.h"
40 #include "FrameLoaderClient.h"
41 #include "MessageEvent.h"
42 #include "MessagePort.h"
43 #include "MessagePortChannel.h"
44 #include "ScriptCallStack.h"
45 #include "ScriptExecutionContext.h"
46 #include "Worker.h"
47 #include "WorkerContext.h"
48 #include "WorkerContextExecutionProxy.h"
49 #include "WorkerScriptController.h"
50 #include "WorkerMessagingProxy.h"
51 #include <wtf/Threading.h>
52
53 #include "FrameLoaderClientImpl.h"
54 #include "PlatformMessagePortChannel.h"
55 #include "WebFrameClient.h"
56 #include "WebFrameImpl.h"
57 #include "WebKit.h"
58 #include "WebKitClient.h"
59 #include "WebMessagePortChannel.h"
60 #include "WebString.h"
61 #include "WebURL.h"
62 #include "WebViewImpl.h"
63 #include "WebWorker.h"
64 #include "WebWorkerImpl.h"
65
66 using namespace WebCore;
67
68 namespace WebKit {
69
70 // When WebKit creates a WorkerContextProxy object, we check if we're in the
71 // renderer or worker process. If the latter, then we just use
72 // WorkerMessagingProxy.
73 //
74 // If we're in the renderer process, then we need use the glue provided
75 // WebWorker object to talk to the worker process over IPC. The worker process
76 // talks to Worker* using WorkerObjectProxy, which we implement on
77 // WebWorkerClientImpl.
78 //
79 // Note that if we're running each worker in a separate process, then nested
80 // workers end up using the same codepath as the renderer process.
81
82 // static
createWorkerContextProxy(Worker * worker)83 WorkerContextProxy* WebWorkerClientImpl::createWorkerContextProxy(Worker* worker)
84 {
85 // Special behavior for multiple workers per process.
86 // FIXME: v8 doesn't support more than one workers per process.
87 // if (!worker->scriptExecutionContext()->isDocument())
88 // return new WorkerMessagingProxy(worker);
89
90 WebWorker* webWorker = 0;
91 WebWorkerClientImpl* proxy = new WebWorkerClientImpl(worker);
92
93 if (worker->scriptExecutionContext()->isDocument()) {
94 Document* document = static_cast<Document*>(
95 worker->scriptExecutionContext());
96 WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame());
97 webWorker = webFrame->client()->createWorker(webFrame, proxy);
98 } else {
99 WorkerScriptController* controller = WorkerScriptController::controllerForContext();
100 if (!controller) {
101 ASSERT_NOT_REACHED();
102 return 0;
103 }
104
105 DedicatedWorkerThread* thread = static_cast<DedicatedWorkerThread*>(controller->workerContext()->thread());
106 WorkerObjectProxy* workerObjectProxy = &thread->workerObjectProxy();
107 WebWorkerImpl* impl = reinterpret_cast<WebWorkerImpl*>(workerObjectProxy);
108 webWorker = impl->client()->createWorker(proxy);
109 }
110
111 proxy->setWebWorker(webWorker);
112 return proxy;
113 }
114
WebWorkerClientImpl(Worker * worker)115 WebWorkerClientImpl::WebWorkerClientImpl(Worker* worker)
116 : m_scriptExecutionContext(worker->scriptExecutionContext())
117 , m_worker(worker)
118 , m_askedToTerminate(false)
119 , m_unconfirmedMessageCount(0)
120 , m_workerContextHadPendingActivity(false)
121 , m_workerThreadId(currentThread())
122 {
123 }
124
~WebWorkerClientImpl()125 WebWorkerClientImpl::~WebWorkerClientImpl()
126 {
127 }
128
setWebWorker(WebWorker * webWorker)129 void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker)
130 {
131 m_webWorker = webWorker;
132 }
133
startWorkerContext(const KURL & scriptURL,const String & userAgent,const String & sourceCode)134 void WebWorkerClientImpl::startWorkerContext(const KURL& scriptURL,
135 const String& userAgent,
136 const String& sourceCode)
137 {
138 // Worker.terminate() could be called from JS before the context is started.
139 if (m_askedToTerminate)
140 return;
141 if (!isMainThread()) {
142 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(
143 &startWorkerContextTask,
144 this,
145 scriptURL.string(),
146 userAgent,
147 sourceCode));
148 return;
149 }
150 m_webWorker->startWorkerContext(scriptURL, userAgent, sourceCode);
151 }
152
terminateWorkerContext()153 void WebWorkerClientImpl::terminateWorkerContext()
154 {
155 if (m_askedToTerminate)
156 return;
157 m_askedToTerminate = true;
158 if (!isMainThread()) {
159 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&terminateWorkerContextTask, this));
160 return;
161 }
162 m_webWorker->terminateWorkerContext();
163 }
164
postMessageToWorkerContext(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)165 void WebWorkerClientImpl::postMessageToWorkerContext(
166 PassRefPtr<SerializedScriptValue> message,
167 PassOwnPtr<MessagePortChannelArray> channels)
168 {
169 // Worker.terminate() could be called from JS before the context is started.
170 if (m_askedToTerminate)
171 return;
172 ++m_unconfirmedMessageCount;
173 if (!isMainThread()) {
174 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageToWorkerContextTask,
175 this,
176 message->toWireString(),
177 channels));
178 return;
179 }
180 WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
181 for (size_t i = 0; i < webChannels.size(); ++i) {
182 WebMessagePortChannel* webchannel =
183 (*channels)[i]->channel()->webChannelRelease();
184 webchannel->setClient(0);
185 webChannels[i] = webchannel;
186 }
187 m_webWorker->postMessageToWorkerContext(message->toWireString(), webChannels);
188 }
189
hasPendingActivity() const190 bool WebWorkerClientImpl::hasPendingActivity() const
191 {
192 return !m_askedToTerminate
193 && (m_unconfirmedMessageCount || m_workerContextHadPendingActivity);
194 }
195
workerObjectDestroyed()196 void WebWorkerClientImpl::workerObjectDestroyed()
197 {
198 if (isMainThread()) {
199 m_webWorker->workerObjectDestroyed();
200 m_worker = 0;
201 }
202 // Even if this is called on the main thread, there could be a queued task for
203 // this object, so don't delete it right away.
204 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerObjectDestroyedTask,
205 this));
206 }
207
postMessageToWorkerObject(const WebString & message,const WebMessagePortChannelArray & channels)208 void WebWorkerClientImpl::postMessageToWorkerObject(const WebString& message,
209 const WebMessagePortChannelArray& channels)
210 {
211 OwnPtr<MessagePortChannelArray> channels2;
212 if (channels.size()) {
213 channels2 = new MessagePortChannelArray(channels.size());
214 for (size_t i = 0; i < channels.size(); ++i) {
215 RefPtr<PlatformMessagePortChannel> platform_channel =
216 PlatformMessagePortChannel::create(channels[i]);
217 channels[i]->setClient(platform_channel.get());
218 (*channels2)[i] = MessagePortChannel::create(platform_channel);
219 }
220 }
221
222 if (currentThread() != m_workerThreadId) {
223 m_scriptExecutionContext->postTask(createCallbackTask(&postMessageToWorkerObjectTask,
224 this,
225 String(message),
226 channels2.release()));
227 return;
228 }
229
230 postMessageToWorkerObjectTask(m_scriptExecutionContext.get(), this,
231 message, channels2.release());
232 }
233
postExceptionToWorkerObject(const WebString & errorMessage,int lineNumber,const WebString & sourceURL)234 void WebWorkerClientImpl::postExceptionToWorkerObject(const WebString& errorMessage,
235 int lineNumber,
236 const WebString& sourceURL)
237 {
238 if (currentThread() != m_workerThreadId) {
239 m_scriptExecutionContext->postTask(createCallbackTask(&postExceptionToWorkerObjectTask,
240 this,
241 String(errorMessage),
242 lineNumber,
243 String(sourceURL)));
244 return;
245 }
246
247 bool unhandled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
248 sourceURL,
249 lineNumber));
250 if (unhandled)
251 m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0);
252 }
253
postConsoleMessageToWorkerObject(int destination,int sourceId,int messageType,int messageLevel,const WebString & message,int lineNumber,const WebString & sourceURL)254 void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int destination,
255 int sourceId,
256 int messageType,
257 int messageLevel,
258 const WebString& message,
259 int lineNumber,
260 const WebString& sourceURL)
261 {
262 if (currentThread() != m_workerThreadId) {
263 m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageToWorkerObjectTask,
264 this,
265 sourceId,
266 messageType,
267 messageLevel,
268 String(message),
269 lineNumber,
270 String(sourceURL)));
271 return;
272 }
273
274 m_scriptExecutionContext->addMessage(static_cast<MessageSource>(sourceId),
275 static_cast<MessageType>(messageType),
276 static_cast<MessageLevel>(messageLevel),
277 String(message), lineNumber,
278 String(sourceURL), 0);
279 }
280
postConsoleMessageToWorkerObject(int sourceId,int messageType,int messageLevel,const WebString & message,int lineNumber,const WebString & sourceURL)281 void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int sourceId,
282 int messageType,
283 int messageLevel,
284 const WebString& message,
285 int lineNumber,
286 const WebString& sourceURL)
287 {
288 postConsoleMessageToWorkerObject(0, sourceId, messageType, messageLevel, message, lineNumber, sourceURL);
289 }
290
confirmMessageFromWorkerObject(bool hasPendingActivity)291 void WebWorkerClientImpl::confirmMessageFromWorkerObject(bool hasPendingActivity)
292 {
293 // unconfirmed_message_count_ can only be updated on the thread where it's
294 // accessed. Otherwise there are race conditions with v8's garbage
295 // collection.
296 m_scriptExecutionContext->postTask(createCallbackTask(&confirmMessageFromWorkerObjectTask,
297 this));
298 }
299
reportPendingActivity(bool hasPendingActivity)300 void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity)
301 {
302 // See above comment in confirmMessageFromWorkerObject.
303 m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask,
304 this,
305 hasPendingActivity));
306 }
307
workerContextDestroyed()308 void WebWorkerClientImpl::workerContextDestroyed()
309 {
310 }
311
workerContextClosed()312 void WebWorkerClientImpl::workerContextClosed()
313 {
314 }
315
startWorkerContextTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & scriptURL,const String & userAgent,const String & sourceCode)316 void WebWorkerClientImpl::startWorkerContextTask(ScriptExecutionContext* context,
317 WebWorkerClientImpl* thisPtr,
318 const String& scriptURL,
319 const String& userAgent,
320 const String& sourceCode)
321 {
322 thisPtr->m_webWorker->startWorkerContext(KURL(ParsedURLString, scriptURL),
323 userAgent, sourceCode);
324 }
325
terminateWorkerContextTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr)326 void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context,
327 WebWorkerClientImpl* thisPtr)
328 {
329 thisPtr->m_webWorker->terminateWorkerContext();
330 }
331
postMessageToWorkerContextTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & message,PassOwnPtr<MessagePortChannelArray> channels)332 void WebWorkerClientImpl::postMessageToWorkerContextTask(ScriptExecutionContext* context,
333 WebWorkerClientImpl* thisPtr,
334 const String& message,
335 PassOwnPtr<MessagePortChannelArray> channels)
336 {
337 WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
338
339 for (size_t i = 0; i < webChannels.size(); ++i) {
340 webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
341 webChannels[i]->setClient(0);
342 }
343
344 thisPtr->m_webWorker->postMessageToWorkerContext(message, webChannels);
345 }
346
workerObjectDestroyedTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr)347 void WebWorkerClientImpl::workerObjectDestroyedTask(ScriptExecutionContext* context,
348 WebWorkerClientImpl* thisPtr)
349 {
350 if (thisPtr->m_worker) // Check we haven't alread called this.
351 thisPtr->m_webWorker->workerObjectDestroyed();
352 delete thisPtr;
353 }
354
postMessageToWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & message,PassOwnPtr<MessagePortChannelArray> channels)355 void WebWorkerClientImpl::postMessageToWorkerObjectTask(
356 ScriptExecutionContext* context,
357 WebWorkerClientImpl* thisPtr,
358 const String& message,
359 PassOwnPtr<MessagePortChannelArray> channels)
360 {
361
362 if (thisPtr->m_worker) {
363 OwnPtr<MessagePortArray> ports =
364 MessagePort::entanglePorts(*context, channels);
365 RefPtr<SerializedScriptValue> serializedMessage =
366 SerializedScriptValue::createFromWire(message);
367 thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(),
368 serializedMessage.release()));
369 }
370 }
371
postExceptionToWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & errorMessage,int lineNumber,const String & sourceURL)372 void WebWorkerClientImpl::postExceptionToWorkerObjectTask(
373 ScriptExecutionContext* context,
374 WebWorkerClientImpl* thisPtr,
375 const String& errorMessage,
376 int lineNumber,
377 const String& sourceURL)
378 {
379 bool handled = false;
380 if (thisPtr->m_worker)
381 handled = thisPtr->m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
382 sourceURL,
383 lineNumber));
384 if (!handled)
385 thisPtr->m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0);
386 }
387
postConsoleMessageToWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,int sourceId,int messageType,int messageLevel,const String & message,int lineNumber,const String & sourceURL)388 void WebWorkerClientImpl::postConsoleMessageToWorkerObjectTask(ScriptExecutionContext* context,
389 WebWorkerClientImpl* thisPtr,
390 int sourceId,
391 int messageType,
392 int messageLevel,
393 const String& message,
394 int lineNumber,
395 const String& sourceURL)
396 {
397 thisPtr->m_scriptExecutionContext->addMessage(static_cast<MessageSource>(sourceId),
398 static_cast<MessageType>(messageType),
399 static_cast<MessageLevel>(messageLevel),
400 message, lineNumber, sourceURL, 0);
401 }
402
confirmMessageFromWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr)403 void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context,
404 WebWorkerClientImpl* thisPtr)
405 {
406 thisPtr->m_unconfirmedMessageCount--;
407 }
408
reportPendingActivityTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,bool hasPendingActivity)409 void WebWorkerClientImpl::reportPendingActivityTask(ScriptExecutionContext* context,
410 WebWorkerClientImpl* thisPtr,
411 bool hasPendingActivity)
412 {
413 thisPtr->m_workerContextHadPendingActivity = hasPendingActivity;
414 }
415
416 } // namespace WebKit
417
418 #endif
419