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 66using namespace WebCore; 67 68namespace 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 83WorkerContextProxy* 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 115WebWorkerClientImpl::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 125WebWorkerClientImpl::~WebWorkerClientImpl() 126{ 127} 128 129void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker) 130{ 131 m_webWorker = webWorker; 132} 133 134void 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 153void 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 165void 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 190bool WebWorkerClientImpl::hasPendingActivity() const 191{ 192 return !m_askedToTerminate 193 && (m_unconfirmedMessageCount || m_workerContextHadPendingActivity); 194} 195 196void 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 208void 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 234void 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 254void 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 281void 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 291void 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 300void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity) 301{ 302 // See above comment in confirmMessageFromWorkerObject. 303 m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask, 304 this, 305 hasPendingActivity)); 306} 307 308void WebWorkerClientImpl::workerContextDestroyed() 309{ 310} 311 312void WebWorkerClientImpl::workerContextClosed() 313{ 314} 315 316void 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 326void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context, 327 WebWorkerClientImpl* thisPtr) 328{ 329 thisPtr->m_webWorker->terminateWorkerContext(); 330} 331 332void 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 347void 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 355void 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 372void 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 388void 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 403void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context, 404 WebWorkerClientImpl* thisPtr) 405{ 406 thisPtr->m_unconfirmedMessageCount--; 407} 408 409void 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