Home | History | Annotate | Download | only in web
      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 "WebSharedWorkerImpl.h"
     33 
     34 #include "public/platform/WebFileError.h"
     35 #include "public/platform/WebMessagePortChannel.h"
     36 #include "public/platform/WebString.h"
     37 #include "public/platform/WebURL.h"
     38 #include "wtf/MainThread.h"
     39 #include "WebDataSourceImpl.h"
     40 #include "WebFrameClient.h"
     41 #include "WebFrameImpl.h"
     42 #include "WebRuntimeFeatures.h"
     43 #include "WebSettings.h"
     44 #include "WebSharedWorkerClient.h"
     45 #include "WebView.h"
     46 #include "WorkerFileSystemClient.h"
     47 #include "core/dom/CrossThreadTask.h"
     48 #include "core/dom/Document.h"
     49 #include "core/dom/MessageEvent.h"
     50 #include "core/dom/MessagePortChannel.h"
     51 #include "core/dom/ScriptExecutionContext.h"
     52 #include "core/dom/default/chromium/PlatformMessagePortChannelChromium.h"
     53 #include "core/html/HTMLFormElement.h"
     54 #include "core/inspector/WorkerDebuggerAgent.h"
     55 #include "core/inspector/WorkerInspectorController.h"
     56 #include "core/loader/FrameLoadRequest.h"
     57 #include "core/loader/FrameLoader.h"
     58 #include "core/page/Page.h"
     59 #include "core/page/PageGroup.h"
     60 #include "core/workers/SharedWorkerGlobalScope.h"
     61 #include "core/workers/SharedWorkerThread.h"
     62 #include "core/workers/WorkerClients.h"
     63 #include "core/workers/WorkerGlobalScope.h"
     64 #include "core/workers/WorkerLoaderProxy.h"
     65 #include "core/workers/WorkerThread.h"
     66 #include "core/workers/WorkerThreadStartupData.h"
     67 #include "modules/webdatabase/DatabaseTask.h"
     68 #include "weborigin/KURL.h"
     69 #include "weborigin/SecurityOrigin.h"
     70 
     71 using namespace WebCore;
     72 
     73 namespace WebKit {
     74 
     75 // This function is called on the main thread to force to initialize some static
     76 // values used in WebKit before any worker thread is started. This is because in
     77 // our worker processs, we do not run any WebKit code in main thread and thus
     78 // when multiple workers try to start at the same time, we might hit crash due
     79 // to contention for initializing static values.
     80 static void initializeWebKitStaticValues()
     81 {
     82     static bool initialized = false;
     83     if (!initialized) {
     84         initialized = true;
     85         // Note that we have to pass a URL with valid protocol in order to follow
     86         // the path to do static value initializations.
     87         RefPtr<SecurityOrigin> origin =
     88             SecurityOrigin::create(KURL(ParsedURLString, "http://localhost"));
     89         origin.release();
     90     }
     91 }
     92 
     93 WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client)
     94     : m_webView(0)
     95     , m_askedToTerminate(false)
     96     , m_client(client)
     97     , m_pauseWorkerContextOnStart(false)
     98 {
     99     initializeWebKitStaticValues();
    100 }
    101 
    102 WebSharedWorkerImpl::~WebSharedWorkerImpl()
    103 {
    104     ASSERT(m_webView);
    105     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
    106     if (webFrame)
    107         webFrame->setClient(0);
    108     m_webView->close();
    109 }
    110 
    111 void WebSharedWorkerImpl::stopWorkerThread()
    112 {
    113     if (m_askedToTerminate)
    114         return;
    115     m_askedToTerminate = true;
    116     if (m_workerThread)
    117         m_workerThread->stop();
    118 }
    119 
    120 void WebSharedWorkerImpl::initializeLoader(const WebURL& url)
    121 {
    122     // Create 'shadow page'. This page is never displayed, it is used to proxy the
    123     // loading requests from the worker context to the rest of WebKit and Chromium
    124     // infrastructure.
    125     ASSERT(!m_webView);
    126     m_webView = WebView::create(0);
    127     m_webView->settings()->setOfflineWebApplicationCacheEnabled(WebRuntimeFeatures::isApplicationCacheEnabled());
    128     // FIXME: Settings information should be passed to the Worker process from Browser process when the worker
    129     // is created (similar to RenderThread::OnCreateNewView).
    130     m_webView->initializeMainFrame(this);
    131 
    132     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
    133 
    134     // Construct substitute data source for the 'shadow page'. We only need it
    135     // to have same origin as the worker so the loading checks work correctly.
    136     CString content("");
    137     int length = static_cast<int>(content.length());
    138     RefPtr<SharedBuffer> buffer(SharedBuffer::create(content.data(), length));
    139     webFrame->frame()->loader()->load(FrameLoadRequest(0, ResourceRequest(url), SubstituteData(buffer, "text/html", "UTF-8", KURL())));
    140 
    141     // This document will be used as 'loading context' for the worker.
    142     m_loadingDocument = webFrame->frame()->document();
    143 }
    144 
    145 void WebSharedWorkerImpl::didCreateDataSource(WebFrame*, WebDataSource* ds)
    146 {
    147     // Tell the loader to load the data into the 'shadow page' synchronously,
    148     // so we can grab the resulting Document right after load.
    149     static_cast<WebDataSourceImpl*>(ds)->setDeferMainResourceDataLoad(false);
    150 }
    151 
    152 WebApplicationCacheHost* WebSharedWorkerImpl::createApplicationCacheHost(WebFrame*, WebApplicationCacheHostClient* appcacheHostClient)
    153 {
    154     if (client())
    155         return client()->createApplicationCacheHost(appcacheHostClient);
    156     return 0;
    157 }
    158 
    159 // WorkerObjectProxy -----------------------------------------------------------
    160 
    161 void WebSharedWorkerImpl::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,
    162                                                     PassOwnPtr<MessagePortChannelArray> channels)
    163 {
    164     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageTask, AllowCrossThreadAccess(this),
    165                                                 message->toWireString(), channels));
    166 }
    167 
    168 void WebSharedWorkerImpl::postMessageTask(ScriptExecutionContext* context,
    169                                           WebSharedWorkerImpl* thisPtr,
    170                                           String message,
    171                                           PassOwnPtr<MessagePortChannelArray> channels)
    172 {
    173     if (!thisPtr->client())
    174         return;
    175 
    176     WebMessagePortChannelArray webChannels(channels ? channels->size() : 0);
    177     for (size_t i = 0; i < webChannels.size(); ++i) {
    178         webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
    179         webChannels[i]->setClient(0);
    180     }
    181 
    182     thisPtr->client()->postMessageToWorkerObject(message, webChannels);
    183 }
    184 
    185 void WebSharedWorkerImpl::postExceptionToWorkerObject(const String& errorMessage,
    186                                                       int lineNumber,
    187                                                       int columnNumber,
    188                                                       const String& sourceURL)
    189 {
    190     WebWorkerBase::dispatchTaskToMainThread(
    191         createCallbackTask(&postExceptionTask, AllowCrossThreadAccess(this),
    192                            errorMessage, lineNumber,
    193                            sourceURL));
    194 }
    195 
    196 void WebSharedWorkerImpl::postExceptionTask(ScriptExecutionContext* context,
    197                                             WebSharedWorkerImpl* thisPtr,
    198                                             const String& errorMessage,
    199                                             int lineNumber, const String& sourceURL)
    200 {
    201     if (!thisPtr->client())
    202         return;
    203 
    204     thisPtr->client()->postExceptionToWorkerObject(errorMessage,
    205                                                    lineNumber,
    206                                                    sourceURL);
    207 }
    208 
    209 void WebSharedWorkerImpl::postConsoleMessageToWorkerObject(MessageSource source,
    210                                                            MessageLevel level,
    211                                                            const String& message,
    212                                                            int lineNumber,
    213                                                            const String& sourceURL)
    214 {
    215     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postConsoleMessageTask, AllowCrossThreadAccess(this), source, level, message, lineNumber, sourceURL));
    216 }
    217 
    218 void WebSharedWorkerImpl::postConsoleMessageTask(ScriptExecutionContext* context,
    219                                                  WebSharedWorkerImpl* thisPtr,
    220                                                  int source,
    221                                                  int level,
    222                                                  const String& message,
    223                                                  int lineNumber,
    224                                                  const String& sourceURL)
    225 {
    226     if (!thisPtr->client())
    227         return;
    228     thisPtr->client()->postConsoleMessageToWorkerObject(source, level, message, lineNumber, sourceURL);
    229 }
    230 
    231 void WebSharedWorkerImpl::postMessageToPageInspector(const String& message)
    232 {
    233     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageToPageInspectorTask, AllowCrossThreadAccess(this), message));
    234 }
    235 
    236 void WebSharedWorkerImpl::postMessageToPageInspectorTask(ScriptExecutionContext*, WebSharedWorkerImpl* thisPtr, const String& message)
    237 {
    238     if (!thisPtr->client())
    239         return;
    240     thisPtr->client()->dispatchDevToolsMessage(message);
    241 }
    242 
    243 void WebSharedWorkerImpl::updateInspectorStateCookie(const WTF::String& cookie)
    244 {
    245     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&updateInspectorStateCookieTask, AllowCrossThreadAccess(this), cookie));
    246 }
    247 
    248 void WebSharedWorkerImpl::updateInspectorStateCookieTask(ScriptExecutionContext*, WebSharedWorkerImpl* thisPtr, const String& cookie)
    249 {
    250     if (!thisPtr->client())
    251         return;
    252     thisPtr->client()->saveDevToolsAgentState(cookie);
    253 }
    254 
    255 void WebSharedWorkerImpl::confirmMessageFromWorkerObject(bool hasPendingActivity)
    256 {
    257     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, AllowCrossThreadAccess(this),
    258                                             hasPendingActivity));
    259 }
    260 
    261 void WebSharedWorkerImpl::confirmMessageTask(ScriptExecutionContext* context,
    262                                              WebSharedWorkerImpl* thisPtr,
    263                                              bool hasPendingActivity)
    264 {
    265     if (!thisPtr->client())
    266         return;
    267     thisPtr->client()->confirmMessageFromWorkerObject(hasPendingActivity);
    268 }
    269 
    270 void WebSharedWorkerImpl::reportPendingActivity(bool hasPendingActivity)
    271 {
    272     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask,
    273                                             AllowCrossThreadAccess(this),
    274                                             hasPendingActivity));
    275 }
    276 
    277 void WebSharedWorkerImpl::reportPendingActivityTask(ScriptExecutionContext* context,
    278                                                     WebSharedWorkerImpl* thisPtr,
    279                                                     bool hasPendingActivity)
    280 {
    281     if (!thisPtr->client())
    282         return;
    283     thisPtr->client()->reportPendingActivity(hasPendingActivity);
    284 }
    285 
    286 void WebSharedWorkerImpl::workerGlobalScopeClosed()
    287 {
    288     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerGlobalScopeClosedTask,
    289                                             AllowCrossThreadAccess(this)));
    290 }
    291 
    292 void WebSharedWorkerImpl::workerGlobalScopeClosedTask(ScriptExecutionContext* context,
    293                                                   WebSharedWorkerImpl* thisPtr)
    294 {
    295     if (thisPtr->client())
    296         thisPtr->client()->workerContextClosed();
    297 
    298     thisPtr->stopWorkerThread();
    299 }
    300 
    301 void WebSharedWorkerImpl::workerGlobalScopeDestroyed()
    302 {
    303     WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerGlobalScopeDestroyedTask,
    304                                                                AllowCrossThreadAccess(this)));
    305 }
    306 
    307 void WebSharedWorkerImpl::workerGlobalScopeDestroyedTask(ScriptExecutionContext* context,
    308                                                      WebSharedWorkerImpl* thisPtr)
    309 {
    310     if (thisPtr->client())
    311         thisPtr->client()->workerContextDestroyed();
    312     // The lifetime of this proxy is controlled by the worker context.
    313     delete thisPtr;
    314 }
    315 
    316 // WorkerLoaderProxy -----------------------------------------------------------
    317 
    318 void WebSharedWorkerImpl::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
    319 {
    320     ASSERT(m_loadingDocument->isDocument());
    321     m_loadingDocument->postTask(task);
    322 }
    323 
    324 bool WebSharedWorkerImpl::postTaskForModeToWorkerGlobalScope(
    325     PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
    326 {
    327     m_workerThread->runLoop().postTaskForMode(task, mode);
    328     return true;
    329 }
    330 
    331 WebWorkerBase* WebSharedWorkerImpl::toWebWorkerBase()
    332 {
    333     return this;
    334 }
    335 
    336 
    337 bool WebSharedWorkerImpl::isStarted()
    338 {
    339     // Should not ever be called from the worker thread (this API is only called on WebSharedWorkerProxy on the renderer thread).
    340     ASSERT_NOT_REACHED();
    341     return workerThread();
    342 }
    343 
    344 void WebSharedWorkerImpl::connect(WebMessagePortChannel* webChannel, ConnectListener* listener)
    345 {
    346     // Convert the WebMessagePortChanel to a WebCore::MessagePortChannel.
    347     RefPtr<PlatformMessagePortChannel> platform_channel =
    348         PlatformMessagePortChannel::create(webChannel);
    349     webChannel->setClient(platform_channel.get());
    350     OwnPtr<MessagePortChannel> channel =
    351         MessagePortChannel::create(platform_channel);
    352 
    353     workerThread()->runLoop().postTask(
    354         createCallbackTask(&connectTask, channel.release()));
    355     if (listener)
    356         listener->connected();
    357 }
    358 
    359 void WebSharedWorkerImpl::connectTask(ScriptExecutionContext* context, PassOwnPtr<MessagePortChannel> channel)
    360 {
    361     // Wrap the passed-in channel in a MessagePort, and send it off via a connect event.
    362     RefPtr<MessagePort> port = MessagePort::create(*context);
    363     port->entangle(channel);
    364     WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    365     ASSERT_WITH_SECURITY_IMPLICATION(workerGlobalScope->isSharedWorkerGlobalScope());
    366     workerGlobalScope->dispatchEvent(createConnectEvent(port));
    367 }
    368 
    369 void WebSharedWorkerImpl::startWorkerContext(const WebURL& url, const WebString& name, const WebString& userAgent, const WebString& sourceCode, const WebString& contentSecurityPolicy, WebContentSecurityPolicyType policyType, long long)
    370 {
    371     initializeLoader(url);
    372 
    373     WorkerThreadStartMode startMode = m_pauseWorkerContextOnStart ? PauseWorkerGlobalScopeOnStart : DontPauseWorkerGlobalScopeOnStart;
    374     OwnPtr<WorkerClients> workerClients = WorkerClients::create();
    375     provideLocalFileSystemToWorker(workerClients.get(), WorkerFileSystemClient::create());
    376     OwnPtr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(url, userAgent, sourceCode, startMode, contentSecurityPolicy, static_cast<WebCore::ContentSecurityPolicy::HeaderType>(policyType), workerClients.release());
    377     setWorkerThread(SharedWorkerThread::create(name, *this, *this, startupData.release()));
    378 
    379     workerThread()->start();
    380 }
    381 
    382 void WebSharedWorkerImpl::terminateWorkerContext()
    383 {
    384     stopWorkerThread();
    385 }
    386 
    387 void WebSharedWorkerImpl::clientDestroyed()
    388 {
    389     m_client = 0;
    390 }
    391 
    392 void WebSharedWorkerImpl::pauseWorkerContextOnStart()
    393 {
    394     m_pauseWorkerContextOnStart = true;
    395 }
    396 
    397 static void resumeWorkerContextTask(ScriptExecutionContext* context, bool)
    398 {
    399     toWorkerGlobalScope(context)->workerInspectorController()->resume();
    400 }
    401 
    402 void WebSharedWorkerImpl::resumeWorkerContext()
    403 {
    404     m_pauseWorkerContextOnStart = false;
    405     if (workerThread())
    406         workerThread()->runLoop().postTaskForMode(createCallbackTask(resumeWorkerContextTask, true), WorkerDebuggerAgent::debuggerTaskMode);
    407 }
    408 
    409 static void connectToWorkerContextInspectorTask(ScriptExecutionContext* context, bool)
    410 {
    411     toWorkerGlobalScope(context)->workerInspectorController()->connectFrontend();
    412 }
    413 
    414 void WebSharedWorkerImpl::attachDevTools()
    415 {
    416     workerThread()->runLoop().postTaskForMode(createCallbackTask(connectToWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
    417 }
    418 
    419 static void reconnectToWorkerContextInspectorTask(ScriptExecutionContext* context, const String& savedState)
    420 {
    421     WorkerInspectorController* ic = toWorkerGlobalScope(context)->workerInspectorController();
    422     ic->restoreInspectorStateFromCookie(savedState);
    423     ic->resume();
    424 }
    425 
    426 void WebSharedWorkerImpl::reattachDevTools(const WebString& savedState)
    427 {
    428     workerThread()->runLoop().postTaskForMode(createCallbackTask(reconnectToWorkerContextInspectorTask, String(savedState)), WorkerDebuggerAgent::debuggerTaskMode);
    429 }
    430 
    431 static void disconnectFromWorkerContextInspectorTask(ScriptExecutionContext* context, bool)
    432 {
    433     toWorkerGlobalScope(context)->workerInspectorController()->disconnectFrontend();
    434 }
    435 
    436 void WebSharedWorkerImpl::detachDevTools()
    437 {
    438     workerThread()->runLoop().postTaskForMode(createCallbackTask(disconnectFromWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode);
    439 }
    440 
    441 static void dispatchOnInspectorBackendTask(ScriptExecutionContext* context, const String& message)
    442 {
    443     toWorkerGlobalScope(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
    444 }
    445 
    446 void WebSharedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
    447 {
    448     workerThread()->runLoop().postTaskForMode(createCallbackTask(dispatchOnInspectorBackendTask, String(message)), WorkerDebuggerAgent::debuggerTaskMode);
    449     WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(workerThread());
    450 }
    451 
    452 WebSharedWorker* WebSharedWorker::create(WebSharedWorkerClient* client)
    453 {
    454     return new WebSharedWorkerImpl(client);
    455 }
    456 
    457 } // namespace WebKit
    458