• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/LocalDOMWindow.h"
37 #include "core/frame/csp/ContentSecurityPolicy.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/workers/DedicatedWorkerGlobalScope.h"
45 #include "core/workers/DedicatedWorkerThread.h"
46 #include "core/workers/Worker.h"
47 #include "core/workers/WorkerClients.h"
48 #include "core/workers/WorkerObjectProxy.h"
49 #include "core/workers/WorkerThreadStartupData.h"
50 #include "platform/NotImplemented.h"
51 #include "platform/heap/Handle.h"
52 #include "wtf/Functional.h"
53 #include "wtf/MainThread.h"
54 
55 namespace WebCore {
56 
57 class MessageWorkerGlobalScopeTask : public ExecutionContextTask {
58 public:
create(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)59     static PassOwnPtr<MessageWorkerGlobalScopeTask> create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
60     {
61         return adoptPtr(new MessageWorkerGlobalScopeTask(message, channels));
62     }
63 
64 private:
MessageWorkerGlobalScopeTask(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)65     MessageWorkerGlobalScopeTask(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels)
66         : m_message(message)
67         , m_channels(channels)
68     {
69     }
70 
performTask(ExecutionContext * scriptContext)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 
WorkerMessagingProxy(Worker * workerObject,PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)85 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject, PassOwnPtrWillBeRawPtr<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 
~WorkerMessagingProxy()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 
startWorkerGlobalScope(const KURL & scriptURL,const String & userAgent,const String & sourceCode,WorkerThreadStartMode startMode)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     OwnPtrWillBeRawPtr<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 
postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)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 
postMessageToWorkerGlobalScope(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)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 
postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task)144 bool WorkerMessagingProxy::postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task)
145 {
146     if (m_askedToTerminate)
147         return false;
148 
149     ASSERT(m_workerThread);
150     m_workerThread->runLoop().postTask(task);
151     return true;
152 }
153 
postTaskToLoader(PassOwnPtr<ExecutionContextTask> task)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 
reportException(const String & errorMessage,int lineNumber,int columnNumber,const String & sourceURL)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     RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, sourceURL, lineNumber, columnNumber, 0);
170     bool errorHandled = !m_workerObject->dispatchEvent(event);
171     if (!errorHandled)
172         m_executionContext->reportException(event, nullptr, NotSharableCrossOrigin);
173 }
174 
reportConsoleMessage(MessageSource source,MessageLevel level,const String & message,int lineNumber,const String & sourceURL)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 
workerThreadCreated(PassRefPtr<DedicatedWorkerThread> workerThread)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 
workerObjectDestroyed()201 void WorkerMessagingProxy::workerObjectDestroyed()
202 {
203     m_workerObject = 0;
204     m_executionContext->postTask(createCallbackTask(&workerObjectDestroyedInternal, AllowCrossThreadAccess(this)));
205 }
206 
workerObjectDestroyedInternal(ExecutionContext *,WorkerMessagingProxy * proxy)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 
connectToWorkerGlobalScopeInspectorTask(ExecutionContext * context,bool)216 static void connectToWorkerGlobalScopeInspectorTask(ExecutionContext* context, bool)
217 {
218     toWorkerGlobalScope(context)->workerInspectorController()->connectFrontend();
219 }
220 
connectToInspector(WorkerGlobalScopeProxy::PageInspector * pageInspector)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().postDebuggerTask(createCallbackTask(connectToWorkerGlobalScopeInspectorTask, true));
228 }
229 
disconnectFromWorkerGlobalScopeInspectorTask(ExecutionContext * context,bool)230 static void disconnectFromWorkerGlobalScopeInspectorTask(ExecutionContext* context, bool)
231 {
232     toWorkerGlobalScope(context)->workerInspectorController()->disconnectFrontend();
233 }
234 
disconnectFromInspector()235 void WorkerMessagingProxy::disconnectFromInspector()
236 {
237     m_pageInspector = 0;
238     if (m_askedToTerminate)
239         return;
240     m_workerThread->runLoop().postDebuggerTask(createCallbackTask(disconnectFromWorkerGlobalScopeInspectorTask, true));
241 }
242 
dispatchOnInspectorBackendTask(ExecutionContext * context,const String & message)243 static void dispatchOnInspectorBackendTask(ExecutionContext* context, const String& message)
244 {
245     toWorkerGlobalScope(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
246 }
247 
sendMessageToInspector(const String & message)248 void WorkerMessagingProxy::sendMessageToInspector(const String& message)
249 {
250     if (m_askedToTerminate)
251         return;
252     m_workerThread->runLoop().postDebuggerTask(createCallbackTask(dispatchOnInspectorBackendTask, String(message)));
253     WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get());
254 }
255 
workerGlobalScopeDestroyed()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 = nullptr;
262 
263     InspectorInstrumentation::workerGlobalScopeTerminated(m_executionContext.get(), this);
264 
265     if (m_mayBeDestroyed)
266         delete this;
267 }
268 
terminateWorkerGlobalScope()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 
postMessageToPageInspector(const String & message)281 void WorkerMessagingProxy::postMessageToPageInspector(const String& message)
282 {
283     if (m_pageInspector)
284         m_pageInspector->dispatchMessageFromWorker(message);
285 }
286 
confirmMessageFromWorkerObject(bool hasPendingActivity)287 void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity)
288 {
289     if (!m_askedToTerminate) {
290         ASSERT(m_unconfirmedMessageCount);
291         --m_unconfirmedMessageCount;
292     }
293     reportPendingActivity(hasPendingActivity);
294 }
295 
reportPendingActivity(bool hasPendingActivity)296 void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity)
297 {
298     m_workerThreadHadPendingActivity = hasPendingActivity;
299 }
300 
hasPendingActivity() const301 bool WorkerMessagingProxy::hasPendingActivity() const
302 {
303     return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate;
304 }
305 
306 } // namespace WebCore
307