1/* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2009 Google Inc. All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28#include "config.h" 29 30#if ENABLE(WORKERS) 31 32#include "WorkerMessagingProxy.h" 33 34#include "ContentSecurityPolicy.h" 35#include "CrossThreadTask.h" 36#include "DedicatedWorkerContext.h" 37#include "DedicatedWorkerThread.h" 38#include "DOMWindow.h" 39#include "Document.h" 40#include "ErrorEvent.h" 41#include "ExceptionCode.h" 42#include "InspectorInstrumentation.h" 43#include "MessageEvent.h" 44#include "NotImplemented.h" 45#include "PageGroup.h" 46#include "ScriptCallStack.h" 47#include "ScriptExecutionContext.h" 48#include "Worker.h" 49#include "WorkerDebuggerAgent.h" 50#include "WorkerInspectorController.h" 51#include <wtf/MainThread.h> 52 53namespace WebCore { 54 55class MessageWorkerContextTask : public ScriptExecutionContext::Task { 56public: 57 static PassOwnPtr<MessageWorkerContextTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 58 { 59 return adoptPtr(new MessageWorkerContextTask(message, channels)); 60 } 61 62private: 63 MessageWorkerContextTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 64 : m_message(message) 65 , m_channels(channels) 66 { 67 } 68 69 virtual void performTask(ScriptExecutionContext* scriptContext) 70 { 71 ASSERT_WITH_SECURITY_IMPLICATION(scriptContext->isWorkerContext()); 72 DedicatedWorkerContext* context = static_cast<DedicatedWorkerContext*>(scriptContext); 73 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release()); 74 context->dispatchEvent(MessageEvent::create(ports.release(), m_message)); 75 context->thread()->workerObjectProxy().confirmMessageFromWorkerObject(context->hasPendingActivity()); 76 } 77 78private: 79 RefPtr<SerializedScriptValue> m_message; 80 OwnPtr<MessagePortChannelArray> m_channels; 81}; 82 83class MessageWorkerTask : public ScriptExecutionContext::Task { 84public: 85 static PassOwnPtr<MessageWorkerTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels, WorkerMessagingProxy* messagingProxy) 86 { 87 return adoptPtr(new MessageWorkerTask(message, channels, messagingProxy)); 88 } 89 90private: 91 MessageWorkerTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels, WorkerMessagingProxy* messagingProxy) 92 : m_message(message) 93 , m_channels(channels) 94 , m_messagingProxy(messagingProxy) 95 { 96 } 97 98 virtual void performTask(ScriptExecutionContext* scriptContext) 99 { 100 Worker* workerObject = m_messagingProxy->workerObject(); 101 if (!workerObject || m_messagingProxy->askedToTerminate()) 102 return; 103 104 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release()); 105 workerObject->dispatchEvent(MessageEvent::create(ports.release(), m_message)); 106 } 107 108private: 109 RefPtr<SerializedScriptValue> m_message; 110 OwnPtr<MessagePortChannelArray> m_channels; 111 WorkerMessagingProxy* m_messagingProxy; 112}; 113 114class WorkerExceptionTask : public ScriptExecutionContext::Task { 115public: 116 static PassOwnPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy) 117 { 118 return adoptPtr(new WorkerExceptionTask(errorMessage, lineNumber, columnNumber, sourceURL, messagingProxy)); 119 } 120 121private: 122 WorkerExceptionTask(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy) 123 : m_errorMessage(errorMessage.isolatedCopy()) 124 , m_lineNumber(lineNumber) 125 , m_columnNumber(columnNumber) 126 , m_sourceURL(sourceURL.isolatedCopy()) 127 , m_messagingProxy(messagingProxy) 128 { 129 } 130 131 virtual void performTask(ScriptExecutionContext* context) 132 { 133 Worker* workerObject = m_messagingProxy->workerObject(); 134 if (!workerObject) 135 return; 136 137 // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated. 138 // This is intentionally different than the behavior in MessageWorkerTask, because terminated workers no longer deliver messages (section 4.6 of the WebWorker spec), but they do report exceptions. 139 140 bool errorHandled = !workerObject->dispatchEvent(ErrorEvent::create(m_errorMessage, m_sourceURL, m_lineNumber)); 141 if (!errorHandled) 142 context->reportException(m_errorMessage, m_lineNumber, m_columnNumber, m_sourceURL, 0); 143 } 144 145 String m_errorMessage; 146 int m_lineNumber; 147 int m_columnNumber; 148 String m_sourceURL; 149 WorkerMessagingProxy* m_messagingProxy; 150}; 151 152class WorkerContextDestroyedTask : public ScriptExecutionContext::Task { 153public: 154 static PassOwnPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy) 155 { 156 return adoptPtr(new WorkerContextDestroyedTask(messagingProxy)); 157 } 158 159private: 160 WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy) 161 : m_messagingProxy(messagingProxy) 162 { 163 } 164 165 virtual void performTask(ScriptExecutionContext*) 166 { 167 m_messagingProxy->workerContextDestroyedInternal(); 168 } 169 170 WorkerMessagingProxy* m_messagingProxy; 171}; 172 173class WorkerTerminateTask : public ScriptExecutionContext::Task { 174public: 175 static PassOwnPtr<WorkerTerminateTask> create(WorkerMessagingProxy* messagingProxy) 176 { 177 return adoptPtr(new WorkerTerminateTask(messagingProxy)); 178 } 179 180private: 181 WorkerTerminateTask(WorkerMessagingProxy* messagingProxy) 182 : m_messagingProxy(messagingProxy) 183 { 184 } 185 186 virtual void performTask(ScriptExecutionContext*) 187 { 188 m_messagingProxy->terminateWorkerContext(); 189 } 190 191 WorkerMessagingProxy* m_messagingProxy; 192}; 193 194class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task { 195public: 196 static PassOwnPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity) 197 { 198 return adoptPtr(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity)); 199 } 200 201private: 202 WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity) 203 : m_messagingProxy(messagingProxy) 204 , m_confirmingMessage(confirmingMessage) 205 , m_hasPendingActivity(hasPendingActivity) 206 { 207 } 208 209 virtual void performTask(ScriptExecutionContext*) 210 { 211 m_messagingProxy->reportPendingActivityInternal(m_confirmingMessage, m_hasPendingActivity); 212 } 213 214 WorkerMessagingProxy* m_messagingProxy; 215 bool m_confirmingMessage; 216 bool m_hasPendingActivity; 217}; 218 219class PostMessageToPageInspectorTask : public ScriptExecutionContext::Task { 220public: 221 static PassOwnPtr<PostMessageToPageInspectorTask> create(WorkerMessagingProxy* messagingProxy, const String& message) 222 { 223 return adoptPtr(new PostMessageToPageInspectorTask(messagingProxy, message)); 224 } 225 226private: 227 PostMessageToPageInspectorTask(WorkerMessagingProxy* messagingProxy, const String& message) 228 : m_messagingProxy(messagingProxy) 229 , m_message(message.isolatedCopy()) 230 { 231 } 232 233 virtual void performTask(ScriptExecutionContext*) 234 { 235#if ENABLE(INSPECTOR) 236 if (WorkerContextProxy::PageInspector* pageInspector = m_messagingProxy->m_pageInspector) 237 pageInspector->dispatchMessageFromWorker(m_message); 238#endif 239 } 240 241 WorkerMessagingProxy* m_messagingProxy; 242 String m_message; 243}; 244 245 246WorkerContextProxy* WorkerContextProxy::create(Worker* worker) 247{ 248 return new WorkerMessagingProxy(worker); 249} 250 251WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject) 252 : m_scriptExecutionContext(workerObject->scriptExecutionContext()) 253 , m_workerObject(workerObject) 254 , m_mayBeDestroyed(false) 255 , m_unconfirmedMessageCount(0) 256 , m_workerThreadHadPendingActivity(false) 257 , m_askedToTerminate(false) 258#if ENABLE(INSPECTOR) 259 , m_pageInspector(0) 260#endif 261{ 262 ASSERT(m_workerObject); 263 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) 264 || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID())); 265} 266 267WorkerMessagingProxy::~WorkerMessagingProxy() 268{ 269 ASSERT(!m_workerObject); 270 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) 271 || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID())); 272} 273 274void WorkerMessagingProxy::startWorkerContext(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode) 275{ 276 // FIXME: This need to be revisited when we support nested worker one day 277 ASSERT(m_scriptExecutionContext->isDocument()); 278 Document* document = static_cast<Document*>(m_scriptExecutionContext.get()); 279 GroupSettings* settings = 0; 280 if (document->page()) 281 settings = document->page()->group().groupSettings(); 282 RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, userAgent, settings, sourceCode, *this, *this, startMode, document->contentSecurityPolicy()->deprecatedHeader(), document->contentSecurityPolicy()->deprecatedHeaderType(), document->topOrigin()); 283 workerThreadCreated(thread); 284 thread->start(); 285 InspectorInstrumentation::didStartWorkerContext(m_scriptExecutionContext.get(), this, scriptURL); 286} 287 288void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 289{ 290 m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, channels, this)); 291} 292 293void WorkerMessagingProxy::postMessageToWorkerContext(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 294{ 295 if (m_askedToTerminate) 296 return; 297 298 if (m_workerThread) { 299 ++m_unconfirmedMessageCount; 300 m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message, channels)); 301 } else 302 m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message, channels)); 303} 304 305bool WorkerMessagingProxy::postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode) 306{ 307 if (m_askedToTerminate) 308 return false; 309 310 ASSERT(m_workerThread); 311 m_workerThread->runLoop().postTaskForMode(task, mode); 312 return true; 313} 314 315void WorkerMessagingProxy::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task) 316{ 317 // FIXME: In case of nested workers, this should go directly to the root Document context. 318 ASSERT(m_scriptExecutionContext->isDocument()); 319 m_scriptExecutionContext->postTask(task); 320} 321 322void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) 323{ 324 m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, columnNumber, sourceURL, this)); 325} 326 327static void postConsoleMessageTask(ScriptExecutionContext* context, WorkerMessagingProxy* messagingProxy, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceURL) 328{ 329 if (messagingProxy->askedToTerminate()) 330 return; 331 context->addConsoleMessage(source, level, message, sourceURL, lineNumber, columnNumber); 332} 333 334void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageLevel level, const String& message, int lineNumber, int columnNumber, const String& sourceURL) 335{ 336 m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageTask, AllowCrossThreadAccess(this), source, level, message, lineNumber, columnNumber, sourceURL)); 337} 338 339void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread) 340{ 341 m_workerThread = workerThread; 342 343 if (m_askedToTerminate) { 344 // Worker.terminate() could be called from JS before the thread was created. 345 m_workerThread->stop(); 346 } else { 347 unsigned taskCount = m_queuedEarlyTasks.size(); 348 ASSERT(!m_unconfirmedMessageCount); 349 m_unconfirmedMessageCount = taskCount; 350 m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity. 351 352 for (unsigned i = 0; i < taskCount; ++i) 353 m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i].release()); 354 m_queuedEarlyTasks.clear(); 355 } 356} 357 358void WorkerMessagingProxy::workerObjectDestroyed() 359{ 360 m_workerObject = 0; 361 m_scriptExecutionContext->postTask(createCallbackTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this))); 362} 363 364void WorkerMessagingProxy::workerObjectDestroyedInternal(ScriptExecutionContext*, WorkerMessagingProxy* proxy) 365{ 366 proxy->m_mayBeDestroyed = true; 367 if (proxy->m_workerThread) 368 proxy->terminateWorkerContext(); 369 else 370 proxy->workerContextDestroyedInternal(); 371} 372 373#if ENABLE(INSPECTOR) 374#if ENABLE(JAVASCRIPT_DEBUGGER) 375static void connectToWorkerContextInspectorTask(ScriptExecutionContext* context, bool) 376{ 377 ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerContext()); 378 static_cast<WorkerContext*>(context)->workerInspectorController()->connectFrontend(); 379} 380#endif 381 382void WorkerMessagingProxy::connectToInspector(WorkerContextProxy::PageInspector* pageInspector) 383{ 384 if (m_askedToTerminate) 385 return; 386 ASSERT(!m_pageInspector); 387 m_pageInspector = pageInspector; 388#if ENABLE(JAVASCRIPT_DEBUGGER) 389 m_workerThread->runLoop().postTaskForMode(createCallbackTask(connectToWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode); 390#endif 391} 392 393#if ENABLE(JAVASCRIPT_DEBUGGER) 394static void disconnectFromWorkerContextInspectorTask(ScriptExecutionContext* context, bool) 395{ 396 ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerContext()); 397 static_cast<WorkerContext*>(context)->workerInspectorController()->disconnectFrontend(); 398} 399#endif 400 401void WorkerMessagingProxy::disconnectFromInspector() 402{ 403 m_pageInspector = 0; 404 if (m_askedToTerminate) 405 return; 406#if ENABLE(JAVASCRIPT_DEBUGGER) 407 m_workerThread->runLoop().postTaskForMode(createCallbackTask(disconnectFromWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode); 408#endif 409} 410 411#if ENABLE(JAVASCRIPT_DEBUGGER) 412static void dispatchOnInspectorBackendTask(ScriptExecutionContext* context, const String& message) 413{ 414 ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerContext()); 415 static_cast<WorkerContext*>(context)->workerInspectorController()->dispatchMessageFromFrontend(message); 416} 417#endif 418 419void WorkerMessagingProxy::sendMessageToInspector(const String& message) 420{ 421 if (m_askedToTerminate) 422 return; 423#if ENABLE(JAVASCRIPT_DEBUGGER) 424 m_workerThread->runLoop().postTaskForMode(createCallbackTask(dispatchOnInspectorBackendTask, String(message)), WorkerDebuggerAgent::debuggerTaskMode); 425 WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get()); 426#endif 427} 428#endif 429 430void WorkerMessagingProxy::workerContextDestroyed() 431{ 432 m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this)); 433 // Will execute workerContextDestroyedInternal() on context's thread. 434} 435 436void WorkerMessagingProxy::workerContextClosed() 437{ 438 // Executes terminateWorkerContext() on parent context's thread. 439 m_scriptExecutionContext->postTask(WorkerTerminateTask::create(this)); 440} 441 442void WorkerMessagingProxy::workerContextDestroyedInternal() 443{ 444 // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication 445 // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too. 446 m_askedToTerminate = true; 447 m_workerThread = 0; 448 449 InspectorInstrumentation::workerContextTerminated(m_scriptExecutionContext.get(), this); 450 451 if (m_mayBeDestroyed) 452 delete this; 453} 454 455void WorkerMessagingProxy::terminateWorkerContext() 456{ 457 if (m_askedToTerminate) 458 return; 459 m_askedToTerminate = true; 460 461 if (m_workerThread) 462 m_workerThread->stop(); 463 464 InspectorInstrumentation::workerContextTerminated(m_scriptExecutionContext.get(), this); 465} 466 467#if ENABLE(INSPECTOR) 468void WorkerMessagingProxy::postMessageToPageInspector(const String& message) 469{ 470 m_scriptExecutionContext->postTask(PostMessageToPageInspectorTask::create(this, message)); 471} 472 473void WorkerMessagingProxy::updateInspectorStateCookie(const String&) 474{ 475 notImplemented(); 476} 477#endif 478 479void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity) 480{ 481 m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity)); 482 // Will execute reportPendingActivityInternal() on context's thread. 483} 484 485void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity) 486{ 487 m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity)); 488 // Will execute reportPendingActivityInternal() on context's thread. 489} 490 491void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity) 492{ 493 if (confirmingMessage && !m_askedToTerminate) { 494 ASSERT(m_unconfirmedMessageCount); 495 --m_unconfirmedMessageCount; 496 } 497 498 m_workerThreadHadPendingActivity = hasPendingActivity; 499} 500 501bool WorkerMessagingProxy::hasPendingActivity() const 502{ 503 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate; 504} 505 506} // namespace WebCore 507 508#endif // ENABLE(WORKERS) 509