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 #undef LOG |
| 51 |
| 52 #include "PlatformMessagePortChannel.h" |
| 53 #include "WebFrameClient.h" |
| 54 #include "WebKit.h" |
| 55 #include "WebKitClient.h" |
| 56 #include "WebMessagePortChannel.h" |
| 57 #include "WebString.h" |
| 58 #include "WebURL.h" |
| 59 #include "WebWorker.h" |
| 60 #include "WebWorkerImpl.h" |
| 61 // FIXME: remove the includes below |
| 62 #include "webkit/glue/webframeloaderclient_impl.h" |
| 63 #include "webkit/glue/webframe_impl.h" |
| 64 #include "webkit/glue/webview_impl.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 |
| 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 WorkerContextExecutionProxy* currentContext = |
| 100 WorkerContextExecutionProxy::retrieve(); |
| 101 if (!currentContext) { |
| 102 ASSERT_NOT_REACHED(); |
| 103 return 0; |
| 104 } |
| 105 |
| 106 DedicatedWorkerThread* thread = |
| 107 static_cast<DedicatedWorkerThread*>(currentContext->workerContext()->thread()); |
| 108 WorkerObjectProxy* workerObjectProxy = &thread->workerObjectProxy(); |
| 109 WebWorkerImpl* impl = reinterpret_cast<WebWorkerImpl*>(workerObjectProxy); |
| 110 webWorker = impl->client()->createWorker(proxy); |
| 111 } |
| 112 |
| 113 proxy->setWebWorker(webWorker); |
| 114 return proxy; |
| 115 } |
| 116 |
| 117 WebWorkerClientImpl::WebWorkerClientImpl(Worker* worker) |
| 118 : m_scriptExecutionContext(worker->scriptExecutionContext()) |
| 119 , m_worker(worker) |
| 120 , m_askedToTerminate(false) |
| 121 , m_unconfirmedMessageCount(0) |
| 122 , m_workerContextHadPendingActivity(false) |
| 123 , m_workerThreadId(currentThread()) |
| 124 { |
| 125 } |
| 126 |
| 127 WebWorkerClientImpl::~WebWorkerClientImpl() |
| 128 { |
| 129 } |
| 130 |
| 131 void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker) |
| 132 { |
| 133 m_webWorker = webWorker; |
| 134 } |
| 135 |
| 136 void WebWorkerClientImpl::startWorkerContext(const KURL& scriptURL, |
| 137 const String& userAgent, |
| 138 const String& sourceCode) |
| 139 { |
| 140 // Worker.terminate() could be called from JS before the context is started. |
| 141 if (m_askedToTerminate) |
| 142 return; |
| 143 if (!isMainThread()) { |
| 144 WebWorkerImpl::dispatchTaskToMainThread(createCallbackTask( |
| 145 &startWorkerContextTask, |
| 146 this, |
| 147 scriptURL.string(), |
| 148 userAgent, |
| 149 sourceCode)); |
| 150 return; |
| 151 } |
| 152 m_webWorker->startWorkerContext(scriptURL, userAgent, sourceCode); |
| 153 } |
| 154 |
| 155 void WebWorkerClientImpl::terminateWorkerContext() |
| 156 { |
| 157 if (m_askedToTerminate) |
| 158 return; |
| 159 m_askedToTerminate = true; |
| 160 if (!isMainThread()) { |
| 161 WebWorkerImpl::dispatchTaskToMainThread(createCallbackTask(&terminateWorkerContextTask, this)); |
| 162 return; |
| 163 } |
| 164 m_webWorker->terminateWorkerContext(); |
| 165 } |
| 166 |
| 167 void WebWorkerClientImpl::postMessageToWorkerContext( |
| 168 PassRefPtr<SerializedScriptValue> message, |
| 169 PassOwnPtr<MessagePortChannelArray> channels) |
| 170 { |
| 171 // Worker.terminate() could be called from JS before the context is started. |
| 172 if (m_askedToTerminate) |
| 173 return; |
| 174 ++m_unconfirmedMessageCount; |
| 175 if (!isMainThread()) { |
| 176 WebWorkerImpl::dispatchTaskToMainThread(createCallbackTask(&postMessageToWorkerContextTask, |
| 177 this, |
| 178 message->toString(), |
| 179 channels)); |
| 180 return; |
| 181 } |
| 182 WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0); |
| 183 for (size_t i = 0; i < webChannels.size(); ++i) { |
| 184 WebMessagePortChannel* webchannel = |
| 185 (*channels)[i]->channel()->webChannelRelease(); |
| 186 webchannel->setClient(0); |
| 187 webChannels[i] = webchannel; |
| 188 } |
| 189 m_webWorker->postMessageToWorkerContext(message->toString(), webChannels); |
| 190 } |
| 191 |
| 192 bool WebWorkerClientImpl::hasPendingActivity() const |
| 193 { |
| 194 return !m_askedToTerminate |
| 195 && (m_unconfirmedMessageCount || m_workerContextHadPendingActivity); |
| 196 } |
| 197 |
| 198 void WebWorkerClientImpl::workerObjectDestroyed() |
| 199 { |
| 200 if (isMainThread()) { |
| 201 m_webWorker->workerObjectDestroyed(); |
| 202 m_worker = 0; |
| 203 } |
| 204 // Even if this is called on the main thread, there could be a queued task for |
| 205 // this object, so don't delete it right away. |
| 206 WebWorkerImpl::dispatchTaskToMainThread(createCallbackTask(&workerObjectDestroyedTask, |
| 207 this)); |
| 208 } |
| 209 |
| 210 void WebWorkerClientImpl::postMessageToWorkerObject(const WebString& message, |
| 211 const WebMessagePortChannelArray& channels) |
| 212 { |
| 213 OwnPtr<MessagePortChannelArray> channels2; |
| 214 if (channels.size()) { |
| 215 channels2 = new MessagePortChannelArray(channels.size()); |
| 216 for (size_t i = 0; i < channels.size(); ++i) { |
| 217 RefPtr<PlatformMessagePortChannel> platform_channel = |
| 218 PlatformMessagePortChannel::create(channels[i]); |
| 219 channels[i]->setClient(platform_channel.get()); |
| 220 (*channels2)[i] = MessagePortChannel::create(platform_channel); |
| 221 } |
| 222 } |
| 223 |
| 224 if (currentThread() != m_workerThreadId) { |
| 225 m_scriptExecutionContext->postTask(createCallbackTask(&postMessageToWorkerObjectTask, |
| 226 this, |
| 227 String(message), |
| 228 channels2.release())); |
| 229 return; |
| 230 } |
| 231 |
| 232 postMessageToWorkerObjectTask(m_scriptExecutionContext.get(), this, |
| 233 message, channels2.release()); |
| 234 } |
| 235 |
| 236 void WebWorkerClientImpl::postExceptionToWorkerObject(const WebString& errorMessage, |
| 237 int lineNumber, |
| 238 const WebString& sourceURL) |
| 239 { |
| 240 if (currentThread() != m_workerThreadId) { |
| 241 m_scriptExecutionContext->postTask(createCallbackTask(&postExceptionToWorkerObjectTask, |
| 242 this, |
| 243 String(errorMessage), |
| 244 lineNumber, |
| 245 String(sourceURL))); |
| 246 return; |
| 247 } |
| 248 |
| 249 bool handled = false; |
| 250 handled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage, |
| 251 sourceURL, |
| 252 lineNumber)); |
| 253 if (!handled) |
| 254 m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL); |
| 255 } |
| 256 |
| 257 void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int destinationId, |
| 258 int sourceId, |
| 259 int messageType, |
| 260 int messageLevel, |
| 261 const WebString& message, |
| 262 int lineNumber, |
| 263 const WebString& sourceURL) |
| 264 { |
| 265 if (currentThread() != m_workerThreadId) { |
| 266 m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageToWorkerObjectTask, |
| 267 this, |
| 268 destinationId, |
| 269 sourceId, |
| 270 messageType, |
| 271 messageLevel, |
| 272 String(message), |
| 273 lineNumber, |
| 274 String(sourceURL))); |
| 275 return; |
| 276 } |
| 277 |
| 278 m_scriptExecutionContext->addMessage(static_cast<MessageDestination>(destinationId), |
| 279 static_cast<MessageSource>(sourceId), |
| 280 static_cast<MessageType>(messageType), |
| 281 static_cast<MessageLevel>(messageLevel), |
| 282 String(message), lineNumber, |
| 283 String(sourceURL)); |
| 284 } |
| 285 |
| 286 void WebWorkerClientImpl::confirmMessageFromWorkerObject(bool hasPendingActivity) |
| 287 { |
| 288 // unconfirmed_message_count_ can only be updated on the thread where it's |
| 289 // accessed. Otherwise there are race conditions with v8's garbage |
| 290 // collection. |
| 291 m_scriptExecutionContext->postTask(createCallbackTask(&confirmMessageFromWorkerObjectTask, |
| 292 this)); |
| 293 } |
| 294 |
| 295 void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity) |
| 296 { |
| 297 // See above comment in confirmMessageFromWorkerObject. |
| 298 m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask, |
| 299 this, |
| 300 hasPendingActivity)); |
| 301 } |
| 302 |
| 303 void WebWorkerClientImpl::workerContextDestroyed() |
| 304 { |
| 305 } |
| 306 |
| 307 void WebWorkerClientImpl::startWorkerContextTask(ScriptExecutionContext* context, |
| 308 WebWorkerClientImpl* thisPtr, |
| 309 const String& scriptURL, |
| 310 const String& userAgent, |
| 311 const String& sourceCode) |
| 312 { |
| 313 thisPtr->m_webWorker->startWorkerContext(KURL(ParsedURLString, scriptURL), |
| 314 userAgent, sourceCode); |
| 315 } |
| 316 |
| 317 void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context, |
| 318 WebWorkerClientImpl* thisPtr) |
| 319 { |
| 320 thisPtr->m_webWorker->terminateWorkerContext(); |
| 321 } |
| 322 |
| 323 void WebWorkerClientImpl::postMessageToWorkerContextTask(ScriptExecutionContext* context, |
| 324 WebWorkerClientImpl* thisPtr, |
| 325 const String& message, |
| 326 PassOwnPtr<MessagePortChannelArray> channels) |
| 327 { |
| 328 WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0); |
| 329 |
| 330 for (size_t i = 0; i < webChannels.size(); ++i) { |
| 331 webChannels[i] = (*channels)[i]->channel()->webChannelRelease(); |
| 332 webChannels[i]->setClient(0); |
| 333 } |
| 334 |
| 335 thisPtr->m_webWorker->postMessageToWorkerContext(message, webChannels); |
| 336 } |
| 337 |
| 338 void WebWorkerClientImpl::workerObjectDestroyedTask(ScriptExecutionContext* context, |
| 339 WebWorkerClientImpl* thisPtr) |
| 340 { |
| 341 if (thisPtr->m_worker) // Check we haven't alread called this. |
| 342 thisPtr->m_webWorker->workerObjectDestroyed(); |
| 343 delete thisPtr; |
| 344 } |
| 345 |
| 346 void WebWorkerClientImpl::postMessageToWorkerObjectTask( |
| 347 ScriptExecutionContext* context, |
| 348 WebWorkerClientImpl* thisPtr, |
| 349 const String& message, |
| 350 PassOwnPtr<MessagePortChannelArray> channels) |
| 351 { |
| 352 |
| 353 if (thisPtr->m_worker) { |
| 354 OwnPtr<MessagePortArray> ports = |
| 355 MessagePort::entanglePorts(*context, channels.release()); |
| 356 RefPtr<SerializedScriptValue> serializedMessage = |
| 357 SerializedScriptValue::create(message); |
| 358 thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(), |
| 359 serializedMessage.release())); |
| 360 } |
| 361 } |
| 362 |
| 363 void WebWorkerClientImpl::postExceptionToWorkerObjectTask( |
| 364 ScriptExecutionContext* context, |
| 365 WebWorkerClientImpl* thisPtr, |
| 366 const String& errorMessage, |
| 367 int lineNumber, |
| 368 const String& sourceURL) |
| 369 { |
| 370 bool handled = false; |
| 371 if (thisPtr->m_worker) |
| 372 handled = thisPtr->m_worker->dispatchEvent(ErrorEvent::create(errorMessage, |
| 373 sourceURL, |
| 374 lineNumber)); |
| 375 if (!handled) |
| 376 thisPtr->m_scriptExecutionContext->reportException(errorMessage, |
| 377 lineNumber, |
| 378 sourceURL); |
| 379 } |
| 380 |
| 381 void WebWorkerClientImpl::postConsoleMessageToWorkerObjectTask(ScriptExecutionContext* context, |
| 382 WebWorkerClientImpl* thisPtr, |
| 383 int destinationId, |
| 384 int sourceId, |
| 385 int messageType, |
| 386 int messageLevel, |
| 387 const String& message, |
| 388 int lineNumber, |
| 389 const String& sourceURL) |
| 390 { |
| 391 thisPtr->m_scriptExecutionContext->addMessage(static_cast<MessageDestination>(destinationId), |
| 392 static_cast<MessageSource>(sourceId), |
| 393 static_cast<MessageType>(messageType), |
| 394 static_cast<MessageLevel>(messageLevel), |
| 395 message, lineNumber, |
| 396 sourceURL); |
| 397 } |
| 398 |
| 399 void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context, |
| 400 WebWorkerClientImpl* thisPtr) |
| 401 { |
| 402 thisPtr->m_unconfirmedMessageCount--; |
| 403 } |
| 404 |
| 405 void WebWorkerClientImpl::reportPendingActivityTask(ScriptExecutionContext* context, |
| 406 WebWorkerClientImpl* thisPtr, |
| 407 bool hasPendingActivity) |
| 408 { |
| 409 thisPtr->m_workerContextHadPendingActivity = hasPendingActivity; |
| 410 } |
| 411 |
| 412 } // namespace WebKit |
| 413 |
| 414 #endif |
OLD | NEW |