Home | History | Annotate | Download | only in workers
      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:
     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, 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 
    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     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 
    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::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 
    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     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 
    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().postDebuggerTask(createCallbackTask(connectToWorkerGlobalScopeInspectorTask, true));
    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().postDebuggerTask(createCallbackTask(disconnectFromWorkerGlobalScopeInspectorTask, true));
    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().postDebuggerTask(createCallbackTask(dispatchOnInspectorBackendTask, String(message)));
    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 = nullptr;
    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