2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "web/WebEmbeddedWorkerImpl.h"
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"
70 // A thin wrapper for one-off script loading.
71 class WebEmbeddedWorkerImpl::Loader : public WorkerScriptLoaderClient {
73 static PassOwnPtr<Loader> create()
75 return adoptPtr(new Loader());
80 m_scriptLoader->setClient(0);
83 void load(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& callback)
85 ASSERT(loadingContext);
86 m_callback = callback;
87 m_scriptLoader->setRequestContext(WebURLRequest::RequestContextServiceWorker);
88 m_scriptLoader->loadAsynchronously(
89 *loadingContext, scriptURL, DenyCrossOriginRequests, this);
92 virtual void notifyFinished() OVERRIDE
99 m_scriptLoader->cancel();
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(); }
107 Loader() : m_scriptLoader(WorkerScriptLoader::create())
111 RefPtr<WorkerScriptLoader> m_scriptLoader;
115 class WebEmbeddedWorkerImpl::LoaderProxy : public WorkerLoaderProxy {
117 static PassOwnPtr<LoaderProxy> create(WebEmbeddedWorkerImpl& embeddedWorker)
119 return adoptPtr(new LoaderProxy(embeddedWorker));
122 virtual void postTaskToLoader(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
124 toWebLocalFrameImpl(m_embeddedWorker.m_mainFrame)->frame()->document()->postTask(task);
127 virtual bool postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
129 if (m_embeddedWorker.m_askedToTerminate || !m_embeddedWorker.m_workerThread)
131 m_embeddedWorker.m_workerThread->postTask(task);
132 return !m_embeddedWorker.m_workerThread->terminated();
136 explicit LoaderProxy(WebEmbeddedWorkerImpl& embeddedWorker)
137 : m_embeddedWorker(embeddedWorker)
141 // Not owned, embedded worker owns this.
142 WebEmbeddedWorkerImpl& m_embeddedWorker;
145 WebEmbeddedWorker* WebEmbeddedWorker::create(
146 WebServiceWorkerContextClient* client,
147 WebWorkerPermissionClientProxy* permissionClient)
149 return new WebEmbeddedWorkerImpl(adoptPtr(client), adoptPtr(permissionClient));
152 static HashSet<WebEmbeddedWorkerImpl*>& runningWorkerInstances()
154 DEFINE_STATIC_LOCAL(HashSet<WebEmbeddedWorkerImpl*>, set, ());
158 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(
159 PassOwnPtr<WebServiceWorkerContextClient> client,
160 PassOwnPtr<WebWorkerPermissionClientProxy> permissionClient)
161 : m_workerContextClient(client)
162 , m_permissionClient(permissionClient)
163 , m_workerInspectorProxy(WorkerInspectorProxy::create())
166 , m_askedToTerminate(false)
167 , m_pauseAfterDownloadState(DontPauseAfterDownload)
168 , m_waitingForDebuggerState(NotWaitingForDebugger)
170 runningWorkerInstances().add(this);
173 WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl()
175 ASSERT(runningWorkerInstances().contains(this));
176 runningWorkerInstances().remove(this);
179 // Detach the client before closing the view to avoid getting called back.
180 toWebLocalFrameImpl(m_mainFrame)->setClient(0);
183 m_mainFrame->close();
186 void WebEmbeddedWorkerImpl::terminateAll()
188 HashSet<WebEmbeddedWorkerImpl*> instances = runningWorkerInstances();
189 for (HashSet<WebEmbeddedWorkerImpl*>::iterator it = instances.begin(), itEnd = instances.end(); it != itEnd; ++it) {
190 (*it)->terminateWorkerContext();
194 void WebEmbeddedWorkerImpl::startWorkerContext(
195 const WebEmbeddedWorkerStartData& data)
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();
206 void WebEmbeddedWorkerImpl::terminateWorkerContext()
208 if (m_askedToTerminate)
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();
218 if (m_pauseAfterDownloadState == IsPausedAfterDownload) {
219 // This may delete 'this'.
220 m_workerContextClient->workerContextFailedToStart();
224 m_workerThread->stop();
225 m_workerInspectorProxy->workerThreadTerminated();
228 void WebEmbeddedWorkerImpl::resumeAfterDownload()
230 ASSERT(!m_askedToTerminate);
231 bool wasPaused = (m_pauseAfterDownloadState == IsPausedAfterDownload);
232 m_pauseAfterDownloadState = DontPauseAfterDownload;
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;
242 void WebEmbeddedWorkerImpl::resumeWorkerContext()
246 void WebEmbeddedWorkerImpl::attachDevTools(const WebString& hostId)
248 WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
250 devtoolsAgent->attach(hostId);
253 void WebEmbeddedWorkerImpl::reattachDevTools(const WebString& hostId, const WebString& savedState)
255 WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
257 devtoolsAgent->reattach(hostId, savedState);
261 void WebEmbeddedWorkerImpl::detachDevTools()
263 WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
265 devtoolsAgent->detach();
268 void WebEmbeddedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
270 if (m_askedToTerminate)
272 WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
274 devtoolsAgent->dispatchOnInspectorBackend(message);
277 void WebEmbeddedWorkerImpl::postMessageToPageInspector(const String& message)
279 WorkerInspectorProxy::PageInspector* pageInspector = m_workerInspectorProxy->pageInspector();
282 pageInspector->dispatchMessageFromWorker(message);
285 void WebEmbeddedWorkerImpl::prepareShadowPageForLoader()
287 // Create 'shadow page', which is never displayed and is used mainly to
288 // provide a context for loading on the main thread.
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.
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);
302 WebLocalFrameImpl* webFrame = toWebLocalFrameImpl(m_webView->mainFrame());
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.
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())));
312 void WebEmbeddedWorkerImpl::willSendRequest(
313 WebLocalFrame* frame, unsigned, WebURLRequest& request,
314 const WebURLResponse& redirectResponse)
316 if (m_networkProvider)
317 m_networkProvider->willSendRequest(frame->dataSource(), request);
320 void WebEmbeddedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame)
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;
333 startScriptLoader(frame);
336 void WebEmbeddedWorkerImpl::sendMessageToInspectorFrontend(const WebString& message)
338 m_workerContextClient->dispatchDevToolsMessage(message);
341 void WebEmbeddedWorkerImpl::resumeStartup()
343 WaitingForDebuggerState waitingForDebuggerState = m_waitingForDebuggerState;
344 m_waitingForDebuggerState = NotWaitingForDebugger;
345 if (waitingForDebuggerState == WaitingForDebuggerBeforeLoadingScript)
346 startScriptLoader(toWebLocalFrameImpl(m_mainFrame));
347 else if (waitingForDebuggerState == WaitingForDebuggerAfterScriptLoaded)
351 void WebEmbeddedWorkerImpl::saveAgentRuntimeState(const WebString& inspectorState)
353 m_workerContextClient->saveDevToolsAgentState(inspectorState);
356 void WebEmbeddedWorkerImpl::startScriptLoader(WebLocalFrame* frame)
358 ASSERT(!m_mainScriptLoader);
359 ASSERT(!m_networkProvider);
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));
370 void WebEmbeddedWorkerImpl::onScriptLoaderFinished()
372 ASSERT(m_mainScriptLoader);
374 if (m_askedToTerminate)
377 if (m_mainScriptLoader->failed()) {
378 m_mainScriptLoader.clear();
379 // This may delete 'this'.
380 m_workerContextClient->workerContextFailedToStart();
384 Platform::current()->histogramCustomCounts("ServiceWorker.ScriptSize", m_mainScriptLoader->script().length(), 1000, 5000000, 50);
386 if (m_pauseAfterDownloadState == DoPauseAfterDownload) {
387 m_pauseAfterDownloadState = IsPausedAfterDownload;
388 m_workerContextClient->didPauseAfterDownload();
394 void WebEmbeddedWorkerImpl::startWorkerThread()
396 ASSERT(m_pauseAfterDownloadState == DontPauseAfterDownload);
397 ASSERT(!m_askedToTerminate);
399 Document* document = toWebLocalFrameImpl(m_mainFrame)->frame()->document();
401 WorkerThreadStartMode startMode = DontPauseWorkerGlobalScopeOnStart;
402 if (InspectorInstrumentation::shouldPauseDedicatedWorkerOnStart(document))
403 startMode = PauseWorkerGlobalScopeOnStart;
405 OwnPtrWillBeRawPtr<WorkerClients> workerClients = WorkerClients::create();
406 providePermissionClientToWorker(workerClients.get(), m_permissionClient.release());
407 provideServiceWorkerGlobalScopeClientToWorker(workerClients.get(), ServiceWorkerGlobalScopeClientImpl::create(*m_workerContextClient));
409 KURL scriptURL = m_mainScriptLoader->url();
410 OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData =
411 WorkerThreadStartupData::create(
413 m_workerStartData.userAgent,
414 m_mainScriptLoader->script(),
416 // FIXME: fill appropriate CSP info and policy type.
418 ContentSecurityPolicyHeaderTypeEnforce,
419 workerClients.release());
421 m_mainScriptLoader.clear();
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);