• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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