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/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"
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
, ());
83 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(PassOwnPtr
<WebServiceWorkerContextClient
> client
, PassOwnPtr
<WebWorkerContentSettingsClientProxy
> ContentSettingsClient
)
84 : m_workerContextClient(client
)
85 , m_contentSettingsClient(ContentSettingsClient
)
86 , m_workerInspectorProxy(WorkerInspectorProxy::create())
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;
102 m_workerThread
->terminateAndWait();
104 ASSERT(runningWorkerInstances().contains(this));
105 runningWorkerInstances().remove(this);
108 // Detach the client before closing the view to avoid getting called back.
109 m_mainFrame
->setClient(0);
112 m_mainFrame
->close();
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
)
130 m_askedToTerminate
= true;
131 if (m_loadingShadowPage
) {
132 // This deletes 'this'.
133 m_workerContextClient
->workerContextFailedToStart();
136 if (m_mainScriptLoader
) {
137 m_mainScriptLoader
->cancel();
138 m_mainScriptLoader
.clear();
139 // This deletes 'this'.
140 m_workerContextClient
->workerContextFailedToStart();
144 m_workerThread
->terminate();
145 m_workerInspectorProxy
->workerThreadTerminated();
148 void WebEmbeddedWorkerImpl::attachDevTools(const WebString
& hostId
)
150 WebDevToolsAgent
* devtoolsAgent
= m_mainFrame
->devToolsAgent();
152 devtoolsAgent
->attach(hostId
);
155 void WebEmbeddedWorkerImpl::reattachDevTools(const WebString
& hostId
, const WebString
& savedState
)
157 WebDevToolsAgent
* devtoolsAgent
= m_mainFrame
->devToolsAgent();
159 devtoolsAgent
->reattach(hostId
, savedState
);
163 void WebEmbeddedWorkerImpl::detachDevTools()
165 WebDevToolsAgent
* devtoolsAgent
= m_mainFrame
->devToolsAgent();
167 devtoolsAgent
->detach();
170 void WebEmbeddedWorkerImpl::dispatchDevToolsMessage(const WebString
& message
)
172 if (m_askedToTerminate
)
174 WebDevToolsAgent
* devtoolsAgent
= m_mainFrame
->devToolsAgent();
176 devtoolsAgent
->dispatchOnInspectorBackend(message
);
179 void WebEmbeddedWorkerImpl::postMessageToPageInspector(const String
& message
)
181 WorkerInspectorProxy::PageInspector
* pageInspector
= m_workerInspectorProxy
->pageInspector();
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
)
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.
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
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
;
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.
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
);
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
,
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
)
287 else if (waitingForDebuggerState
== WaitingForDebuggerAfterScriptLoaded
)
291 void WebEmbeddedWorkerImpl::onScriptLoaderFinished()
293 ASSERT(m_mainScriptLoader
);
295 if (m_askedToTerminate
)
298 if (m_mainScriptLoader
->failed()) {
299 m_mainScriptLoader
.clear();
300 // This deletes 'this'.
301 m_workerContextClient
->workerContextFailedToStart();
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);
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(
337 m_workerStartData
.userAgent
,
338 m_mainScriptLoader
->script(),
339 m_mainScriptLoader
->releaseCachedMetadata(),
341 document
->contentSecurityPolicy()->headers(),
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
);