OLD | NEW |
| (Empty) |
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 | |
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 | |
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 | |
125 WebWorkerClientImpl::~WebWorkerClientImpl() | |
126 { | |
127 } | |
128 | |
129 void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker) | |
130 { | |
131 m_webWorker = webWorker; | |
132 } | |
133 | |
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 | |
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 | |
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->toString(), | |
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->toString(), webChannels); | |
188 } | |
189 | |
190 bool WebWorkerClientImpl::hasPendingActivity() const | |
191 { | |
192 return !m_askedToTerminate | |
193 && (m_unconfirmedMessageCount || m_workerContextHadPendingActivity); | |
194 } | |
195 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
301 void WebWorkerClientImpl::workerContextDestroyed() | |
302 { | |
303 } | |
304 | |
305 void WebWorkerClientImpl::workerContextClosed() | |
306 { | |
307 } | |
308 | |
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 | |
319 void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context, | |
320 WebWorkerClientImpl* thisPtr) | |
321 { | |
322 thisPtr->m_webWorker->terminateWorkerContext(); | |
323 } | |
324 | |
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 | |
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 | |
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::create(message); | |
360 thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(), | |
361 serializedMessage.release())); | |
362 } | |
363 } | |
364 | |
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 | |
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 | |
401 void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context, | |
402 WebWorkerClientImpl* thisPtr) | |
403 { | |
404 thisPtr->m_unconfirmedMessageCount--; | |
405 } | |
406 | |
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 | |
OLD | NEW |