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/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