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 "CrossThreadTask.h"
     35 #include "DatabaseTask.h"
     36 #include "MessagePortChannel.h"
     37 #include "PlatformMessagePortChannel.h"
     38 
     39 #include "WebDataSourceImpl.h"
     40 #include "WebFileError.h"
     41 #include "WebFrameClient.h"
     42 #include "WebFrameImpl.h"
     43 #include "WebMessagePortChannel.h"
     44 #include "WebRuntimeFeatures.h"
     45 #include "WebSettings.h"
     46 #include "WebView.h"
     47 #include "WebWorkerClient.h"
     48 
     49 #include "WorkerContext.h"
     50 #include "WorkerFileSystemCallbacksBridge.h"
     51 #include "WorkerScriptController.h"
     52 #include "WorkerThread.h"
     53 #include <wtf/MainThread.h>
     54 
     55 using namespace WebCore;
     56 
     57 namespace WebKit {
     58 
     59 #if ENABLE(WORKERS)
     60 
     61 static const char allowDatabaseMode[] = "allowDatabaseMode";
     62 static const char openFileSystemMode[] = "openFileSystemMode";
     63 
     64 namespace {
     65 
     66 // This class is used to route the result of the WebWorkerBase::allowDatabase
     67 // call back to the worker context.
     68 class AllowDatabaseMainThreadBridge : public ThreadSafeRefCounted<AllowDatabaseMainThreadBridge> {
     69 public:
     70     static PassRefPtr<AllowDatabaseMainThreadBridge> create(WebWorkerBase* worker, const WTF::String& mode, WebCommonWorkerClient* commonClient, WebFrame* frame, const WTF::String& name, const WTF::String& displayName, unsigned long estimatedSize)
     71     {
     72         return adoptRef(new AllowDatabaseMainThreadBridge(worker, mode, commonClient, frame, name, displayName, estimatedSize));
     73     }
     74 
     75     // These methods are invoked on the worker context.
     76     void cancel()
     77     {
     78         MutexLocker locker(m_mutex);
     79         m_worker = 0;
     80     }
     81 
     82     bool result()
     83     {
     84         return m_result;
     85     }
     86 
     87     // This method is invoked on the main thread.
     88     void signalCompleted(bool result)
     89     {
     90         MutexLocker locker(m_mutex);
     91         if (m_worker)
     92             m_worker->postTaskForModeToWorkerContext(createCallbackTask(&didComplete, this, result), m_mode);
     93     }
     94 
     95 private:
     96     AllowDatabaseMainThreadBridge(WebWorkerBase* worker, const WTF::String& mode, WebCommonWorkerClient* commonClient, WebFrame* frame, const WTF::String& name, const WTF::String& displayName, unsigned long estimatedSize)
     97         : m_worker(worker)
     98         , m_mode(mode)
     99     {
    100         worker->dispatchTaskToMainThread(createCallbackTask(&allowDatabaseTask, commonClient, frame, String(name), String(displayName), estimatedSize, this));
    101     }
    102 
    103     static void allowDatabaseTask(WebCore::ScriptExecutionContext* context, WebCommonWorkerClient* commonClient, WebFrame* frame, const WTF::String name, const WTF::String displayName, unsigned long estimatedSize, PassRefPtr<AllowDatabaseMainThreadBridge> bridge)
    104     {
    105         if (!commonClient)
    106             bridge->signalCompleted(false);
    107         else
    108             bridge->signalCompleted(commonClient->allowDatabase(frame, name, displayName, estimatedSize));
    109     }
    110 
    111     static void didComplete(WebCore::ScriptExecutionContext* context, PassRefPtr<AllowDatabaseMainThreadBridge> bridge, bool result)
    112     {
    113         bridge->m_result = result;
    114     }
    115 
    116     bool m_result;
    117     Mutex m_mutex;
    118     WebWorkerBase* m_worker;
    119     WTF::String m_mode;
    120 };
    121 
    122 }
    123 
    124 // This function is called on the main thread to force to initialize some static
    125 // values used in WebKit before any worker thread is started. This is because in
    126 // our worker processs, we do not run any WebKit code in main thread and thus
    127 // when multiple workers try to start at the same time, we might hit crash due
    128 // to contention for initializing static values.
    129 static void initializeWebKitStaticValues()
    130 {
    131     static bool initialized = false;
    132     if (!initialized) {
    133         initialized = true;
    134         // Note that we have to pass a URL with valid protocol in order to follow
    135         // the path to do static value initializations.
    136         RefPtr<SecurityOrigin> origin =
    137             SecurityOrigin::create(KURL(ParsedURLString, "http://localhost"));
    138         origin.release();
    139     }
    140 }
    141 
    142 WebWorkerBase::WebWorkerBase()
    143     : m_webView(0)
    144     , m_askedToTerminate(false)
    145 {
    146     initializeWebKitStaticValues();
    147 }
    148 
    149 WebWorkerBase::~WebWorkerBase()
    150 {
    151     ASSERT(m_webView);
    152     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
    153     if (webFrame)
    154         webFrame->setClient(0);
    155     m_webView->close();
    156 }
    157 
    158 void WebWorkerBase::stopWorkerThread()
    159 {
    160     if (m_askedToTerminate)
    161         return;
    162     m_askedToTerminate = true;
    163     if (m_workerThread)
    164         m_workerThread->stop();
    165 }
    166 
    167 void WebWorkerBase::initializeLoader(const WebURL& url)
    168 {
    169     // Create 'shadow page'. This page is never displayed, it is used to proxy the
    170     // loading requests from the worker context to the rest of WebKit and Chromium
    171     // infrastructure.
    172     ASSERT(!m_webView);
    173     m_webView = WebView::create(0);
    174     m_webView->settings()->setOfflineWebApplicationCacheEnabled(WebRuntimeFeatures::isApplicationCacheEnabled());
    175     m_webView->initializeMainFrame(this);
    176 
    177     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
    178 
    179     // Construct substitute data source for the 'shadow page'. We only need it
    180     // to have same origin as the worker so the loading checks work correctly.
    181     CString content("");
    182     int len = static_cast<int>(content.length());
    183     RefPtr<SharedBuffer> buf(SharedBuffer::create(content.data(), len));
    184     SubstituteData substData(buf, String("text/html"), String("UTF-8"), KURL());
    185     webFrame->frame()->loader()->load(ResourceRequest(url), substData, false);
    186 
    187     // This document will be used as 'loading context' for the worker.
    188     m_loadingDocument = webFrame->frame()->document();
    189 }
    190 
    191 void WebWorkerBase::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)
    192 {
    193     callOnMainThread(invokeTaskMethod, task.leakPtr());
    194 }
    195 
    196 void WebWorkerBase::invokeTaskMethod(void* param)
    197 {
    198     ScriptExecutionContext::Task* task =
    199         static_cast<ScriptExecutionContext::Task*>(param);
    200     task->performTask(0);
    201     delete task;
    202 }
    203 
    204 void WebWorkerBase::didCreateDataSource(WebFrame*, WebDataSource* ds)
    205 {
    206     // Tell the loader to load the data into the 'shadow page' synchronously,
    207     // so we can grab the resulting Document right after load.
    208     static_cast<WebDataSourceImpl*>(ds)->setDeferMainResourceDataLoad(false);
    209 }
    210 
    211 WebApplicationCacheHost* WebWorkerBase::createApplicationCacheHost(WebFrame*, WebApplicationCacheHostClient* appcacheHostClient)
    212 {
    213     if (commonClient())
    214         return commonClient()->createApplicationCacheHost(appcacheHostClient);
    215     return 0;
    216 }
    217 
    218 bool WebWorkerBase::allowDatabase(WebFrame*, const WebString& name, const WebString& displayName, unsigned long estimatedSize)
    219 {
    220     WorkerRunLoop& runLoop = m_workerThread->runLoop();
    221     WorkerScriptController* controller = WorkerScriptController::controllerForContext();
    222     WorkerContext* workerContext = controller->workerContext();
    223 
    224     // Create a unique mode just for this synchronous call.
    225     String mode = allowDatabaseMode;
    226     mode.append(String::number(runLoop.createUniqueId()));
    227 
    228     RefPtr<AllowDatabaseMainThreadBridge> bridge = AllowDatabaseMainThreadBridge::create(this, mode, commonClient(), m_webView->mainFrame(), String(name), String(displayName), estimatedSize);
    229 
    230     // Either the bridge returns, or the queue gets terminated.
    231     if (runLoop.runInMode(workerContext, mode) == MessageQueueTerminated) {
    232         bridge->cancel();
    233         return false;
    234     }
    235 
    236     return bridge->result();
    237 }
    238 
    239 #if ENABLE(FILE_SYSTEM)
    240 void WebWorkerBase::openFileSystemForWorker(WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks, bool synchronous)
    241 {
    242     WorkerRunLoop& runLoop = m_workerThread->runLoop();
    243     WorkerScriptController* controller = WorkerScriptController::controllerForContext();
    244     WorkerContext* workerContext = controller->workerContext();
    245 
    246     // Create a unique mode for this openFileSystem call.
    247     String mode = openFileSystemMode;
    248     mode.append(String::number(runLoop.createUniqueId()));
    249 
    250     RefPtr<WorkerFileSystemCallbacksBridge> bridge = WorkerFileSystemCallbacksBridge::create(this, workerContext, callbacks);
    251     bridge->postOpenFileSystemToMainThread(commonClient(), type, size, create, mode);
    252 
    253     if (synchronous) {
    254         if (runLoop.runInMode(workerContext, mode) == MessageQueueTerminated)
    255             bridge->stop();
    256     }
    257 }
    258 #endif
    259 
    260 // WorkerObjectProxy -----------------------------------------------------------
    261 
    262 void WebWorkerBase::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,
    263                                               PassOwnPtr<MessagePortChannelArray> channels)
    264 {
    265     dispatchTaskToMainThread(createCallbackTask(&postMessageTask, this,
    266                                                 message->toWireString(), channels));
    267 }
    268 
    269 void WebWorkerBase::postMessageTask(ScriptExecutionContext* context,
    270                                     WebWorkerBase* thisPtr,
    271                                     String message,
    272                                     PassOwnPtr<MessagePortChannelArray> channels)
    273 {
    274     if (!thisPtr->client())
    275         return;
    276 
    277     WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
    278     for (size_t i = 0; i < webChannels.size(); ++i) {
    279         webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
    280         webChannels[i]->setClient(0);
    281     }
    282 
    283     thisPtr->client()->postMessageToWorkerObject(message, webChannels);
    284 }
    285 
    286 void WebWorkerBase::postExceptionToWorkerObject(const String& errorMessage,
    287                                                 int lineNumber,
    288                                                 const String& sourceURL)
    289 {
    290     dispatchTaskToMainThread(createCallbackTask(&postExceptionTask, this,
    291                                                 errorMessage, lineNumber,
    292                                                 sourceURL));
    293 }
    294 
    295 void WebWorkerBase::postExceptionTask(ScriptExecutionContext* context,
    296                                       WebWorkerBase* thisPtr,
    297                                       const String& errorMessage,
    298                                       int lineNumber, const String& sourceURL)
    299 {
    300     if (!thisPtr->commonClient())
    301         return;
    302 
    303     thisPtr->commonClient()->postExceptionToWorkerObject(errorMessage,
    304                                                          lineNumber,
    305                                                          sourceURL);
    306 }
    307 
    308 void WebWorkerBase::postConsoleMessageToWorkerObject(MessageSource source,
    309                                                      MessageType type,
    310                                                      MessageLevel level,
    311                                                      const String& message,
    312                                                      int lineNumber,
    313                                                      const String& sourceURL)
    314 {
    315     dispatchTaskToMainThread(createCallbackTask(&postConsoleMessageTask, this,
    316                                                 source, type, level,
    317                                                 message, lineNumber, sourceURL));
    318 }
    319 
    320 void WebWorkerBase::postConsoleMessageTask(ScriptExecutionContext* context,
    321                                            WebWorkerBase* thisPtr,
    322                                            int source,
    323                                            int type, int level,
    324                                            const String& message,
    325                                            int lineNumber,
    326                                            const String& sourceURL)
    327 {
    328     if (!thisPtr->commonClient())
    329         return;
    330     thisPtr->commonClient()->postConsoleMessageToWorkerObject(source,
    331                                                               type, level, message,
    332                                                               lineNumber, sourceURL);
    333 }
    334 
    335 void WebWorkerBase::confirmMessageFromWorkerObject(bool hasPendingActivity)
    336 {
    337     dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, this,
    338                                                 hasPendingActivity));
    339 }
    340 
    341 void WebWorkerBase::confirmMessageTask(ScriptExecutionContext* context,
    342                                        WebWorkerBase* thisPtr,
    343                                        bool hasPendingActivity)
    344 {
    345     if (!thisPtr->client())
    346         return;
    347     thisPtr->client()->confirmMessageFromWorkerObject(hasPendingActivity);
    348 }
    349 
    350 void WebWorkerBase::reportPendingActivity(bool hasPendingActivity)
    351 {
    352     dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask,
    353                                                 this, hasPendingActivity));
    354 }
    355 
    356 void WebWorkerBase::reportPendingActivityTask(ScriptExecutionContext* context,
    357                                               WebWorkerBase* thisPtr,
    358                                               bool hasPendingActivity)
    359 {
    360     if (!thisPtr->client())
    361         return;
    362     thisPtr->client()->reportPendingActivity(hasPendingActivity);
    363 }
    364 
    365 void WebWorkerBase::workerContextClosed()
    366 {
    367     dispatchTaskToMainThread(createCallbackTask(&workerContextClosedTask,
    368                                                 this));
    369 }
    370 
    371 void WebWorkerBase::workerContextClosedTask(ScriptExecutionContext* context,
    372                                             WebWorkerBase* thisPtr)
    373 {
    374     if (thisPtr->commonClient())
    375         thisPtr->commonClient()->workerContextClosed();
    376 
    377     thisPtr->stopWorkerThread();
    378 }
    379 
    380 void WebWorkerBase::workerContextDestroyed()
    381 {
    382     dispatchTaskToMainThread(createCallbackTask(&workerContextDestroyedTask,
    383                                                 this));
    384 }
    385 
    386 void WebWorkerBase::workerContextDestroyedTask(ScriptExecutionContext* context,
    387                                                WebWorkerBase* thisPtr)
    388 {
    389     if (thisPtr->commonClient())
    390         thisPtr->commonClient()->workerContextDestroyed();
    391     // The lifetime of this proxy is controlled by the worker context.
    392     delete thisPtr;
    393 }
    394 
    395 // WorkerLoaderProxy -----------------------------------------------------------
    396 
    397 void WebWorkerBase::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
    398 {
    399     ASSERT(m_loadingDocument->isDocument());
    400     m_loadingDocument->postTask(task);
    401 }
    402 
    403 void WebWorkerBase::postTaskForModeToWorkerContext(
    404     PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
    405 {
    406     m_workerThread->runLoop().postTaskForMode(task, mode);
    407 }
    408 
    409 #endif // ENABLE(WORKERS)
    410 
    411 } // namespace WebKit
    412