/* * Copyright (C) 2013 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WebEmbeddedWorkerImpl.h" #include "ServiceWorkerGlobalScopeProxy.h" #include "WebDataSourceImpl.h" #include "WebFrameImpl.h" #include "WebServiceWorkerContextClient.h" #include "WebView.h" #include "WebWorkerPermissionClientProxy.h" #include "WorkerPermissionClient.h" #include "core/dom/Document.h" #include "core/loader/FrameLoadRequest.h" #include "core/loader/SubstituteData.h" #include "core/workers/WorkerClients.h" #include "core/workers/WorkerLoaderProxy.h" #include "core/workers/WorkerScriptLoader.h" #include "core/workers/WorkerScriptLoaderClient.h" #include "core/workers/WorkerThreadStartupData.h" #include "modules/serviceworkers/ServiceWorkerThread.h" #include "platform/NotImplemented.h" #include "platform/SharedBuffer.h" #include "wtf/Functional.h" using namespace WebCore; namespace blink { // A thin wrapper for one-off script loading. class WebEmbeddedWorkerImpl::Loader : public WorkerScriptLoaderClient { public: static PassOwnPtr create(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& callback) { return adoptPtr(new Loader(loadingContext, scriptURL, callback)); } virtual ~Loader() { m_scriptLoader->setClient(0); } virtual void notifyFinished() OVERRIDE { m_callback(); } void cancel() { m_scriptLoader->cancel(); } bool failed() const { return m_scriptLoader->failed(); } const KURL& url() const { return m_scriptLoader->responseURL(); } String script() const { return m_scriptLoader->script(); } private: Loader(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& callback) : m_scriptLoader(WorkerScriptLoader::create()) , m_callback(callback) { m_scriptLoader->setTargetType(ResourceRequest::TargetIsServiceWorker); m_scriptLoader->loadAsynchronously( loadingContext, scriptURL, DenyCrossOriginRequests, this); } RefPtr m_scriptLoader; Closure m_callback; }; class WebEmbeddedWorkerImpl::LoaderProxy : public WorkerLoaderProxy { public: static PassOwnPtr create(WebEmbeddedWorkerImpl& embeddedWorker) { return adoptPtr(new LoaderProxy(embeddedWorker)); } virtual void postTaskToLoader(PassOwnPtr task) OVERRIDE { m_embeddedWorker.m_loadingContext->postTask(task); } virtual bool postTaskForModeToWorkerGlobalScope(PassOwnPtr task, const String& mode) OVERRIDE { if (m_embeddedWorker.m_askedToTerminate || !m_embeddedWorker.m_workerThread) return false; return m_embeddedWorker.m_workerThread->runLoop().postTaskForMode(task, mode); } private: explicit LoaderProxy(WebEmbeddedWorkerImpl& embeddedWorker) : m_embeddedWorker(embeddedWorker) { } // Not owned, embedded worker owns this. WebEmbeddedWorkerImpl& m_embeddedWorker; }; WebEmbeddedWorker* WebEmbeddedWorker::create( WebServiceWorkerContextClient* client, WebWorkerPermissionClientProxy* permissionClient) { return new WebEmbeddedWorkerImpl(adoptPtr(client), adoptPtr(permissionClient)); } WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl( PassOwnPtr client, PassOwnPtr permissionClient) : m_workerContextClient(client) , m_permissionClient(permissionClient) , m_askedToTerminate(false) { } WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl() { ASSERT(m_webView); // Detach the client before closing the view to avoid getting called back. toWebFrameImpl(m_mainFrame)->setClient(0); m_webView->close(); m_mainFrame->close(); } void WebEmbeddedWorkerImpl::startWorkerContext( const WebEmbeddedWorkerStartData& data) { ASSERT(!m_askedToTerminate); ASSERT(!m_mainScriptLoader); m_workerStartData = data; prepareShadowPageForLoader(); m_mainScriptLoader = Loader::create( m_loadingContext.get(), data.scriptURL, bind(&WebEmbeddedWorkerImpl::onScriptLoaderFinished, this)); } void WebEmbeddedWorkerImpl::terminateWorkerContext() { if (m_askedToTerminate) return; m_askedToTerminate = true; if (m_mainScriptLoader) m_mainScriptLoader->cancel(); if (m_workerThread) m_workerThread->stop(); } void WebEmbeddedWorkerImpl::prepareShadowPageForLoader() { // Create 'shadow page', which is never displayed and is used mainly to // provide a context for loading on the main thread. // // FIXME: This does mostly same as WebSharedWorkerImpl::initializeLoader. // This code, and probably most of the code in this class should be shared // with SharedWorker. ASSERT(!m_webView); m_webView = WebView::create(0); m_mainFrame = WebFrame::create(this); m_webView->setMainFrame(m_mainFrame); WebFrameImpl* webFrame = toWebFrameImpl(m_webView->mainFrame()); // Construct substitute data source for the 'shadow page'. We only need it // to have same origin as the worker so the loading checks work correctly. CString content(""); int length = static_cast(content.length()); RefPtr buffer(SharedBuffer::create(content.data(), length)); webFrame->frame()->loader().load(FrameLoadRequest(0, ResourceRequest(m_workerStartData.scriptURL), SubstituteData(buffer, "text/html", "UTF-8", KURL()))); // This document context will be used as 'loading context' for the worker. m_loadingContext = webFrame->frame()->document(); } void WebEmbeddedWorkerImpl::didCreateDataSource(WebFrame*, WebDataSource* ds) { // Tell the loader to load the data into the 'shadow page' synchronously, // so we can grab the resulting Document right after load. static_cast(ds)->setDeferMainResourceDataLoad(false); } void WebEmbeddedWorkerImpl::onScriptLoaderFinished() { ASSERT(m_mainScriptLoader); if (m_mainScriptLoader->failed() || m_askedToTerminate) { m_workerContextClient->workerContextFailedToStart(); m_mainScriptLoader.clear(); return; } WorkerThreadStartMode startMode = (m_workerStartData.startMode == WebEmbeddedWorkerStartModePauseOnStart) ? PauseWorkerGlobalScopeOnStart : DontPauseWorkerGlobalScopeOnStart; OwnPtr workerClients = WorkerClients::create(); providePermissionClientToWorker(workerClients.get(), m_permissionClient.release()); OwnPtr startupData = WorkerThreadStartupData::create( m_mainScriptLoader->url(), m_workerStartData.userAgent, m_mainScriptLoader->script(), startMode, // FIXME: fill appropriate CSP info and policy type. String(), ContentSecurityPolicy::Enforce, workerClients.release()); m_mainScriptLoader.clear(); m_workerGlobalScopeProxy = ServiceWorkerGlobalScopeProxy::create(*this, *m_loadingContext, m_workerContextClient.release()); m_loaderProxy = LoaderProxy::create(*this); m_workerThread = ServiceWorkerThread::create(*m_loaderProxy, *m_workerGlobalScopeProxy, startupData.release()); m_workerThread->start(); } } // namespace blink