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/InspectorInstrumentation.h"
     37 #include "core/inspector/WorkerDebuggerAgent.h"
     38 #include "core/inspector/WorkerInspectorController.h"
     39 #include "core/loader/FrameLoadRequest.h"
     40 #include "core/loader/SubstituteData.h"
     41 #include "core/workers/WorkerClients.h"
     42 #include "core/workers/WorkerGlobalScope.h"
     43 #include "core/workers/WorkerInspectorProxy.h"
     44 #include "core/workers/WorkerLoaderProxy.h"
     45 #include "core/workers/WorkerScriptLoader.h"
     46 #include "core/workers/WorkerScriptLoaderClient.h"
     47 #include "core/workers/WorkerThreadStartupData.h"
     48 #include "modules/serviceworkers/ServiceWorkerThread.h"
     49 #include "platform/NotImplemented.h"
     50 #include "platform/SharedBuffer.h"
     51 #include "platform/heap/Handle.h"
     52 #include "platform/network/ContentSecurityPolicyParsers.h"
     53 #include "public/platform/Platform.h"
     54 #include "public/platform/WebURLRequest.h"
     55 #include "public/web/WebDevToolsAgent.h"
     56 #include "public/web/WebServiceWorkerContextClient.h"
     57 #include "public/web/WebServiceWorkerNetworkProvider.h"
     58 #include "public/web/WebSettings.h"
     59 #include "public/web/WebView.h"
     60 #include "public/web/WebWorkerPermissionClientProxy.h"
     61 #include "web/ServiceWorkerGlobalScopeClientImpl.h"
     62 #include "web/ServiceWorkerGlobalScopeProxy.h"
     63 #include "web/WebDataSourceImpl.h"
     64 #include "web/WebLocalFrameImpl.h"
     65 #include "web/WorkerPermissionClient.h"
     66 #include "wtf/Functional.h"
     67 
     68 namespace blink {
     69 
     70 // A thin wrapper for one-off script loading.
     71 class WebEmbeddedWorkerImpl::Loader : public WorkerScriptLoaderClient {
     72 public:
     73     static PassOwnPtr<Loader> create()
     74     {
     75         return adoptPtr(new Loader());
     76     }
     77 
     78     virtual ~Loader()
     79     {
     80         m_scriptLoader->setClient(0);
     81     }
     82 
     83     void load(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& callback)
     84     {
     85         ASSERT(loadingContext);
     86         m_callback = callback;
     87         m_scriptLoader->setRequestContext(WebURLRequest::RequestContextServiceWorker);
     88         m_scriptLoader->loadAsynchronously(
     89             *loadingContext, scriptURL, DenyCrossOriginRequests, this);
     90     }
     91 
     92     virtual void notifyFinished() OVERRIDE
     93     {
     94         m_callback();
     95     }
     96 
     97     void cancel()
     98     {
     99         m_scriptLoader->cancel();
    100     }
    101 
    102     bool failed() const { return m_scriptLoader->failed(); }
    103     const KURL& url() const { return m_scriptLoader->responseURL(); }
    104     String script() const { return m_scriptLoader->script(); }
    105 
    106 private:
    107     Loader() : m_scriptLoader(WorkerScriptLoader::create())
    108     {
    109     }
    110 
    111     RefPtr<WorkerScriptLoader> m_scriptLoader;
    112     Closure m_callback;
    113 };
    114 
    115 class WebEmbeddedWorkerImpl::LoaderProxy : public WorkerLoaderProxy {
    116 public:
    117     static PassOwnPtr<LoaderProxy> create(WebEmbeddedWorkerImpl& embeddedWorker)
    118     {
    119         return adoptPtr(new LoaderProxy(embeddedWorker));
    120     }
    121 
    122     virtual void postTaskToLoader(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
    123     {
    124         toWebLocalFrameImpl(m_embeddedWorker.m_mainFrame)->frame()->document()->postTask(task);
    125     }
    126 
    127     virtual bool postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
    128     {
    129         if (m_embeddedWorker.m_askedToTerminate || !m_embeddedWorker.m_workerThread)
    130             return false;
    131         m_embeddedWorker.m_workerThread->postTask(task);
    132         return !m_embeddedWorker.m_workerThread->terminated();
    133     }
    134 
    135 private:
    136     explicit LoaderProxy(WebEmbeddedWorkerImpl& embeddedWorker)
    137         : m_embeddedWorker(embeddedWorker)
    138     {
    139     }
    140 
    141     // Not owned, embedded worker owns this.
    142     WebEmbeddedWorkerImpl& m_embeddedWorker;
    143 };
    144 
    145 WebEmbeddedWorker* WebEmbeddedWorker::create(
    146     WebServiceWorkerContextClient* client,
    147     WebWorkerPermissionClientProxy* permissionClient)
    148 {
    149     return new WebEmbeddedWorkerImpl(adoptPtr(client), adoptPtr(permissionClient));
    150 }
    151 
    152 static HashSet<WebEmbeddedWorkerImpl*>& runningWorkerInstances()
    153 {
    154     DEFINE_STATIC_LOCAL(HashSet<WebEmbeddedWorkerImpl*>, set, ());
    155     return set;
    156 }
    157 
    158 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(
    159     PassOwnPtr<WebServiceWorkerContextClient> client,
    160     PassOwnPtr<WebWorkerPermissionClientProxy> permissionClient)
    161     : m_workerContextClient(client)
    162     , m_permissionClient(permissionClient)
    163     , m_workerInspectorProxy(WorkerInspectorProxy::create())
    164     , m_webView(0)
    165     , m_mainFrame(0)
    166     , m_askedToTerminate(false)
    167     , m_pauseAfterDownloadState(DontPauseAfterDownload)
    168     , m_waitingForDebuggerState(NotWaitingForDebugger)
    169 {
    170     runningWorkerInstances().add(this);
    171 }
    172 
    173 WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl()
    174 {
    175     ASSERT(runningWorkerInstances().contains(this));
    176     runningWorkerInstances().remove(this);
    177     ASSERT(m_webView);
    178 
    179     // Detach the client before closing the view to avoid getting called back.
    180     toWebLocalFrameImpl(m_mainFrame)->setClient(0);
    181 
    182     m_webView->close();
    183     m_mainFrame->close();
    184 }
    185 
    186 void WebEmbeddedWorkerImpl::terminateAll()
    187 {
    188     HashSet<WebEmbeddedWorkerImpl*> instances = runningWorkerInstances();
    189     for (HashSet<WebEmbeddedWorkerImpl*>::iterator it = instances.begin(), itEnd = instances.end(); it != itEnd; ++it) {
    190         (*it)->terminateWorkerContext();
    191     }
    192 }
    193 
    194 void WebEmbeddedWorkerImpl::startWorkerContext(
    195     const WebEmbeddedWorkerStartData& data)
    196 {
    197     ASSERT(!m_askedToTerminate);
    198     ASSERT(!m_mainScriptLoader);
    199     ASSERT(m_pauseAfterDownloadState == DontPauseAfterDownload);
    200     m_workerStartData = data;
    201     if (data.pauseAfterDownloadMode == WebEmbeddedWorkerStartData::PauseAfterDownload)
    202         m_pauseAfterDownloadState = DoPauseAfterDownload;
    203     prepareShadowPageForLoader();
    204 }
    205 
    206 void WebEmbeddedWorkerImpl::terminateWorkerContext()
    207 {
    208     if (m_askedToTerminate)
    209         return;
    210     m_askedToTerminate = true;
    211     if (m_mainScriptLoader) {
    212         m_mainScriptLoader->cancel();
    213         m_mainScriptLoader.clear();
    214         // This may delete 'this'.
    215         m_workerContextClient->workerContextFailedToStart();
    216         return;
    217     }
    218     if (m_pauseAfterDownloadState == IsPausedAfterDownload) {
    219         // This may delete 'this'.
    220         m_workerContextClient->workerContextFailedToStart();
    221         return;
    222     }
    223     if (m_workerThread)
    224         m_workerThread->stop();
    225     m_workerInspectorProxy->workerThreadTerminated();
    226 }
    227 
    228 void WebEmbeddedWorkerImpl::resumeAfterDownload()
    229 {
    230     ASSERT(!m_askedToTerminate);
    231     bool wasPaused = (m_pauseAfterDownloadState == IsPausedAfterDownload);
    232     m_pauseAfterDownloadState = DontPauseAfterDownload;
    233 
    234     // If we were asked to wait for debugger while updating service worker version then it is good time now.
    235     m_workerContextClient->workerReadyForInspection();
    236     if (m_workerStartData.waitForDebuggerMode == WebEmbeddedWorkerStartData::WaitForDebugger)
    237         m_waitingForDebuggerState = WaitingForDebuggerAfterScriptLoaded;
    238     else if (wasPaused)
    239         startWorkerThread();
    240 }
    241 
    242 void WebEmbeddedWorkerImpl::resumeWorkerContext()
    243 {
    244 }
    245 
    246 void WebEmbeddedWorkerImpl::attachDevTools(const WebString& hostId)
    247 {
    248     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
    249     if (devtoolsAgent)
    250         devtoolsAgent->attach(hostId);
    251 }
    252 
    253 void WebEmbeddedWorkerImpl::reattachDevTools(const WebString& hostId, const WebString& savedState)
    254 {
    255     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
    256     if (devtoolsAgent)
    257         devtoolsAgent->reattach(hostId, savedState);
    258     resumeStartup();
    259 }
    260 
    261 void WebEmbeddedWorkerImpl::detachDevTools()
    262 {
    263     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
    264     if (devtoolsAgent)
    265         devtoolsAgent->detach();
    266 }
    267 
    268 void WebEmbeddedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
    269 {
    270     if (m_askedToTerminate)
    271         return;
    272     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
    273     if (devtoolsAgent)
    274         devtoolsAgent->dispatchOnInspectorBackend(message);
    275 }
    276 
    277 void WebEmbeddedWorkerImpl::postMessageToPageInspector(const String& message)
    278 {
    279     WorkerInspectorProxy::PageInspector* pageInspector = m_workerInspectorProxy->pageInspector();
    280     if (!pageInspector)
    281         return;
    282     pageInspector->dispatchMessageFromWorker(message);
    283 }
    284 
    285 void WebEmbeddedWorkerImpl::prepareShadowPageForLoader()
    286 {
    287     // Create 'shadow page', which is never displayed and is used mainly to
    288     // provide a context for loading on the main thread.
    289     //
    290     // FIXME: This does mostly same as WebSharedWorkerImpl::initializeLoader.
    291     // This code, and probably most of the code in this class should be shared
    292     // with SharedWorker.
    293     ASSERT(!m_webView);
    294     m_webView = WebView::create(0);
    295     // FIXME: http://crbug.com/363843. This needs to find a better way to
    296     // not create graphics layers.
    297     m_webView->settings()->setAcceleratedCompositingEnabled(false);
    298     m_mainFrame = WebLocalFrame::create(this);
    299     m_webView->setMainFrame(m_mainFrame);
    300     m_webView->setDevToolsAgentClient(this);
    301 
    302     WebLocalFrameImpl* webFrame = toWebLocalFrameImpl(m_webView->mainFrame());
    303 
    304     // Construct substitute data source for the 'shadow page'. We only need it
    305     // to have same origin as the worker so the loading checks work correctly.
    306     CString content("");
    307     int length = static_cast<int>(content.length());
    308     RefPtr<SharedBuffer> buffer(SharedBuffer::create(content.data(), length));
    309     webFrame->frame()->loader().load(FrameLoadRequest(0, ResourceRequest(m_workerStartData.scriptURL), SubstituteData(buffer, "text/html", "UTF-8", KURL())));
    310 }
    311 
    312 void WebEmbeddedWorkerImpl::willSendRequest(
    313     WebLocalFrame* frame, unsigned, WebURLRequest& request,
    314     const WebURLResponse& redirectResponse)
    315 {
    316     if (m_networkProvider)
    317         m_networkProvider->willSendRequest(frame->dataSource(), request);
    318 }
    319 
    320 void WebEmbeddedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame)
    321 {
    322     // If we were asked to wait for debugger then it is the good time to do that.
    323     // However if we are updating service worker version (m_pauseAfterDownloadState is set)
    324     // Then we need to load the worker script to check the version, so in this case we wait for debugger
    325     // later in ::resumeAfterDownload().
    326     if (m_pauseAfterDownloadState != DoPauseAfterDownload) {
    327         m_workerContextClient->workerReadyForInspection();
    328         if (m_workerStartData.waitForDebuggerMode == WebEmbeddedWorkerStartData::WaitForDebugger) {
    329             m_waitingForDebuggerState = WaitingForDebuggerBeforeLoadingScript;
    330             return;
    331         }
    332     }
    333     startScriptLoader(frame);
    334 }
    335 
    336 void WebEmbeddedWorkerImpl::sendMessageToInspectorFrontend(const WebString& message)
    337 {
    338     m_workerContextClient->dispatchDevToolsMessage(message);
    339 }
    340 
    341 void WebEmbeddedWorkerImpl::resumeStartup()
    342 {
    343     WaitingForDebuggerState waitingForDebuggerState = m_waitingForDebuggerState;
    344     m_waitingForDebuggerState = NotWaitingForDebugger;
    345     if (waitingForDebuggerState == WaitingForDebuggerBeforeLoadingScript)
    346         startScriptLoader(toWebLocalFrameImpl(m_mainFrame));
    347     else if (waitingForDebuggerState == WaitingForDebuggerAfterScriptLoaded)
    348         startWorkerThread();
    349 }
    350 
    351 void WebEmbeddedWorkerImpl::saveAgentRuntimeState(const WebString& inspectorState)
    352 {
    353     m_workerContextClient->saveDevToolsAgentState(inspectorState);
    354 }
    355 
    356 void WebEmbeddedWorkerImpl::startScriptLoader(WebLocalFrame* frame)
    357 {
    358     ASSERT(!m_mainScriptLoader);
    359     ASSERT(!m_networkProvider);
    360     ASSERT(m_mainFrame);
    361     ASSERT(m_workerContextClient);
    362     m_networkProvider = adoptPtr(m_workerContextClient->createServiceWorkerNetworkProvider(frame->dataSource()));
    363     m_mainScriptLoader = Loader::create();
    364     m_mainScriptLoader->load(
    365         toWebLocalFrameImpl(m_mainFrame)->frame()->document(),
    366         m_workerStartData.scriptURL,
    367         bind(&WebEmbeddedWorkerImpl::onScriptLoaderFinished, this));
    368 }
    369 
    370 void WebEmbeddedWorkerImpl::onScriptLoaderFinished()
    371 {
    372     ASSERT(m_mainScriptLoader);
    373 
    374     if (m_askedToTerminate)
    375         return;
    376 
    377     if (m_mainScriptLoader->failed()) {
    378         m_mainScriptLoader.clear();
    379         // This may delete 'this'.
    380         m_workerContextClient->workerContextFailedToStart();
    381         return;
    382     }
    383 
    384     Platform::current()->histogramCustomCounts("ServiceWorker.ScriptSize", m_mainScriptLoader->script().length(), 1000, 5000000, 50);
    385 
    386     if (m_pauseAfterDownloadState == DoPauseAfterDownload) {
    387         m_pauseAfterDownloadState = IsPausedAfterDownload;
    388         m_workerContextClient->didPauseAfterDownload();
    389         return;
    390     }
    391     startWorkerThread();
    392 }
    393 
    394 void WebEmbeddedWorkerImpl::startWorkerThread()
    395 {
    396     ASSERT(m_pauseAfterDownloadState == DontPauseAfterDownload);
    397     ASSERT(!m_askedToTerminate);
    398 
    399     Document* document = toWebLocalFrameImpl(m_mainFrame)->frame()->document();
    400 
    401     WorkerThreadStartMode startMode = DontPauseWorkerGlobalScopeOnStart;
    402     if (InspectorInstrumentation::shouldPauseDedicatedWorkerOnStart(document))
    403         startMode = PauseWorkerGlobalScopeOnStart;
    404 
    405     OwnPtrWillBeRawPtr<WorkerClients> workerClients = WorkerClients::create();
    406     providePermissionClientToWorker(workerClients.get(), m_permissionClient.release());
    407     provideServiceWorkerGlobalScopeClientToWorker(workerClients.get(), ServiceWorkerGlobalScopeClientImpl::create(*m_workerContextClient));
    408 
    409     KURL scriptURL = m_mainScriptLoader->url();
    410     OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData =
    411         WorkerThreadStartupData::create(
    412             scriptURL,
    413             m_workerStartData.userAgent,
    414             m_mainScriptLoader->script(),
    415             startMode,
    416             // FIXME: fill appropriate CSP info and policy type.
    417             String(),
    418             ContentSecurityPolicyHeaderTypeEnforce,
    419             workerClients.release());
    420 
    421     m_mainScriptLoader.clear();
    422 
    423     m_workerGlobalScopeProxy = ServiceWorkerGlobalScopeProxy::create(*this, *document, *m_workerContextClient);
    424     m_loaderProxy = LoaderProxy::create(*this);
    425     m_workerThread = ServiceWorkerThread::create(*m_loaderProxy, *m_workerGlobalScopeProxy, startupData.release());
    426     m_workerThread->start();
    427     m_workerInspectorProxy->workerThreadCreated(document, m_workerThread.get(), scriptURL);
    428 }
    429 
    430 } // namespace blink
    431