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 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 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->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 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 unhandled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage, 248 sourceURL, 249 lineNumber)); 250 if (unhandled) 251 m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0); 252 } 253 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 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 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 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 308 void WebWorkerClientImpl::workerContextDestroyed() 309 { 310 } 311 312 void WebWorkerClientImpl::workerContextClosed() 313 { 314 } 315 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 326 void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context, 327 WebWorkerClientImpl* thisPtr) 328 { 329 thisPtr->m_webWorker->terminateWorkerContext(); 330 } 331 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 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 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 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 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 403 void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context, 404 WebWorkerClientImpl* thisPtr) 405 { 406 thisPtr->m_unconfirmedMessageCount--; 407 } 408 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