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