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