Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / web / WebEmbeddedWorkerImpl.cpp
blob72067a85e2ac02b91f9b1661a2cb36e078b94cba
1 /*
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
6 * met:
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.
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.
31 #include "config.h"
32 #include "web/WebEmbeddedWorkerImpl.h"
34 #include "core/dom/CrossThreadTask.h"
35 #include "core/dom/Document.h"
36 #include "core/fetch/SubstituteData.h"
37 #include "core/frame/csp/ContentSecurityPolicy.h"
38 #include "core/inspector/InspectorInstrumentation.h"
39 #include "core/inspector/WorkerDebuggerAgent.h"
40 #include "core/inspector/WorkerInspectorController.h"
41 #include "core/loader/FrameLoadRequest.h"
42 #include "core/workers/WorkerClients.h"
43 #include "core/workers/WorkerGlobalScope.h"
44 #include "core/workers/WorkerInspectorProxy.h"
45 #include "core/workers/WorkerLoaderProxy.h"
46 #include "core/workers/WorkerScriptLoader.h"
47 #include "core/workers/WorkerThreadStartupData.h"
48 #include "modules/serviceworkers/ServiceWorkerContainerClient.h"
49 #include "modules/serviceworkers/ServiceWorkerThread.h"
50 #include "platform/SharedBuffer.h"
51 #include "platform/heap/Handle.h"
52 #include "platform/network/ContentSecurityPolicyParsers.h"
53 #include "platform/network/ContentSecurityPolicyResponseHeaders.h"
54 #include "public/platform/Platform.h"
55 #include "public/platform/WebServiceWorkerProvider.h"
56 #include "public/platform/WebURLRequest.h"
57 #include "public/web/WebDevToolsAgent.h"
58 #include "public/web/WebServiceWorkerContextClient.h"
59 #include "public/web/WebServiceWorkerNetworkProvider.h"
60 #include "public/web/WebSettings.h"
61 #include "public/web/WebView.h"
62 #include "public/web/WebWorkerContentSettingsClientProxy.h"
63 #include "web/ServiceWorkerGlobalScopeClientImpl.h"
64 #include "web/ServiceWorkerGlobalScopeProxy.h"
65 #include "web/WebDataSourceImpl.h"
66 #include "web/WebLocalFrameImpl.h"
67 #include "web/WorkerContentSettingsClient.h"
68 #include "wtf/Functional.h"
70 namespace blink {
72 WebEmbeddedWorker* WebEmbeddedWorker::create(WebServiceWorkerContextClient* client, WebWorkerContentSettingsClientProxy* contentSettingsClient)
74 return new WebEmbeddedWorkerImpl(adoptPtr(client), adoptPtr(contentSettingsClient));
77 static HashSet<WebEmbeddedWorkerImpl*>& runningWorkerInstances()
79 DEFINE_STATIC_LOCAL(HashSet<WebEmbeddedWorkerImpl*>, set, ());
80 return set;
83 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(PassOwnPtr<WebServiceWorkerContextClient> client, PassOwnPtr<WebWorkerContentSettingsClientProxy> ContentSettingsClient)
84 : m_workerContextClient(client)
85 , m_contentSettingsClient(ContentSettingsClient)
86 , m_workerInspectorProxy(WorkerInspectorProxy::create())
87 , m_webView(nullptr)
88 , m_mainFrame(nullptr)
89 , m_loadingShadowPage(false)
90 , m_askedToTerminate(false)
91 , m_waitingForDebuggerState(NotWaitingForDebugger)
93 runningWorkerInstances().add(this);
96 WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl()
98 // Prevent onScriptLoaderFinished from deleting 'this'.
99 m_askedToTerminate = true;
101 if (m_workerThread)
102 m_workerThread->terminateAndWait();
104 ASSERT(runningWorkerInstances().contains(this));
105 runningWorkerInstances().remove(this);
106 ASSERT(m_webView);
108 // Detach the client before closing the view to avoid getting called back.
109 m_mainFrame->setClient(0);
111 m_webView->close();
112 m_mainFrame->close();
113 if (m_loaderProxy)
114 m_loaderProxy->detachProvider(this);
117 void WebEmbeddedWorkerImpl::startWorkerContext(
118 const WebEmbeddedWorkerStartData& data)
120 ASSERT(!m_askedToTerminate);
121 ASSERT(!m_mainScriptLoader);
122 m_workerStartData = data;
123 prepareShadowPageForLoader();
126 void WebEmbeddedWorkerImpl::terminateWorkerContext()
128 if (m_askedToTerminate)
129 return;
130 m_askedToTerminate = true;
131 if (m_loadingShadowPage) {
132 // This deletes 'this'.
133 m_workerContextClient->workerContextFailedToStart();
134 return;
136 if (m_mainScriptLoader) {
137 m_mainScriptLoader->cancel();
138 m_mainScriptLoader.clear();
139 // This deletes 'this'.
140 m_workerContextClient->workerContextFailedToStart();
141 return;
143 if (m_workerThread)
144 m_workerThread->terminate();
145 m_workerInspectorProxy->workerThreadTerminated();
148 void WebEmbeddedWorkerImpl::attachDevTools(const WebString& hostId)
150 WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent();
151 if (devtoolsAgent)
152 devtoolsAgent->attach(hostId);
155 void WebEmbeddedWorkerImpl::reattachDevTools(const WebString& hostId, const WebString& savedState)
157 WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent();
158 if (devtoolsAgent)
159 devtoolsAgent->reattach(hostId, savedState);
160 resumeStartup();
163 void WebEmbeddedWorkerImpl::detachDevTools()
165 WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent();
166 if (devtoolsAgent)
167 devtoolsAgent->detach();
170 void WebEmbeddedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
172 if (m_askedToTerminate)
173 return;
174 WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent();
175 if (devtoolsAgent)
176 devtoolsAgent->dispatchOnInspectorBackend(message);
179 void WebEmbeddedWorkerImpl::postMessageToPageInspector(const String& message)
181 WorkerInspectorProxy::PageInspector* pageInspector = m_workerInspectorProxy->pageInspector();
182 if (!pageInspector)
183 return;
184 pageInspector->dispatchMessageFromWorker(message);
187 void WebEmbeddedWorkerImpl::postTaskToLoader(PassOwnPtr<ExecutionContextTask> task)
189 m_mainFrame->frame()->document()->postTask(FROM_HERE, task);
192 bool WebEmbeddedWorkerImpl::postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task)
194 if (m_askedToTerminate || !m_workerThread)
195 return false;
197 m_workerThread->postTask(FROM_HERE, task);
198 return !m_workerThread->terminated();
201 void WebEmbeddedWorkerImpl::prepareShadowPageForLoader()
203 // Create 'shadow page', which is never displayed and is used mainly to
204 // provide a context for loading on the main thread.
206 // FIXME: This does mostly same as WebSharedWorkerImpl::initializeLoader.
207 // This code, and probably most of the code in this class should be shared
208 // with SharedWorker.
209 ASSERT(!m_webView);
210 m_webView = WebView::create(0);
211 WebSettings* settings = m_webView->settings();
212 // FIXME: http://crbug.com/363843. This needs to find a better way to
213 // not create graphics layers.
214 settings->setAcceleratedCompositingEnabled(false);
215 // Currently we block all mixed-content requests from a ServiceWorker.
216 // FIXME: When we support FetchEvent.default(), we should relax this
217 // restriction.
218 settings->setStrictMixedContentChecking(true);
219 settings->setAllowDisplayOfInsecureContent(false);
220 settings->setAllowRunningOfInsecureContent(false);
221 m_mainFrame = toWebLocalFrameImpl(WebLocalFrame::create(WebTreeScopeType::Document, this));
222 m_webView->setMainFrame(m_mainFrame.get());
223 m_mainFrame->setDevToolsAgentClient(this);
225 // If we were asked to wait for debugger then it is the good time to do that.
226 m_workerContextClient->workerReadyForInspection();
227 if (m_workerStartData.waitForDebuggerMode == WebEmbeddedWorkerStartData::WaitForDebugger) {
228 m_waitingForDebuggerState = WaitingForDebuggerBeforeLoadingScript;
229 return;
232 loadShadowPage();
235 void WebEmbeddedWorkerImpl::loadShadowPage()
237 // Construct substitute data source for the 'shadow page'. We only need it
238 // to have same origin as the worker so the loading checks work correctly.
239 CString content("");
240 int length = static_cast<int>(content.length());
241 RefPtr<SharedBuffer> buffer(SharedBuffer::create(content.data(), length));
242 m_loadingShadowPage = true;
243 m_mainFrame->frame()->loader().load(FrameLoadRequest(0, ResourceRequest(m_workerStartData.scriptURL), SubstituteData(buffer, "text/html", "UTF-8", KURL())));
246 void WebEmbeddedWorkerImpl::willSendRequest(
247 WebLocalFrame* frame, unsigned, WebURLRequest& request,
248 const WebURLResponse& redirectResponse)
250 if (m_networkProvider)
251 m_networkProvider->willSendRequest(frame->dataSource(), request);
254 void WebEmbeddedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame, bool)
256 ASSERT(!m_mainScriptLoader);
257 ASSERT(!m_networkProvider);
258 ASSERT(m_mainFrame);
259 ASSERT(m_workerContextClient);
260 ASSERT(m_loadingShadowPage);
261 ASSERT(!m_askedToTerminate);
262 m_loadingShadowPage = false;
263 m_networkProvider = adoptPtr(m_workerContextClient->createServiceWorkerNetworkProvider(frame->dataSource()));
264 m_mainScriptLoader = WorkerScriptLoader::create();
265 m_mainScriptLoader->setRequestContext(WebURLRequest::RequestContextServiceWorker);
266 m_mainScriptLoader->loadAsynchronously(
267 *m_mainFrame->frame()->document(),
268 m_workerStartData.scriptURL,
269 DenyCrossOriginRequests,
270 nullptr,
271 bind(&WebEmbeddedWorkerImpl::onScriptLoaderFinished, this));
272 // Do nothing here since onScriptLoaderFinished() might have been already
273 // invoked and |this| might have been deleted at this point.
276 void WebEmbeddedWorkerImpl::sendProtocolMessage(int callId, const WebString& message, const WebString& state)
278 m_workerContextClient->sendDevToolsMessage(callId, message, state);
281 void WebEmbeddedWorkerImpl::resumeStartup()
283 WaitingForDebuggerState waitingForDebuggerState = m_waitingForDebuggerState;
284 m_waitingForDebuggerState = NotWaitingForDebugger;
285 if (waitingForDebuggerState == WaitingForDebuggerBeforeLoadingScript)
286 loadShadowPage();
287 else if (waitingForDebuggerState == WaitingForDebuggerAfterScriptLoaded)
288 startWorkerThread();
291 void WebEmbeddedWorkerImpl::onScriptLoaderFinished()
293 ASSERT(m_mainScriptLoader);
295 if (m_askedToTerminate)
296 return;
298 if (m_mainScriptLoader->failed()) {
299 m_mainScriptLoader.clear();
300 // This deletes 'this'.
301 m_workerContextClient->workerContextFailedToStart();
302 return;
304 m_workerContextClient->workerScriptLoaded();
306 Platform::current()->histogramCustomCounts("ServiceWorker.ScriptSize", m_mainScriptLoader->script().length(), 1000, 5000000, 50);
307 if (m_mainScriptLoader->cachedMetadata())
308 Platform::current()->histogramCustomCounts("ServiceWorker.ScriptCachedMetadataSize", m_mainScriptLoader->cachedMetadata()->size(), 1000, 50000000, 50);
310 startWorkerThread();
313 void WebEmbeddedWorkerImpl::startWorkerThread()
315 ASSERT(!m_askedToTerminate);
317 Document* document = m_mainFrame->frame()->document();
319 WorkerThreadStartMode startMode = DontPauseWorkerGlobalScopeOnStart;
320 if (InspectorInstrumentation::shouldPauseDedicatedWorkerOnStart(document))
321 startMode = PauseWorkerGlobalScopeOnStart;
323 // FIXME: this document's origin is pristine and without any extra privileges. (crbug.com/254993)
324 SecurityOrigin* starterOrigin = document->securityOrigin();
326 OwnPtrWillBeRawPtr<WorkerClients> workerClients = WorkerClients::create();
327 provideContentSettingsClientToWorker(workerClients.get(), m_contentSettingsClient.release());
328 provideServiceWorkerGlobalScopeClientToWorker(workerClients.get(), ServiceWorkerGlobalScopeClientImpl::create(*m_workerContextClient));
329 provideServiceWorkerContainerClientToWorker(workerClients.get(), adoptPtr(m_workerContextClient->createServiceWorkerProvider()));
331 // We need to set the CSP to both the shadow page's document and the ServiceWorkerGlobalScope.
332 document->initContentSecurityPolicy(m_mainScriptLoader->releaseContentSecurityPolicy());
334 KURL scriptURL = m_mainScriptLoader->url();
335 OwnPtr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(
336 scriptURL,
337 m_workerStartData.userAgent,
338 m_mainScriptLoader->script(),
339 m_mainScriptLoader->releaseCachedMetadata(),
340 startMode,
341 document->contentSecurityPolicy()->headers(),
342 starterOrigin,
343 workerClients.release(),
344 static_cast<V8CacheOptions>(m_workerStartData.v8CacheOptions));
346 m_mainScriptLoader.clear();
348 m_workerGlobalScopeProxy = ServiceWorkerGlobalScopeProxy::create(*this, *document, *m_workerContextClient);
349 m_loaderProxy = WorkerLoaderProxy::create(this);
350 m_workerThread = ServiceWorkerThread::create(m_loaderProxy, *m_workerGlobalScopeProxy);
351 m_workerThread->start(startupData.release());
352 m_workerInspectorProxy->workerThreadCreated(document, m_workerThread.get(), scriptURL);
355 } // namespace blink