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 "WebWorkerBase.h"
     33 
     34 #include "GenericWorkerTask.h"
     35 #include "MessagePortChannel.h"
     36 #include "PlatformMessagePortChannel.h"
     37 
     38 #include "WebDataSourceImpl.h"
     39 #include "WebFrameClient.h"
     40 #include "WebFrameImpl.h"
     41 #include "WebMessagePortChannel.h"
     42 #include "WebView.h"
     43 #include "WebWorkerClient.h"
     44 
     45 #include "WorkerThread.h"
     46 #include <wtf/MainThread.h>
     47 
     48 using namespace WebCore;
     49 
     50 namespace WebKit {
     51 
     52 #if ENABLE(WORKERS)
     53 
     54 // Dummy WebViewDelegate - we only need it in Worker process to load a
     55 // 'shadow page' which will initialize WebCore loader.
     56 class WorkerWebFrameClient : public WebFrameClient {
     57 public:
     58     // Tell the loader to load the data into the 'shadow page' synchronously,
     59     // so we can grab the resulting Document right after load.
     60     virtual void didCreateDataSource(WebFrame* frame, WebDataSource* ds)
     61     {
     62         static_cast<WebDataSourceImpl*>(ds)->setDeferMainResourceDataLoad(false);
     63     }
     64 
     65     // Lazy allocate and leak this instance.
     66     static WorkerWebFrameClient* sharedInstance()
     67     {
     68         static WorkerWebFrameClient client;
     69         return &client;
     70     }
     71 
     72 private:
     73     WorkerWebFrameClient()
     74     {
     75     }
     76 };
     77 
     78 // This function is called on the main thread to force to initialize some static
     79 // values used in WebKit before any worker thread is started. This is because in
     80 // our worker processs, we do not run any WebKit code in main thread and thus
     81 // when multiple workers try to start at the same time, we might hit crash due
     82 // to contention for initializing static values.
     83 static void initializeWebKitStaticValues()
     84 {
     85     static bool initialized = false;
     86     if (!initialized) {
     87         initialized = true;
     88         // Note that we have to pass a URL with valid protocol in order to follow
     89         // the path to do static value initializations.
     90         RefPtr<SecurityOrigin> origin =
     91             SecurityOrigin::create(KURL(ParsedURLString, "http://localhost"));
     92         origin.release();
     93     }
     94 }
     95 
     96 WebWorkerBase::WebWorkerBase()
     97     : m_webView(0)
     98     , m_askedToTerminate(false)
     99 {
    100     initializeWebKitStaticValues();
    101 }
    102 
    103 WebWorkerBase::~WebWorkerBase()
    104 {
    105     ASSERT(m_webView);
    106     m_webView->close();
    107 }
    108 
    109 void WebWorkerBase::stopWorkerThread()
    110 {
    111     if (m_askedToTerminate)
    112         return;
    113     m_askedToTerminate = true;
    114     if (m_workerThread)
    115         m_workerThread->stop();
    116 }
    117 
    118 void WebWorkerBase::initializeLoader(const WebURL& url)
    119 {
    120     // Create 'shadow page'. This page is never displayed, it is used to proxy the
    121     // loading requests from the worker context to the rest of WebKit and Chromium
    122     // infrastructure.
    123     ASSERT(!m_webView);
    124     m_webView = WebView::create(0);
    125     m_webView->initializeMainFrame(WorkerWebFrameClient::sharedInstance());
    126 
    127     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
    128 
    129     // Construct substitute data source for the 'shadow page'. We only need it
    130     // to have same origin as the worker so the loading checks work correctly.
    131     CString content("");
    132     int len = static_cast<int>(content.length());
    133     RefPtr<SharedBuffer> buf(SharedBuffer::create(content.data(), len));
    134     SubstituteData substData(buf, String("text/html"), String("UTF-8"), KURL());
    135     webFrame->frame()->loader()->load(ResourceRequest(url), substData, false);
    136 
    137     // This document will be used as 'loading context' for the worker.
    138     m_loadingDocument = webFrame->frame()->document();
    139 }
    140 
    141 void WebWorkerBase::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)
    142 {
    143     return callOnMainThread(invokeTaskMethod, task.release());
    144 }
    145 
    146 void WebWorkerBase::invokeTaskMethod(void* param)
    147 {
    148     ScriptExecutionContext::Task* task =
    149         static_cast<ScriptExecutionContext::Task*>(param);
    150     task->performTask(0);
    151     delete task;
    152 }
    153 
    154 // WorkerObjectProxy -----------------------------------------------------------
    155 
    156 void WebWorkerBase::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,
    157                                               PassOwnPtr<MessagePortChannelArray> channels)
    158 {
    159     dispatchTaskToMainThread(createCallbackTask(&postMessageTask, this,
    160                                                 message->toWireString(), channels));
    161 }
    162 
    163 void WebWorkerBase::postMessageTask(ScriptExecutionContext* context,
    164                                     WebWorkerBase* thisPtr,
    165                                     String message,
    166                                     PassOwnPtr<MessagePortChannelArray> channels)
    167 {
    168     if (!thisPtr->client())
    169         return;
    170 
    171     WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
    172     for (size_t i = 0; i < webChannels.size(); ++i) {
    173         webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
    174         webChannels[i]->setClient(0);
    175     }
    176 
    177     thisPtr->client()->postMessageToWorkerObject(message, webChannels);
    178 }
    179 
    180 void WebWorkerBase::postExceptionToWorkerObject(const String& errorMessage,
    181                                                 int lineNumber,
    182                                                 const String& sourceURL)
    183 {
    184     dispatchTaskToMainThread(createCallbackTask(&postExceptionTask, this,
    185                                                 errorMessage, lineNumber,
    186                                                 sourceURL));
    187 }
    188 
    189 void WebWorkerBase::postExceptionTask(ScriptExecutionContext* context,
    190                                       WebWorkerBase* thisPtr,
    191                                       const String& errorMessage,
    192                                       int lineNumber, const String& sourceURL)
    193 {
    194     if (!thisPtr->commonClient())
    195         return;
    196 
    197     thisPtr->commonClient()->postExceptionToWorkerObject(errorMessage,
    198                                                          lineNumber,
    199                                                          sourceURL);
    200 }
    201 
    202 void WebWorkerBase::postConsoleMessageToWorkerObject(MessageDestination destination,
    203                                                      MessageSource source,
    204                                                      MessageType type,
    205                                                      MessageLevel level,
    206                                                      const String& message,
    207                                                      int lineNumber,
    208                                                      const String& sourceURL)
    209 {
    210     dispatchTaskToMainThread(createCallbackTask(&postConsoleMessageTask, this,
    211                                                 static_cast<int>(destination),
    212                                                 static_cast<int>(source),
    213                                                 static_cast<int>(type),
    214                                                 static_cast<int>(level),
    215                                                 message, lineNumber, sourceURL));
    216 }
    217 
    218 void WebWorkerBase::postConsoleMessageTask(ScriptExecutionContext* context,
    219                                            WebWorkerBase* thisPtr,
    220                                            int destination, int source,
    221                                            int type, int level,
    222                                            const String& message,
    223                                            int lineNumber,
    224                                            const String& sourceURL)
    225 {
    226     if (!thisPtr->commonClient())
    227         return;
    228     thisPtr->commonClient()->postConsoleMessageToWorkerObject(destination, source,
    229                                                               type, level, message,
    230                                                               lineNumber, sourceURL);
    231 }
    232 
    233 void WebWorkerBase::confirmMessageFromWorkerObject(bool hasPendingActivity)
    234 {
    235     dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, this,
    236                                                 hasPendingActivity));
    237 }
    238 
    239 void WebWorkerBase::confirmMessageTask(ScriptExecutionContext* context,
    240                                        WebWorkerBase* thisPtr,
    241                                        bool hasPendingActivity)
    242 {
    243     if (!thisPtr->client())
    244         return;
    245     thisPtr->client()->confirmMessageFromWorkerObject(hasPendingActivity);
    246 }
    247 
    248 void WebWorkerBase::reportPendingActivity(bool hasPendingActivity)
    249 {
    250     dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask,
    251                                                 this, hasPendingActivity));
    252 }
    253 
    254 void WebWorkerBase::reportPendingActivityTask(ScriptExecutionContext* context,
    255                                               WebWorkerBase* thisPtr,
    256                                               bool hasPendingActivity)
    257 {
    258     if (!thisPtr->client())
    259         return;
    260     thisPtr->client()->reportPendingActivity(hasPendingActivity);
    261 }
    262 
    263 void WebWorkerBase::workerContextClosed()
    264 {
    265     dispatchTaskToMainThread(createCallbackTask(&workerContextClosedTask,
    266                                                 this));
    267 }
    268 
    269 void WebWorkerBase::workerContextClosedTask(ScriptExecutionContext* context,
    270                                             WebWorkerBase* thisPtr)
    271 {
    272     if (thisPtr->commonClient())
    273         thisPtr->commonClient()->workerContextClosed();
    274 
    275     thisPtr->stopWorkerThread();
    276 }
    277 
    278 void WebWorkerBase::workerContextDestroyed()
    279 {
    280     dispatchTaskToMainThread(createCallbackTask(&workerContextDestroyedTask,
    281                                                 this));
    282 }
    283 
    284 void WebWorkerBase::workerContextDestroyedTask(ScriptExecutionContext* context,
    285                                                WebWorkerBase* thisPtr)
    286 {
    287     if (thisPtr->commonClient())
    288         thisPtr->commonClient()->workerContextDestroyed();
    289     // The lifetime of this proxy is controlled by the worker context.
    290     delete thisPtr;
    291 }
    292 
    293 // WorkerLoaderProxy -----------------------------------------------------------
    294 
    295 void WebWorkerBase::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
    296 {
    297     ASSERT(m_loadingDocument->isDocument());
    298     m_loadingDocument->postTask(task);
    299 }
    300 
    301 void WebWorkerBase::postTaskForModeToWorkerContext(
    302     PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
    303 {
    304     m_workerThread->runLoop().postTaskForMode(task, mode);
    305 }
    306 
    307 #endif // ENABLE(WORKERS)
    308 
    309 } // namespace WebKit
    310