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 57 namespace blink { 58 59 class MessageWorkerGlobalScopeTask : public ExecutionContextTask { 60 public: 61 static PassOwnPtr<MessageWorkerGlobalScopeTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) 62 { 63 return adoptPtr(new MessageWorkerGlobalScopeTask(message, channels)); 64 } 65 66 private: 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 82 private: 83 RefPtr<SerializedScriptValue> m_message; 84 OwnPtr<MessagePortChannelArray> m_channels; 85 }; 86 87 WorkerMessagingProxy::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 103 WorkerMessagingProxy::~WorkerMessagingProxy() 104 { 105 ASSERT(!m_workerObject); 106 ASSERT((m_executionContext->isDocument() && isMainThread()) 107 || (m_executionContext->isWorkerGlobalScope() && toWorkerGlobalScope(m_executionContext.get())->thread()->isCurrentThread())); 108 } 109 110 void 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 129 void 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 138 void 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 150 bool 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 160 void 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 167 void 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 181 void 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 197 void 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 212 void WorkerMessagingProxy::workerObjectDestroyed() 213 { 214 m_workerObject = 0; 215 m_executionContext->postTask(createCrossThreadTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this))); 216 } 217 218 void 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 227 void 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 238 void 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 250 void 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 259 WorkerInspectorProxy* WorkerMessagingProxy::workerInspectorProxy() 260 { 261 return m_workerInspectorProxy.get(); 262 } 263 264 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity) 265 { 266 if (!m_askedToTerminate) { 267 ASSERT(m_unconfirmedMessageCount); 268 --m_unconfirmedMessageCount; 269 } 270 reportPendingActivity(hasPendingActivity); 271 } 272 273 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity) 274 { 275 m_workerThreadHadPendingActivity = hasPendingActivity; 276 } 277 278 bool WorkerMessagingProxy::hasPendingActivity() const 279 { 280 return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate; 281 } 282 283 void 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