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
66using namespace WebCore;
67
68namespace 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
83WorkerContextProxy* 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
115WebWorkerClientImpl::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
125WebWorkerClientImpl::~WebWorkerClientImpl()
126{
127}
128
129void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker)
130{
131    m_webWorker = webWorker;
132}
133
134void 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
153void 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
165void 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
190bool WebWorkerClientImpl::hasPendingActivity() const
191{
192    return !m_askedToTerminate
193           && (m_unconfirmedMessageCount || m_workerContextHadPendingActivity);
194}
195
196void 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
208void 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
234void 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
254void 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
281void 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
291void 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
300void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity)
301{
302    // See above comment in confirmMessageFromWorkerObject.
303    m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask,
304                                                          this,
305                                                          hasPendingActivity));
306}
307
308void WebWorkerClientImpl::workerContextDestroyed()
309{
310}
311
312void WebWorkerClientImpl::workerContextClosed()
313{
314}
315
316void 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
326void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context,
327                                                     WebWorkerClientImpl* thisPtr)
328{
329    thisPtr->m_webWorker->terminateWorkerContext();
330}
331
332void 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
347void 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
355void 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
372void 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
388void 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
403void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context,
404                                                             WebWorkerClientImpl* thisPtr)
405{
406    thisPtr->m_unconfirmedMessageCount--;
407}
408
409void 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