Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "WebWorkerClientImpl.h"
     33 
     34 #if ENABLE(WORKERS)
     35 
     36 #include "CrossThreadTask.h"
     37 #include "DedicatedWorkerThread.h"
     38 #include "ErrorEvent.h"
     39 #include "Frame.h"
     40 #include "FrameLoaderClient.h"
     41 #include "MessageEvent.h"
     42 #include "MessagePort.h"
     43 #include "MessagePortChannel.h"
     44 #include "ScriptCallStack.h"
     45 #include "ScriptExecutionContext.h"
     46 #include "Worker.h"
     47 #include "WorkerContext.h"
     48 #include "WorkerContextExecutionProxy.h"
     49 #include "WorkerScriptController.h"
     50 #include "WorkerMessagingProxy.h"
     51 #include <wtf/Threading.h>
     52 
     53 #include "FrameLoaderClientImpl.h"
     54 #include "PlatformMessagePortChannel.h"
     55 #include "WebFrameClient.h"
     56 #include "WebFrameImpl.h"
     57 #include "WebKit.h"
     58 #include "WebKitClient.h"
     59 #include "WebMessagePortChannel.h"
     60 #include "WebString.h"
     61 #include "WebURL.h"
     62 #include "WebViewImpl.h"
     63 #include "WebWorker.h"
     64 #include "WebWorkerImpl.h"
     65 
     66 using namespace WebCore;
     67 
     68 namespace WebKit {
     69 
     70 // When WebKit creates a WorkerContextProxy object, we check if we're in the
     71 // renderer or worker process.  If the latter, then we just use
     72 // WorkerMessagingProxy.
     73 //
     74 // If we're in the renderer process, then we need use the glue provided
     75 // WebWorker object to talk to the worker process over IPC.  The worker process
     76 // talks to Worker* using WorkerObjectProxy, which we implement on
     77 // WebWorkerClientImpl.
     78 //
     79 // Note that if we're running each worker in a separate process, then nested
     80 // workers end up using the same codepath as the renderer process.
     81 
     82 // static
     83 WorkerContextProxy* WebWorkerClientImpl::createWorkerContextProxy(Worker* worker)
     84 {
     85     // Special behavior for multiple workers per process.
     86     // FIXME: v8 doesn't support more than one workers per process.
     87     // if (!worker->scriptExecutionContext()->isDocument())
     88     //     return new WorkerMessagingProxy(worker);
     89 
     90     WebWorker* webWorker = 0;
     91     WebWorkerClientImpl* proxy = new WebWorkerClientImpl(worker);
     92 
     93     if (worker->scriptExecutionContext()->isDocument()) {
     94         Document* document = static_cast<Document*>(
     95             worker->scriptExecutionContext());
     96         WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame());
     97         webWorker = webFrame->client()->createWorker(webFrame, proxy);
     98     } else {
     99         WorkerScriptController* controller = WorkerScriptController::controllerForContext();
    100         if (!controller) {
    101             ASSERT_NOT_REACHED();
    102             return 0;
    103         }
    104 
    105         DedicatedWorkerThread* thread = static_cast<DedicatedWorkerThread*>(controller->workerContext()->thread());
    106         WorkerObjectProxy* workerObjectProxy = &thread->workerObjectProxy();
    107         WebWorkerImpl* impl = reinterpret_cast<WebWorkerImpl*>(workerObjectProxy);
    108         webWorker = impl->client()->createWorker(proxy);
    109     }
    110 
    111     proxy->setWebWorker(webWorker);
    112     return proxy;
    113 }
    114 
    115 WebWorkerClientImpl::WebWorkerClientImpl(Worker* worker)
    116     : m_scriptExecutionContext(worker->scriptExecutionContext())
    117     , m_worker(worker)
    118     , m_askedToTerminate(false)
    119     , m_unconfirmedMessageCount(0)
    120     , m_workerContextHadPendingActivity(false)
    121     , m_workerThreadId(currentThread())
    122 {
    123 }
    124 
    125 WebWorkerClientImpl::~WebWorkerClientImpl()
    126 {
    127 }
    128 
    129 void WebWorkerClientImpl::setWebWorker(WebWorker* webWorker)
    130 {
    131     m_webWorker = webWorker;
    132 }
    133 
    134 void WebWorkerClientImpl::startWorkerContext(const KURL& scriptURL,
    135                                              const String& userAgent,
    136                                              const String& sourceCode)
    137 {
    138     // Worker.terminate() could be called from JS before the context is started.
    139     if (m_askedToTerminate)
    140         return;
    141     if (!isMainThread()) {
    142         WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(
    143             &startWorkerContextTask,
    144             this,
    145             scriptURL.string(),
    146             userAgent,
    147             sourceCode));
    148         return;
    149     }
    150     m_webWorker->startWorkerContext(scriptURL, userAgent, sourceCode);
    151 }
    152 
    153 void WebWorkerClientImpl::terminateWorkerContext()
    154 {
    155     if (m_askedToTerminate)
    156         return;
    157     m_askedToTerminate = true;
    158     if (!isMainThread()) {
    159         WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&terminateWorkerContextTask, this));
    160         return;
    161     }
    162     m_webWorker->terminateWorkerContext();
    163 }
    164 
    165 void WebWorkerClientImpl::postMessageToWorkerContext(
    166     PassRefPtr<SerializedScriptValue> message,
    167     PassOwnPtr<MessagePortChannelArray> channels)
    168 {
    169     // Worker.terminate() could be called from JS before the context is started.
    170     if (m_askedToTerminate)
    171         return;
    172     ++m_unconfirmedMessageCount;
    173     if (!isMainThread()) {
    174         WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageToWorkerContextTask,
    175                                                                    this,
    176                                                                    message->toWireString(),
    177                                                                    channels));
    178         return;
    179     }
    180     WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
    181     for (size_t i = 0; i < webChannels.size(); ++i) {
    182         WebMessagePortChannel* webchannel =
    183                         (*channels)[i]->channel()->webChannelRelease();
    184         webchannel->setClient(0);
    185         webChannels[i] = webchannel;
    186     }
    187     m_webWorker->postMessageToWorkerContext(message->toWireString(), webChannels);
    188 }
    189 
    190 bool WebWorkerClientImpl::hasPendingActivity() const
    191 {
    192     return !m_askedToTerminate
    193            && (m_unconfirmedMessageCount || m_workerContextHadPendingActivity);
    194 }
    195 
    196 void WebWorkerClientImpl::workerObjectDestroyed()
    197 {
    198     if (isMainThread()) {
    199         m_webWorker->workerObjectDestroyed();
    200         m_worker = 0;
    201     }
    202     // Even if this is called on the main thread, there could be a queued task for
    203     // this object, so don't delete it right away.
    204     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerObjectDestroyedTask,
    205                                                                this));
    206 }
    207 
    208 void WebWorkerClientImpl::postMessageToWorkerObject(const WebString& message,
    209                                                     const WebMessagePortChannelArray& channels)
    210 {
    211     OwnPtr<MessagePortChannelArray> channels2;
    212     if (channels.size()) {
    213         channels2 = new MessagePortChannelArray(channels.size());
    214         for (size_t i = 0; i < channels.size(); ++i) {
    215             RefPtr<PlatformMessagePortChannel> platform_channel =
    216                             PlatformMessagePortChannel::create(channels[i]);
    217             channels[i]->setClient(platform_channel.get());
    218             (*channels2)[i] = MessagePortChannel::create(platform_channel);
    219         }
    220     }
    221 
    222     if (currentThread() != m_workerThreadId) {
    223         m_scriptExecutionContext->postTask(createCallbackTask(&postMessageToWorkerObjectTask,
    224                                                               this,
    225                                                               String(message),
    226                                                               channels2.release()));
    227         return;
    228     }
    229 
    230     postMessageToWorkerObjectTask(m_scriptExecutionContext.get(), this,
    231                                   message, channels2.release());
    232 }
    233 
    234 void WebWorkerClientImpl::postExceptionToWorkerObject(const WebString& errorMessage,
    235                                                       int lineNumber,
    236                                                       const WebString& sourceURL)
    237 {
    238     if (currentThread() != m_workerThreadId) {
    239         m_scriptExecutionContext->postTask(createCallbackTask(&postExceptionToWorkerObjectTask,
    240                                                               this,
    241                                                               String(errorMessage),
    242                                                               lineNumber,
    243                                                               String(sourceURL)));
    244         return;
    245     }
    246 
    247     bool unhandled = m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
    248                                                                 sourceURL,
    249                                                                 lineNumber));
    250     if (unhandled)
    251         m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0);
    252 }
    253 
    254 void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int destination,
    255                                                            int sourceId,
    256                                                            int messageType,
    257                                                            int messageLevel,
    258                                                            const WebString& message,
    259                                                            int lineNumber,
    260                                                            const WebString& sourceURL)
    261 {
    262     if (currentThread() != m_workerThreadId) {
    263         m_scriptExecutionContext->postTask(createCallbackTask(&postConsoleMessageToWorkerObjectTask,
    264                                                               this,
    265                                                               sourceId,
    266                                                               messageType,
    267                                                               messageLevel,
    268                                                               String(message),
    269                                                               lineNumber,
    270                                                               String(sourceURL)));
    271         return;
    272     }
    273 
    274     m_scriptExecutionContext->addMessage(static_cast<MessageSource>(sourceId),
    275                                          static_cast<MessageType>(messageType),
    276                                          static_cast<MessageLevel>(messageLevel),
    277                                          String(message), lineNumber,
    278                                          String(sourceURL), 0);
    279 }
    280 
    281 void WebWorkerClientImpl::postConsoleMessageToWorkerObject(int sourceId,
    282                                                            int messageType,
    283                                                            int messageLevel,
    284                                                            const WebString& message,
    285                                                            int lineNumber,
    286                                                            const WebString& sourceURL)
    287 {
    288     postConsoleMessageToWorkerObject(0, sourceId, messageType, messageLevel, message, lineNumber, sourceURL);
    289 }
    290 
    291 void WebWorkerClientImpl::confirmMessageFromWorkerObject(bool hasPendingActivity)
    292 {
    293     // unconfirmed_message_count_ can only be updated on the thread where it's
    294     // accessed.  Otherwise there are race conditions with v8's garbage
    295     // collection.
    296     m_scriptExecutionContext->postTask(createCallbackTask(&confirmMessageFromWorkerObjectTask,
    297                                                           this));
    298 }
    299 
    300 void WebWorkerClientImpl::reportPendingActivity(bool hasPendingActivity)
    301 {
    302     // See above comment in confirmMessageFromWorkerObject.
    303     m_scriptExecutionContext->postTask(createCallbackTask(&reportPendingActivityTask,
    304                                                           this,
    305                                                           hasPendingActivity));
    306 }
    307 
    308 void WebWorkerClientImpl::workerContextDestroyed()
    309 {
    310 }
    311 
    312 void WebWorkerClientImpl::workerContextClosed()
    313 {
    314 }
    315 
    316 void WebWorkerClientImpl::startWorkerContextTask(ScriptExecutionContext* context,
    317                                                  WebWorkerClientImpl* thisPtr,
    318                                                  const String& scriptURL,
    319                                                  const String& userAgent,
    320                                                  const String& sourceCode)
    321 {
    322     thisPtr->m_webWorker->startWorkerContext(KURL(ParsedURLString, scriptURL),
    323                                              userAgent, sourceCode);
    324 }
    325 
    326 void WebWorkerClientImpl::terminateWorkerContextTask(ScriptExecutionContext* context,
    327                                                      WebWorkerClientImpl* thisPtr)
    328 {
    329     thisPtr->m_webWorker->terminateWorkerContext();
    330 }
    331 
    332 void WebWorkerClientImpl::postMessageToWorkerContextTask(ScriptExecutionContext* context,
    333                                                          WebWorkerClientImpl* thisPtr,
    334                                                          const String& message,
    335                                                          PassOwnPtr<MessagePortChannelArray> channels)
    336 {
    337     WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
    338 
    339     for (size_t i = 0; i < webChannels.size(); ++i) {
    340         webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
    341         webChannels[i]->setClient(0);
    342     }
    343 
    344     thisPtr->m_webWorker->postMessageToWorkerContext(message, webChannels);
    345 }
    346 
    347 void WebWorkerClientImpl::workerObjectDestroyedTask(ScriptExecutionContext* context,
    348                                                     WebWorkerClientImpl* thisPtr)
    349 {
    350     if (thisPtr->m_worker) // Check we haven't alread called this.
    351         thisPtr->m_webWorker->workerObjectDestroyed();
    352     delete thisPtr;
    353 }
    354 
    355 void WebWorkerClientImpl::postMessageToWorkerObjectTask(
    356                                                         ScriptExecutionContext* context,
    357                                                         WebWorkerClientImpl* thisPtr,
    358                                                         const String& message,
    359                                                         PassOwnPtr<MessagePortChannelArray> channels)
    360 {
    361 
    362     if (thisPtr->m_worker) {
    363         OwnPtr<MessagePortArray> ports =
    364             MessagePort::entanglePorts(*context, channels);
    365         RefPtr<SerializedScriptValue> serializedMessage =
    366             SerializedScriptValue::createFromWire(message);
    367         thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(),
    368                                                               serializedMessage.release()));
    369     }
    370 }
    371 
    372 void WebWorkerClientImpl::postExceptionToWorkerObjectTask(
    373                                                           ScriptExecutionContext* context,
    374                                                           WebWorkerClientImpl* thisPtr,
    375                                                           const String& errorMessage,
    376                                                           int lineNumber,
    377                                                           const String& sourceURL)
    378 {
    379     bool handled = false;
    380     if (thisPtr->m_worker)
    381         handled = thisPtr->m_worker->dispatchEvent(ErrorEvent::create(errorMessage,
    382                                                                       sourceURL,
    383                                                                       lineNumber));
    384     if (!handled)
    385         thisPtr->m_scriptExecutionContext->reportException(errorMessage, lineNumber, sourceURL, 0);
    386 }
    387 
    388 void WebWorkerClientImpl::postConsoleMessageToWorkerObjectTask(ScriptExecutionContext* context,
    389                                                                WebWorkerClientImpl* thisPtr,
    390                                                                int sourceId,
    391                                                                int messageType,
    392                                                                int messageLevel,
    393                                                                const String& message,
    394                                                                int lineNumber,
    395                                                                const String& sourceURL)
    396 {
    397     thisPtr->m_scriptExecutionContext->addMessage(static_cast<MessageSource>(sourceId),
    398                                                   static_cast<MessageType>(messageType),
    399                                                   static_cast<MessageLevel>(messageLevel),
    400                                                   message, lineNumber, sourceURL, 0);
    401 }
    402 
    403 void WebWorkerClientImpl::confirmMessageFromWorkerObjectTask(ScriptExecutionContext* context,
    404                                                              WebWorkerClientImpl* thisPtr)
    405 {
    406     thisPtr->m_unconfirmedMessageCount--;
    407 }
    408 
    409 void WebWorkerClientImpl::reportPendingActivityTask(ScriptExecutionContext* context,
    410                                                     WebWorkerClientImpl* thisPtr,
    411                                                     bool hasPendingActivity)
    412 {
    413     thisPtr->m_workerContextHadPendingActivity = hasPendingActivity;
    414 }
    415 
    416 } // namespace WebKit
    417 
    418 #endif
    419