• 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 "DedicatedWorkerThread.h"
37 #include "ErrorEvent.h"
38 #include "Frame.h"
39 #include "FrameLoaderClient.h"
40 #include "GenericWorkerTask.h"
41 #include "MessageEvent.h"
42 #include "MessagePort.h"
43 #include "MessagePortChannel.h"
44 #include "ScriptExecutionContext.h"
45 #include "Worker.h"
46 #include "WorkerContext.h"
47 #include "WorkerContextExecutionProxy.h"
48 #include "WorkerMessagingProxy.h"
49 #include <wtf/Threading.h>
50 
51 #include "FrameLoaderClientImpl.h"
52 #include "PlatformMessagePortChannel.h"
53 #include "WebFrameClient.h"
54 #include "WebFrameImpl.h"
55 #include "WebKit.h"
56 #include "WebKitClient.h"
57 #include "WebMessagePortChannel.h"
58 #include "WebString.h"
59 #include "WebURL.h"
60 #include "WebViewImpl.h"
61 #include "WebWorker.h"
62 #include "WebWorkerImpl.h"
63 
64 using namespace WebCore;
65 
66 namespace WebKit {
67 
68 // When WebKit creates a WorkerContextProxy object, we check if we're in the
69 // renderer or worker process.  If the latter, then we just use
70 // WorkerMessagingProxy.
71 //
72 // If we're in the renderer process, then we need use the glue provided
73 // WebWorker object to talk to the worker process over IPC.  The worker process
74 // talks to Worker* using WorkerObjectProxy, which we implement on
75 // WebWorkerClientImpl.
76 //
77 // Note that if we're running each worker in a separate process, then nested
78 // workers end up using the same codepath as the renderer process.
79 
80 // static
createWorkerContextProxy(Worker * worker)81 WorkerContextProxy* WebWorkerClientImpl::createWorkerContextProxy(Worker* worker)
82 {
83     // Special behavior for multiple workers per process.
84     // FIXME: v8 doesn't support more than one workers per process.
85     // if (!worker->scriptExecutionContext()->isDocument())
86     //     return new WorkerMessagingProxy(worker);
87 
88     WebWorker* webWorker = 0;
89     WebWorkerClientImpl* proxy = new WebWorkerClientImpl(worker);
90 
91     if (worker->scriptExecutionContext()->isDocument()) {
92         Document* document = static_cast<Document*>(
93             worker->scriptExecutionContext());
94         WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame());
95         webWorker = webFrame->client()->createWorker(webFrame, proxy);
96     } else {
97         WorkerContextExecutionProxy* currentContext =
98         WorkerContextExecutionProxy::retrieve();
99         if (!currentContext) {
100             ASSERT_NOT_REACHED();
101             return 0;
102         }
103 
104         DedicatedWorkerThread* thread =
105         static_cast<DedicatedWorkerThread*>(currentContext->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 handled = false;
248     handled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
249                                                          sourceURL,
250                                                          lineNumber));
251     if (!handled)
252         m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL);
253 }
254 
postConsoleMessageToWorkerObject(int destinationId,int sourceId,int messageType,int messageLevel,const WebString & message,int lineNumber,const WebString & sourceURL)255 void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int destinationId,
256                                                            int sourceId,
257                                                            int messageType,
258                                                            int messageLevel,
259                                                            const WebString& message,
260                                                            int lineNumber,
261                                                            const WebString& sourceURL)
262 {
263     if (currentThread() != m_workerThreadId) {
264         m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageToWorkerObjectTask,
265                                                               this,
266                                                               destinationId,
267                                                               sourceId,
268                                                               messageType,
269                                                               messageLevel,
270                                                               String(message),
271                                                               lineNumber,
272                                                               String(sourceURL)));
273         return;
274     }
275 
276     m_scriptExecutionContext->addMessage(static_cast<MessageDestination>(destinationId),
277                                          static_cast<MessageSource>(sourceId),
278                                          static_cast<MessageType>(messageType),
279                                          static_cast<MessageLevel>(messageLevel),
280                                          String(message), lineNumber,
281                                          String(sourceURL));
282 }
283 
confirmMessageFromWorkerObject(bool hasPendingActivity)284 void WebWorkerClientImpl::confirmMessageFromWorkerObject(bool hasPendingActivity)
285 {
286     // unconfirmed_message_count_ can only be updated on the thread where it's
287     // accessed.  Otherwise there are race conditions with v8's garbage
288     // collection.
289     m_scriptExecutionContext->postTask(createCallbackTask(&confirmMessageFromWorkerObjectTask,
290                                                           this));
291 }
292 
reportPendingActivity(bool hasPendingActivity)293 void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity)
294 {
295     // See above comment in confirmMessageFromWorkerObject.
296     m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask,
297                                                           this,
298                                                           hasPendingActivity));
299 }
300 
workerContextDestroyed()301 void WebWorkerClientImpl::workerContextDestroyed()
302 {
303 }
304 
workerContextClosed()305 void WebWorkerClientImpl::workerContextClosed()
306 {
307 }
308 
startWorkerContextTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & scriptURL,const String & userAgent,const String & sourceCode)309 void WebWorkerClientImpl::startWorkerContextTask(ScriptExecutionContext* context,
310                                                  WebWorkerClientImpl* thisPtr,
311                                                  const String& scriptURL,
312                                                  const String& userAgent,
313                                                  const String& sourceCode)
314 {
315     thisPtr->m_webWorker->startWorkerContext(KURL(ParsedURLString, scriptURL),
316                                              userAgent, sourceCode);
317 }
318 
terminateWorkerContextTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr)319 void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context,
320                                                      WebWorkerClientImpl* thisPtr)
321 {
322     thisPtr->m_webWorker->terminateWorkerContext();
323 }
324 
postMessageToWorkerContextTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & message,PassOwnPtr<MessagePortChannelArray> channels)325 void WebWorkerClientImpl::postMessageToWorkerContextTask(ScriptExecutionContext* context,
326                                                          WebWorkerClientImpl* thisPtr,
327                                                          const String& message,
328                                                          PassOwnPtr<MessagePortChannelArray> channels)
329 {
330     WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
331 
332     for (size_t i = 0; i < webChannels.size(); ++i) {
333         webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
334         webChannels[i]->setClient(0);
335     }
336 
337     thisPtr->m_webWorker->postMessageToWorkerContext(message, webChannels);
338 }
339 
workerObjectDestroyedTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr)340 void WebWorkerClientImpl::workerObjectDestroyedTask(ScriptExecutionContext* context,
341                                                     WebWorkerClientImpl* thisPtr)
342 {
343     if (thisPtr->m_worker) // Check we haven't alread called this.
344         thisPtr->m_webWorker->workerObjectDestroyed();
345     delete thisPtr;
346 }
347 
postMessageToWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & message,PassOwnPtr<MessagePortChannelArray> channels)348 void WebWorkerClientImpl::postMessageToWorkerObjectTask(
349                                                         ScriptExecutionContext* context,
350                                                         WebWorkerClientImpl* thisPtr,
351                                                         const String& message,
352                                                         PassOwnPtr<MessagePortChannelArray> channels)
353 {
354 
355     if (thisPtr->m_worker) {
356         OwnPtr<MessagePortArray> ports =
357             MessagePort::entanglePorts(*context, channels.release());
358         RefPtr<SerializedScriptValue> serializedMessage =
359             SerializedScriptValue::createFromWire(message);
360         thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(),
361                                                               serializedMessage.release()));
362     }
363 }
364 
postExceptionToWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,const String & errorMessage,int lineNumber,const String & sourceURL)365 void WebWorkerClientImpl::postExceptionToWorkerObjectTask(
366                                                           ScriptExecutionContext* context,
367                                                           WebWorkerClientImpl* thisPtr,
368                                                           const String& errorMessage,
369                                                           int lineNumber,
370                                                           const String& sourceURL)
371 {
372     bool handled = false;
373     if (thisPtr->m_worker)
374         handled = thisPtr->m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
375                                                                       sourceURL,
376                                                                       lineNumber));
377     if (!handled)
378         thisPtr->m_scriptExecutionContext->reportException(errorMessage,
379                                                            lineNumber,
380                                                            sourceURL);
381 }
382 
postConsoleMessageToWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,int destinationId,int sourceId,int messageType,int messageLevel,const String & message,int lineNumber,const String & sourceURL)383 void WebWorkerClientImpl::postConsoleMessageToWorkerObjectTask(ScriptExecutionContext* context,
384                                                                WebWorkerClientImpl* thisPtr,
385                                                                int destinationId,
386                                                                int sourceId,
387                                                                int messageType,
388                                                                int messageLevel,
389                                                                const String& message,
390                                                                int lineNumber,
391                                                                const String& sourceURL)
392 {
393     thisPtr->m_scriptExecutionContext->addMessage(static_cast<MessageDestination>(destinationId),
394                                                   static_cast<MessageSource>(sourceId),
395                                                   static_cast<MessageType>(messageType),
396                                                   static_cast<MessageLevel>(messageLevel),
397                                                   message, lineNumber,
398                                                   sourceURL);
399 }
400 
confirmMessageFromWorkerObjectTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr)401 void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context,
402                                                              WebWorkerClientImpl* thisPtr)
403 {
404     thisPtr->m_unconfirmedMessageCount--;
405 }
406 
reportPendingActivityTask(ScriptExecutionContext * context,WebWorkerClientImpl * thisPtr,bool hasPendingActivity)407 void WebWorkerClientImpl::reportPendingActivityTask(ScriptExecutionContext* context,
408                                                     WebWorkerClientImpl* thisPtr,
409                                                     bool hasPendingActivity)
410 {
411     thisPtr->m_workerContextHadPendingActivity = hasPendingActivity;
412 }
413 
414 } // namespace WebKit
415 
416 #endif
417