Home | History | Annotate | Download | only in web
      1 /*
      2  * Copyright (C) 2013 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 "web/WebEmbeddedWorkerImpl.h"
     33 
     34 #include "core/dom/CrossThreadTask.h"
     35 #include "core/dom/Document.h"
     36 #include "core/inspector/WorkerDebuggerAgent.h"
     37 #include "core/inspector/WorkerInspectorController.h"
     38 #include "core/loader/FrameLoadRequest.h"
     39 #include "core/loader/SubstituteData.h"
     40 #include "core/workers/WorkerClients.h"
     41 #include "core/workers/WorkerGlobalScope.h"
     42 #include "core/workers/WorkerLoaderProxy.h"
     43 #include "core/workers/WorkerScriptLoader.h"
     44 #include "core/workers/WorkerScriptLoaderClient.h"
     45 #include "core/workers/WorkerThreadStartupData.h"
     46 #include "modules/serviceworkers/ServiceWorkerThread.h"
     47 #include "platform/NotImplemented.h"
     48 #include "platform/SharedBuffer.h"
     49 #include "platform/heap/Handle.h"
     50 #include "platform/network/ContentSecurityPolicyParsers.h"
     51 #include "public/web/WebServiceWorkerContextClient.h"
     52 #include "public/web/WebServiceWorkerNetworkProvider.h"
     53 #include "public/web/WebSettings.h"
     54 #include "public/web/WebView.h"
     55 #include "public/web/WebWorkerPermissionClientProxy.h"
     56 #include "web/ServiceWorkerGlobalScopeClientImpl.h"
     57 #include "web/ServiceWorkerGlobalScopeProxy.h"
     58 #include "web/WebDataSourceImpl.h"
     59 #include "web/WebLocalFrameImpl.h"
     60 #include "web/WorkerPermissionClient.h"
     61 #include "wtf/Functional.h"
     62 
     63 using namespace WebCore;
     64 
     65 namespace blink {
     66 
     67 // A thin wrapper for one-off script loading.
     68 class WebEmbeddedWorkerImpl::Loader : public WorkerScriptLoaderClient {
     69 public:
     70     static PassOwnPtr<Loader> create()
     71     {
     72         return adoptPtr(new Loader());
     73     }
     74 
     75     virtual ~Loader()
     76     {
     77         m_scriptLoader->setClient(0);
     78     }
     79 
     80     void load(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& callback)
     81     {
     82         ASSERT(loadingContext);
     83         m_callback = callback;
     84         m_scriptLoader->setTargetType(ResourceRequest::TargetIsServiceWorker);
     85         m_scriptLoader->loadAsynchronously(
     86             *loadingContext, scriptURL, DenyCrossOriginRequests, this);
     87     }
     88 
     89     virtual void notifyFinished() OVERRIDE
     90     {
     91         m_callback();
     92     }
     93 
     94     void cancel()
     95     {
     96         m_scriptLoader->cancel();
     97     }
     98 
     99     bool failed() const { return m_scriptLoader->failed(); }
    100     const KURL& url() const { return m_scriptLoader->responseURL(); }
    101     String script() const { return m_scriptLoader->script(); }
    102 
    103 private:
    104     Loader() : m_scriptLoader(WorkerScriptLoader::create())
    105     {
    106     }
    107 
    108     RefPtr<WorkerScriptLoader> m_scriptLoader;
    109     Closure m_callback;
    110 };
    111 
    112 class WebEmbeddedWorkerImpl::LoaderProxy : public WorkerLoaderProxy {
    113 public:
    114     static PassOwnPtr<LoaderProxy> create(WebEmbeddedWorkerImpl& embeddedWorker)
    115     {
    116         return adoptPtr(new LoaderProxy(embeddedWorker));
    117     }
    118 
    119     virtual void postTaskToLoader(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
    120     {
    121         toWebLocalFrameImpl(m_embeddedWorker.m_mainFrame)->frame()->document()->postTask(task);
    122     }
    123 
    124     virtual bool postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
    125     {
    126         if (m_embeddedWorker.m_askedToTerminate || !m_embeddedWorker.m_workerThread)
    127             return false;
    128         return m_embeddedWorker.m_workerThread->runLoop().postTask(task);
    129     }
    130 
    131 private:
    132     explicit LoaderProxy(WebEmbeddedWorkerImpl& embeddedWorker)
    133         : m_embeddedWorker(embeddedWorker)
    134     {
    135     }
    136 
    137     // Not owned, embedded worker owns this.
    138     WebEmbeddedWorkerImpl& m_embeddedWorker;
    139 };
    140 
    141 WebEmbeddedWorker* WebEmbeddedWorker::create(
    142     WebServiceWorkerContextClient* client,
    143     WebWorkerPermissionClientProxy* permissionClient)
    144 {
    145     return new WebEmbeddedWorkerImpl(adoptPtr(client), adoptPtr(permissionClient));
    146 }
    147 
    148 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(
    149     PassOwnPtr<WebServiceWorkerContextClient> client,
    150     PassOwnPtr<WebWorkerPermissionClientProxy> permissionClient)
    151     : m_workerContextClient(client)
    152     , m_permissionClient(permissionClient)
    153     , m_webView(0)
    154     , m_mainFrame(0)
    155     , m_askedToTerminate(false)
    156     , m_pauseAfterDownloadState(DontPauseAfterDownload)
    157 {
    158 }
    159 
    160 WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl()
    161 {
    162     ASSERT(m_webView);
    163 
    164     // Detach the client before closing the view to avoid getting called back.
    165     toWebLocalFrameImpl(m_mainFrame)->setClient(0);
    166 
    167     m_webView->close();
    168     m_mainFrame->close();
    169 }
    170 
    171 void WebEmbeddedWorkerImpl::startWorkerContext(
    172     const WebEmbeddedWorkerStartData& data)
    173 {
    174     ASSERT(!m_askedToTerminate);
    175     ASSERT(!m_mainScriptLoader);
    176     ASSERT(m_pauseAfterDownloadState == DontPauseAfterDownload);
    177     m_workerStartData = data;
    178     if (data.pauseAfterDownloadMode == WebEmbeddedWorkerStartData::PauseAfterDownload)
    179         m_pauseAfterDownloadState = DoPauseAfterDownload;
    180     prepareShadowPageForLoader();
    181 }
    182 
    183 void WebEmbeddedWorkerImpl::terminateWorkerContext()
    184 {
    185     if (m_askedToTerminate)
    186         return;
    187     m_askedToTerminate = true;
    188     if (m_mainScriptLoader)
    189         m_mainScriptLoader->cancel();
    190     if (m_workerThread)
    191         m_workerThread->stop();
    192 }
    193 
    194 namespace {
    195 
    196 void resumeWorkerContextTask(ExecutionContext* context, bool)
    197 {
    198     toWorkerGlobalScope(context)->workerInspectorController()->resume();
    199 }
    200 
    201 void connectToWorkerContextInspectorTask(ExecutionContext* context, bool)
    202 {
    203     toWorkerGlobalScope(context)->workerInspectorController()->connectFrontend();
    204 }
    205 
    206 void reconnectToWorkerContextInspectorTask(ExecutionContext* context, const String& savedState)
    207 {
    208     WorkerInspectorController* ic = toWorkerGlobalScope(context)->workerInspectorController();
    209     ic->restoreInspectorStateFromCookie(savedState);
    210     ic->resume();
    211 }
    212 
    213 void disconnectFromWorkerContextInspectorTask(ExecutionContext* context, bool)
    214 {
    215     toWorkerGlobalScope(context)->workerInspectorController()->disconnectFrontend();
    216 }
    217 
    218 void dispatchOnInspectorBackendTask(ExecutionContext* context, const String& message)
    219 {
    220     toWorkerGlobalScope(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
    221 }
    222 
    223 } // namespace
    224 
    225 void WebEmbeddedWorkerImpl::resumeAfterDownload()
    226 {
    227     bool wasPaused = (m_pauseAfterDownloadState == IsPausedAfterDownload);
    228     m_pauseAfterDownloadState = DontPauseAfterDownload;
    229     if (wasPaused)
    230         startWorkerThread();
    231 }
    232 
    233 void WebEmbeddedWorkerImpl::resumeWorkerContext()
    234 {
    235     if (m_workerThread)
    236         m_workerThread->runLoop().postDebuggerTask(createCallbackTask(resumeWorkerContextTask, true));
    237 }
    238 
    239 void WebEmbeddedWorkerImpl::attachDevTools()
    240 {
    241     if (m_workerThread)
    242         m_workerThread->runLoop().postDebuggerTask(createCallbackTask(connectToWorkerContextInspectorTask, true));
    243 }
    244 
    245 void WebEmbeddedWorkerImpl::reattachDevTools(const WebString& savedState)
    246 {
    247     m_workerThread->runLoop().postDebuggerTask(createCallbackTask(reconnectToWorkerContextInspectorTask, String(savedState)));
    248 }
    249 
    250 void WebEmbeddedWorkerImpl::detachDevTools()
    251 {
    252     m_workerThread->runLoop().postDebuggerTask(createCallbackTask(disconnectFromWorkerContextInspectorTask, true));
    253 }
    254 
    255 void WebEmbeddedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
    256 {
    257     m_workerThread->runLoop().postDebuggerTask(createCallbackTask(dispatchOnInspectorBackendTask, String(message)));
    258     WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get());
    259 }
    260 
    261 void WebEmbeddedWorkerImpl::prepareShadowPageForLoader()
    262 {
    263     // Create 'shadow page', which is never displayed and is used mainly to
    264     // provide a context for loading on the main thread.
    265     //
    266     // FIXME: This does mostly same as WebSharedWorkerImpl::initializeLoader.
    267     // This code, and probably most of the code in this class should be shared
    268     // with SharedWorker.
    269     ASSERT(!m_webView);
    270     m_webView = WebView::create(0);
    271     // FIXME: http://crbug.com/363843. This needs to find a better way to
    272     // not create graphics layers.
    273     m_webView->settings()->setAcceleratedCompositingEnabled(false);
    274     m_mainFrame = WebLocalFrame::create(this);
    275     m_webView->setMainFrame(m_mainFrame);
    276 
    277     WebLocalFrameImpl* webFrame = toWebLocalFrameImpl(m_webView->mainFrame());
    278 
    279     // Construct substitute data source for the 'shadow page'. We only need it
    280     // to have same origin as the worker so the loading checks work correctly.
    281     CString content("");
    282     int length = static_cast<int>(content.length());
    283     RefPtr<SharedBuffer> buffer(SharedBuffer::create(content.data(), length));
    284     webFrame->frame()->loader().load(FrameLoadRequest(0, ResourceRequest(m_workerStartData.scriptURL), SubstituteData(buffer, "text/html", "UTF-8", KURL())));
    285 }
    286 
    287 void WebEmbeddedWorkerImpl::willSendRequest(
    288     WebLocalFrame* frame, unsigned, WebURLRequest& request,
    289     const WebURLResponse& redirectResponse)
    290 {
    291     if (m_networkProvider)
    292         m_networkProvider->willSendRequest(frame->dataSource(), request);
    293 }
    294 
    295 void WebEmbeddedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame)
    296 {
    297     ASSERT(!m_mainScriptLoader);
    298     ASSERT(!m_networkProvider);
    299     ASSERT(m_mainFrame);
    300     ASSERT(m_workerContextClient);
    301     m_networkProvider = adoptPtr(m_workerContextClient->createServiceWorkerNetworkProvider(frame->dataSource()));
    302     m_mainScriptLoader = Loader::create();
    303     m_mainScriptLoader->load(
    304         toWebLocalFrameImpl(m_mainFrame)->frame()->document(),
    305         m_workerStartData.scriptURL,
    306         bind(&WebEmbeddedWorkerImpl::onScriptLoaderFinished, this));
    307 }
    308 
    309 void WebEmbeddedWorkerImpl::onScriptLoaderFinished()
    310 {
    311     ASSERT(m_mainScriptLoader);
    312 
    313     if (m_mainScriptLoader->failed() || m_askedToTerminate) {
    314         m_mainScriptLoader.clear();
    315         // This may delete 'this'.
    316         m_workerContextClient->workerContextFailedToStart();
    317         return;
    318     }
    319 
    320     if (m_pauseAfterDownloadState == DoPauseAfterDownload) {
    321         m_pauseAfterDownloadState = IsPausedAfterDownload;
    322         m_workerContextClient->didPauseAfterDownload();
    323         return;
    324     }
    325     startWorkerThread();
    326 }
    327 
    328 void WebEmbeddedWorkerImpl::startWorkerThread()
    329 {
    330     ASSERT(m_pauseAfterDownloadState == DontPauseAfterDownload);
    331     if (m_askedToTerminate)
    332         return;
    333 
    334     // FIXME: startMode is deprecated, switch to waitForDebuggerMode once chromium is setting that value.
    335     WorkerThreadStartMode startMode =
    336         (m_workerStartData.startMode == WebEmbeddedWorkerStartModePauseOnStart)
    337         ? PauseWorkerGlobalScopeOnStart : DontPauseWorkerGlobalScopeOnStart;
    338 
    339     OwnPtrWillBeRawPtr<WorkerClients> workerClients = WorkerClients::create();
    340     providePermissionClientToWorker(workerClients.get(), m_permissionClient.release());
    341     provideServiceWorkerGlobalScopeClientToWorker(workerClients.get(), ServiceWorkerGlobalScopeClientImpl::create(*m_workerContextClient));
    342 
    343     OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData =
    344         WorkerThreadStartupData::create(
    345             m_mainScriptLoader->url(),
    346             m_workerStartData.userAgent,
    347             m_mainScriptLoader->script(),
    348             startMode,
    349             // FIXME: fill appropriate CSP info and policy type.
    350             String(),
    351             ContentSecurityPolicyHeaderTypeEnforce,
    352             workerClients.release());
    353 
    354     m_mainScriptLoader.clear();
    355 
    356     m_workerGlobalScopeProxy = ServiceWorkerGlobalScopeProxy::create(*this, *toWebLocalFrameImpl(m_mainFrame)->frame()->document(), *m_workerContextClient);
    357     m_loaderProxy = LoaderProxy::create(*this);
    358 
    359     m_workerThread = ServiceWorkerThread::create(*m_loaderProxy, *m_workerGlobalScopeProxy, startupData.release());
    360     m_workerThread->start();
    361 }
    362 
    363 } // namespace blink
    364