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 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 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#include "WorkerMessagingProxy.h" 30 31#include "ContentSecurityPolicy.h" 32#include "DOMWindow.h" 33#include "DedicatedWorkerGlobalScope.h" 34#include "DedicatedWorkerThread.h" 35#include "Document.h" 36#include "ErrorEvent.h" 37#include "Event.h" 38#include "EventNames.h" 39#include "ExceptionCode.h" 40#include "InspectorInstrumentation.h" 41#include "MessageEvent.h" 42#include "PageGroup.h" 43#include "ScriptExecutionContext.h" 44#include "Worker.h" 45#include "WorkerDebuggerAgent.h" 46#include "WorkerInspectorController.h" 47#include <inspector/InspectorAgentBase.h> 48#include <inspector/ScriptCallStack.h> 49#include <runtime/ConsoleTypes.h> 50#include <wtf/MainThread.h> 51 52namespace WebCore { 53 54WorkerGlobalScopeProxy* WorkerGlobalScopeProxy::create(Worker* worker) 55{ 56 return new WorkerMessagingProxy(worker); 57} 58 59WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject) 60 : m_scriptExecutionContext(workerObject->scriptExecutionContext()) 61 , m_workerObject(workerObject) 62 , m_mayBeDestroyed(false) 63 , m_unconfirmedMessageCount(0) 64 , m_workerThreadHadPendingActivity(false) 65 , m_askedToTerminate(false) 66#if ENABLE(INSPECTOR) 67 , m_pageInspector(0) 68#endif 69{ 70 ASSERT(m_workerObject); 71 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) 72 || (m_scriptExecutionContext->isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(*m_scriptExecutionContext).thread().threadID())); 73} 74 75WorkerMessagingProxy::~WorkerMessagingProxy() 76{ 77 ASSERT(!m_workerObject); 78 ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) 79 || (m_scriptExecutionContext->isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(*m_scriptExecutionContext).thread().threadID())); 80} 81 82void WorkerMessagingProxy::startWorkerGlobalScope(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode) 83{ 84 // FIXME: This need to be revisited when we support nested worker one day 85 ASSERT_WITH_SECURITY_IMPLICATION(m_scriptExecutionContext->isDocument()); 86 Document* document = static_cast<Document*>(m_scriptExecutionContext.get()); 87 GroupSettings* settings = 0; 88 if (document->page()) 89 settings = &document->page()->group().groupSettings(); 90 RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, userAgent, settings, sourceCode, *this, *this, startMode, document->contentSecurityPolicy()->deprecatedHeader(), document->contentSecurityPolicy()->deprecatedHeaderType(), document->topOrigin()); 91 workerThreadCreated(thread); 92 thread->start(); 93 InspectorInstrumentation::didStartWorkerGlobalScope(m_scriptExecutionContext.get(), this, scriptURL); 94} 95 96void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels) 97{ 98 MessagePortChannelArray* channelsPtr = channels.release(); 99 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext& context) { 100 Worker* workerObject = this->workerObject(); 101 if (!workerObject || askedToTerminate()) 102 return; 103 104 std::unique_ptr<MessagePortArray> ports = MessagePort::entanglePorts(context, std::unique_ptr<MessagePortChannelArray>(channelsPtr)); 105 workerObject->dispatchEvent(MessageEvent::create(WTF::move(ports), message)); 106 }); 107} 108 109void WorkerMessagingProxy::postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message, std::unique_ptr<MessagePortChannelArray> channels) 110{ 111 if (m_askedToTerminate) 112 return; 113 114 MessagePortChannelArray* channelsPtr = channels.release(); 115 ScriptExecutionContext::Task task([=] (ScriptExecutionContext& scriptContext) { 116 ASSERT_WITH_SECURITY_IMPLICATION(scriptContext.isWorkerGlobalScope()); 117 DedicatedWorkerGlobalScope& context = static_cast<DedicatedWorkerGlobalScope&>(scriptContext); 118 std::unique_ptr<MessagePortArray> ports = MessagePort::entanglePorts(scriptContext, std::unique_ptr<MessagePortChannelArray>(channelsPtr)); 119 context.dispatchEvent(MessageEvent::create(WTF::move(ports), message)); 120 context.thread().workerObjectProxy().confirmMessageFromWorkerObject(context.hasPendingActivity()); 121 }); 122 123 if (m_workerThread) { 124 ++m_unconfirmedMessageCount; 125 m_workerThread->runLoop().postTask(WTF::move(task)); 126 } else 127 m_queuedEarlyTasks.append(std::make_unique<ScriptExecutionContext::Task>(WTF::move(task))); 128} 129 130void WorkerMessagingProxy::postTaskToLoader(ScriptExecutionContext::Task task) 131{ 132 // FIXME: In case of nested workers, this should go directly to the root Document context. 133 ASSERT(m_scriptExecutionContext->isDocument()); 134 m_scriptExecutionContext->postTask(WTF::move(task)); 135} 136 137bool WorkerMessagingProxy::postTaskForModeToWorkerGlobalScope(ScriptExecutionContext::Task task, const String& mode) 138{ 139 if (m_askedToTerminate) 140 return false; 141 142 ASSERT(m_workerThread); 143 m_workerThread->runLoop().postTaskForMode(WTF::move(task), mode); 144 return true; 145} 146 147void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) 148{ 149 String errorMessageCopy = errorMessage.isolatedCopy(); 150 String sourceURLCopy = sourceURL.isolatedCopy(); 151 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext& context) { 152 Worker* workerObject = this->workerObject(); 153 if (!workerObject) 154 return; 155 156 // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated. 157 // 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. 158 159 bool errorHandled = !workerObject->dispatchEvent(ErrorEvent::create(errorMessageCopy, sourceURLCopy, lineNumber, columnNumber)); 160 if (!errorHandled) 161 context.reportException(errorMessageCopy, lineNumber, columnNumber, sourceURLCopy, 0); 162 }); 163} 164 165void WorkerMessagingProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageLevel level, const String& message, int lineNumber, int columnNumber, const String& sourceURL) 166{ 167 String messageCopy = message.isolatedCopy(); 168 String sourceURLCopy = sourceURL.isolatedCopy(); 169 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext& context) { 170 if (askedToTerminate()) 171 return; 172 context.addConsoleMessage(source, level, messageCopy, sourceURLCopy, lineNumber, columnNumber); 173 }); 174} 175 176void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread) 177{ 178 m_workerThread = workerThread; 179 180 if (m_askedToTerminate) { 181 // Worker.terminate() could be called from JS before the thread was created. 182 m_workerThread->stop(); 183 } else { 184 ASSERT(!m_unconfirmedMessageCount); 185 m_unconfirmedMessageCount = m_queuedEarlyTasks.size(); 186 m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity. 187 188 auto queuedEarlyTasks = WTF::move(m_queuedEarlyTasks); 189 for (auto& task : queuedEarlyTasks) 190 m_workerThread->runLoop().postTask(WTF::move(*task)); 191 } 192} 193 194void WorkerMessagingProxy::workerObjectDestroyed() 195{ 196 m_workerObject = 0; 197 m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) { 198 m_mayBeDestroyed = true; 199 if (m_workerThread) 200 terminateWorkerGlobalScope(); 201 else 202 workerGlobalScopeDestroyedInternal(); 203 }); 204} 205 206void WorkerMessagingProxy::notifyNetworkStateChange(bool isOnline) 207{ 208 if (m_askedToTerminate) 209 return; 210 211 if (!m_workerThread) 212 return; 213 214 m_workerThread->runLoop().postTask([=] (ScriptExecutionContext& context) { 215 toWorkerGlobalScope(&context)->dispatchEvent(Event::create(isOnline ? eventNames().onlineEvent : eventNames().offlineEvent, false, false)); 216 }); 217} 218 219#if ENABLE(INSPECTOR) 220void WorkerMessagingProxy::connectToInspector(WorkerGlobalScopeProxy::PageInspector* pageInspector) 221{ 222 if (m_askedToTerminate) 223 return; 224 ASSERT(!m_pageInspector); 225 m_pageInspector = pageInspector; 226 m_workerThread->runLoop().postTaskForMode([] (ScriptExecutionContext& context) { 227 toWorkerGlobalScope(&context)->workerInspectorController().connectFrontend(); 228 }, WorkerDebuggerAgent::debuggerTaskMode); 229} 230 231void WorkerMessagingProxy::disconnectFromInspector() 232{ 233 m_pageInspector = 0; 234 if (m_askedToTerminate) 235 return; 236 m_workerThread->runLoop().postTaskForMode([] (ScriptExecutionContext& context) { 237 toWorkerGlobalScope(&context)->workerInspectorController().disconnectFrontend(Inspector::InspectorDisconnectReason::InspectorDestroyed); 238 }, WorkerDebuggerAgent::debuggerTaskMode); 239} 240 241void WorkerMessagingProxy::sendMessageToInspector(const String& message) 242{ 243 if (m_askedToTerminate) 244 return; 245 String messageCopy = message.isolatedCopy(); 246 m_workerThread->runLoop().postTaskForMode([messageCopy] (ScriptExecutionContext& context) { 247 toWorkerGlobalScope(&context)->workerInspectorController().dispatchMessageFromFrontend(messageCopy); 248 }, WorkerDebuggerAgent::debuggerTaskMode); 249 WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get()); 250} 251#endif 252 253void WorkerMessagingProxy::workerGlobalScopeDestroyed() 254{ 255 m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) { 256 workerGlobalScopeDestroyedInternal(); 257 }); 258 // Will execute workerGlobalScopeDestroyedInternal() on context's thread. 259} 260 261void WorkerMessagingProxy::workerGlobalScopeClosed() 262{ 263 // Executes terminateWorkerGlobalScope() on parent context's thread. 264 m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) { 265 terminateWorkerGlobalScope(); 266 }); 267} 268 269void WorkerMessagingProxy::workerGlobalScopeDestroyedInternal() 270{ 271 // WorkerGlobalScopeDestroyedTask is always the last to be performed, so the proxy is not needed for communication 272 // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too. 273 m_askedToTerminate = true; 274 m_workerThread = 0; 275 276 InspectorInstrumentation::workerGlobalScopeTerminated(m_scriptExecutionContext.get(), this); 277 278 if (m_mayBeDestroyed) 279 delete this; 280} 281 282void WorkerMessagingProxy::terminateWorkerGlobalScope() 283{ 284 if (m_askedToTerminate) 285 return; 286 m_askedToTerminate = true; 287 288 if (m_workerThread) 289 m_workerThread->stop(); 290 291 InspectorInstrumentation::workerGlobalScopeTerminated(m_scriptExecutionContext.get(), this); 292} 293 294#if ENABLE(INSPECTOR) 295void WorkerMessagingProxy::postMessageToPageInspector(const String& message) 296{ 297 String messageCopy = message.isolatedCopy(); 298 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) { 299 m_pageInspector->dispatchMessageFromWorker(messageCopy); 300 }); 301} 302#endif 303 304void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity) 305{ 306 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) { 307 reportPendingActivityInternal(true, hasPendingActivity); 308 }); 309 // Will execute reportPendingActivityInternal() on context's thread. 310} 311 312void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity) 313{ 314 m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) { 315 reportPendingActivityInternal(false, hasPendingActivity); 316 }); 317 // Will execute reportPendingActivityInternal() on context's thread. 318} 319 320void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity) 321{ 322 if (confirmingMessage && !m_askedToTerminate) { 323 ASSERT(m_unconfirmedMessageCount); 324 --m_unconfirmedMessageCount; 325 } 326 327 m_workerThreadHadPendingActivity = hasPendingActivity; 328} 329 330bool WorkerMessagingProxy::hasPendingActivity() const 331{ 332 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate; 333} 334 335} // namespace WebCore 336