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#include "core/workers/WorkerMessagingProxy.h" 31 32#include "core/dom/CrossThreadTask.h" 33#include "core/dom/Document.h" 34#include "core/events/ErrorEvent.h" 35#include "core/events/MessageEvent.h" 36#include "core/frame/Console.h" 37#include "core/frame/FrameConsole.h" 38#include "core/frame/LocalDOMWindow.h" 39#include "core/frame/LocalFrame.h" 40#include "core/frame/csp/ContentSecurityPolicy.h" 41#include "core/inspector/ScriptCallStack.h" 42#include "core/inspector/WorkerDebuggerAgent.h" 43#include "core/loader/DocumentLoadTiming.h" 44#include "core/loader/DocumentLoader.h" 45#include "core/workers/DedicatedWorkerGlobalScope.h" 46#include "core/workers/DedicatedWorkerThread.h" 47#include "core/workers/Worker.h" 48#include "core/workers/WorkerClients.h" 49#include "core/workers/WorkerInspectorProxy.h" 50#include "core/workers/WorkerObjectProxy.h" 51#include "core/workers/WorkerThreadStartupData.h" 52#include "platform/NotImplemented.h" 53#include "platform/heap/Handle.h" 54#include "wtf/Functional.h" 55#include "wtf/MainThread.h" 56 57namespace blink { 58 59class MessageWorkerGlobalScopeTask : public ExecutionContextTask { 60public: 61 static PassOwnPtr<MessageWorkerGlobalScopeTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 62 { 63 return adoptPtr(new MessageWorkerGlobalScopeTask(message, channels)); 64 } 65 66private: 67 MessageWorkerGlobalScopeTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 68 : m_message(message) 69 , m_channels(channels) 70 { 71 } 72 73 virtual void performTask(ExecutionContext* scriptContext) 74 { 75 ASSERT_WITH_SECURITY_IMPLICATION(scriptContext->isWorkerGlobalScope()); 76 DedicatedWorkerGlobalScope* context = static_cast<DedicatedWorkerGlobalScope*>(scriptContext); 77 OwnPtrWillBeRawPtr<MessagePortArray> ports = MessagePort::entanglePorts(*scriptContext, m_channels.release()); 78 context->dispatchEvent(MessageEvent::create(ports.release(), m_message)); 79 context->thread()->workerObjectProxy().confirmMessageFromWorkerObject(context->hasPendingActivity()); 80 } 81 82private: 83 RefPtr<SerializedScriptValue> m_message; 84 OwnPtr<MessagePortChannelArray> m_channels; 85}; 86 87WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients) 88 : m_executionContext(workerObject->executionContext()) 89 , m_workerObjectProxy(WorkerObjectProxy::create(m_executionContext.get(), this)) 90 , m_workerObject(workerObject) 91 , m_mayBeDestroyed(false) 92 , m_unconfirmedMessageCount(0) 93 , m_workerThreadHadPendingActivity(false) 94 , m_askedToTerminate(false) 95 , m_workerInspectorProxy(WorkerInspectorProxy::create()) 96 , m_workerClients(workerClients) 97{ 98 ASSERT(m_workerObject); 99 ASSERT((m_executionContext->isDocument() && isMainThread()) 100 || (m_executionContext->isWorkerGlobalScope() && toWorkerGlobalScope(m_executionContext.get())->thread()->isCurrentThread())); 101} 102 103WorkerMessagingProxy::~WorkerMessagingProxy() 104{ 105 ASSERT(!m_workerObject); 106 ASSERT((m_executionContext->isDocument() && isMainThread()) 107 || (m_executionContext->isWorkerGlobalScope() && toWorkerGlobalScope(m_executionContext.get())->thread()->isCurrentThread())); 108} 109 110void WorkerMessagingProxy::startWorkerGlobalScope(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode) 111{ 112 // FIXME: This need to be revisited when we support nested worker one day 113 ASSERT(m_executionContext->isDocument()); 114 if (m_askedToTerminate) { 115 // Worker.terminate() could be called from JS before the thread was created. 116 return; 117 } 118 Document* document = toDocument(m_executionContext.get()); 119 120 OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(scriptURL, userAgent, sourceCode, startMode, document->contentSecurityPolicy()->deprecatedHeader(), document->contentSecurityPolicy()->deprecatedHeaderType(), m_workerClients.release()); 121 double originTime = document->loader() ? document->loader()->timing()->referenceMonotonicTime() : monotonicallyIncreasingTime(); 122 123 RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(*this, *m_workerObjectProxy.get(), originTime, startupData.release()); 124 thread->start(); 125 workerThreadCreated(thread); 126 m_workerInspectorProxy->workerThreadCreated(m_executionContext.get(), m_workerThread.get(), scriptURL); 127} 128 129void WorkerMessagingProxy::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 130{ 131 if (!m_workerObject || m_askedToTerminate) 132 return; 133 134 OwnPtrWillBeRawPtr<MessagePortArray> ports = MessagePort::entanglePorts(*m_executionContext.get(), channels); 135 m_workerObject->dispatchEvent(MessageEvent::create(ports.release(), message)); 136} 137 138void WorkerMessagingProxy::postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 139{ 140 if (m_askedToTerminate) 141 return; 142 143 if (m_workerThread) { 144 ++m_unconfirmedMessageCount; 145 m_workerThread->postTask(MessageWorkerGlobalScopeTask::create(message, channels)); 146 } else 147 m_queuedEarlyTasks.append(MessageWorkerGlobalScopeTask::create(message, channels)); 148} 149 150bool WorkerMessagingProxy::postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task) 151{ 152 if (m_askedToTerminate) 153 return false; 154 155 ASSERT(m_workerThread); 156 m_workerThread->postTask(task); 157 return true; 158} 159 160void WorkerMessagingProxy::postTaskToLoader(PassOwnPtr<ExecutionContextTask> task) 161{ 162 // FIXME: In case of nested workers, this should go directly to the root Document context. 163 ASSERT(m_executionContext->isDocument()); 164 m_executionContext->postTask(task); 165} 166 167void WorkerMessagingProxy::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) 168{ 169 if (!m_workerObject) 170 return; 171 172 // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated. 173 // 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. 174 175 RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, sourceURL, lineNumber, columnNumber, 0); 176 bool errorHandled = !m_workerObject->dispatchEvent(event); 177 if (!errorHandled) 178 m_executionContext->reportException(event, 0, nullptr, NotSharableCrossOrigin); 179} 180 181void WorkerMessagingProxy::reportConsoleMessage(MessageSource source, MessageLevel level, const String& message, int lineNumber, const String& sourceURL) 182{ 183 if (m_askedToTerminate) 184 return; 185 // FIXME: In case of nested workers, this should go directly to the root Document context. 186 ASSERT(m_executionContext->isDocument()); 187 Document* document = toDocument(m_executionContext.get()); 188 LocalFrame* frame = document->frame(); 189 if (!frame) 190 return; 191 192 RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(source, level, message, sourceURL, lineNumber); 193 consoleMessage->setWorkerGlobalScopeProxy(this); 194 frame->console().addMessage(consoleMessage.release()); 195} 196 197void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread) 198{ 199 ASSERT(!m_askedToTerminate); 200 m_workerThread = workerThread; 201 202 unsigned taskCount = m_queuedEarlyTasks.size(); 203 ASSERT(!m_unconfirmedMessageCount); 204 m_unconfirmedMessageCount = taskCount; 205 m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity. 206 207 for (unsigned i = 0; i < taskCount; ++i) 208 m_workerThread->postTask(m_queuedEarlyTasks[i].release()); 209 m_queuedEarlyTasks.clear(); 210} 211 212void WorkerMessagingProxy::workerObjectDestroyed() 213{ 214 m_workerObject = 0; 215 m_executionContext->postTask(createCrossThreadTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this))); 216} 217 218void WorkerMessagingProxy::workerObjectDestroyedInternal(ExecutionContext*, WorkerMessagingProxy* proxy) 219{ 220 proxy->m_mayBeDestroyed = true; 221 if (proxy->m_workerThread) 222 proxy->terminateWorkerGlobalScope(); 223 else 224 proxy->workerThreadTerminated(); 225} 226 227void WorkerMessagingProxy::workerThreadTerminated() 228{ 229 // This method is always the last to be performed, so the proxy is not needed for communication 230 // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too. 231 m_askedToTerminate = true; 232 m_workerThread = nullptr; 233 terminateInternally(); 234 if (m_mayBeDestroyed) 235 delete this; 236} 237 238void WorkerMessagingProxy::terminateWorkerGlobalScope() 239{ 240 if (m_askedToTerminate) 241 return; 242 m_askedToTerminate = true; 243 244 if (m_workerThread) 245 m_workerThread->stop(); 246 247 terminateInternally(); 248} 249 250void WorkerMessagingProxy::postMessageToPageInspector(const String& message) 251{ 252 if (!m_workerInspectorProxy) 253 return; 254 WorkerInspectorProxy::PageInspector* pageInspector = m_workerInspectorProxy->pageInspector(); 255 if (pageInspector) 256 pageInspector->dispatchMessageFromWorker(message); 257} 258 259WorkerInspectorProxy* WorkerMessagingProxy::workerInspectorProxy() 260{ 261 return m_workerInspectorProxy.get(); 262} 263 264void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity) 265{ 266 if (!m_askedToTerminate) { 267 ASSERT(m_unconfirmedMessageCount); 268 --m_unconfirmedMessageCount; 269 } 270 reportPendingActivity(hasPendingActivity); 271} 272 273void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity) 274{ 275 m_workerThreadHadPendingActivity = hasPendingActivity; 276} 277 278bool WorkerMessagingProxy::hasPendingActivity() const 279{ 280 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate; 281} 282 283void WorkerMessagingProxy::terminateInternally() 284{ 285 m_workerInspectorProxy->workerThreadTerminated(); 286 287 // FIXME: This need to be revisited when we support nested worker one day 288 ASSERT(m_executionContext->isDocument()); 289 Document* document = toDocument(m_executionContext.get()); 290 LocalFrame* frame = document->frame(); 291 if (frame) 292 frame->console().adoptWorkerMessagesAfterTermination(this); 293} 294 295} // namespace blink 296