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