Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / web / WebEmbeddedWorkerImpl.cpp
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/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"
67
68 namespace blink {
69
70 // A thin wrapper for one-off script loading.
71 class WebEmbeddedWorkerImpl::Loader : public WorkerScriptLoaderClient {
72 public:
73     static PassOwnPtr<Loader> create()
74     {
75         return adoptPtr(new Loader());
76     }
77
78     virtual ~Loader()
79     {
80         m_scriptLoader->setClient(0);
81     }
82
83     void load(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& callback)
84     {
85         ASSERT(loadingContext);
86         m_callback = callback;
87         m_scriptLoader->setRequestContext(WebURLRequest::RequestContextServiceWorker);
88         m_scriptLoader->loadAsynchronously(
89             *loadingContext, scriptURL, DenyCrossOriginRequests, this);
90     }
91
92     virtual void notifyFinished() OVERRIDE
93     {
94         m_callback();
95     }
96
97     void cancel()
98     {
99         m_scriptLoader->cancel();
100     }
101
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(); }
105
106 private:
107     Loader() : m_scriptLoader(WorkerScriptLoader::create())
108     {
109     }
110
111     RefPtr<WorkerScriptLoader> m_scriptLoader;
112     Closure m_callback;
113 };
114
115 class WebEmbeddedWorkerImpl::LoaderProxy : public WorkerLoaderProxy {
116 public:
117     static PassOwnPtr<LoaderProxy> create(WebEmbeddedWorkerImpl& embeddedWorker)
118     {
119         return adoptPtr(new LoaderProxy(embeddedWorker));
120     }
121
122     virtual void postTaskToLoader(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
123     {
124         toWebLocalFrameImpl(m_embeddedWorker.m_mainFrame)->frame()->document()->postTask(task);
125     }
126
127     virtual bool postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task) OVERRIDE
128     {
129         if (m_embeddedWorker.m_askedToTerminate || !m_embeddedWorker.m_workerThread)
130             return false;
131         m_embeddedWorker.m_workerThread->postTask(task);
132         return !m_embeddedWorker.m_workerThread->terminated();
133     }
134
135 private:
136     explicit LoaderProxy(WebEmbeddedWorkerImpl& embeddedWorker)
137         : m_embeddedWorker(embeddedWorker)
138     {
139     }
140
141     // Not owned, embedded worker owns this.
142     WebEmbeddedWorkerImpl& m_embeddedWorker;
143 };
144
145 WebEmbeddedWorker* WebEmbeddedWorker::create(
146     WebServiceWorkerContextClient* client,
147     WebWorkerPermissionClientProxy* permissionClient)
148 {
149     return new WebEmbeddedWorkerImpl(adoptPtr(client), adoptPtr(permissionClient));
150 }
151
152 static HashSet<WebEmbeddedWorkerImpl*>& runningWorkerInstances()
153 {
154     DEFINE_STATIC_LOCAL(HashSet<WebEmbeddedWorkerImpl*>, set, ());
155     return set;
156 }
157
158 WebEmbeddedWorkerImpl::WebEmbeddedWorkerImpl(
159     PassOwnPtr<WebServiceWorkerContextClient> client,
160     PassOwnPtr<WebWorkerPermissionClientProxy> permissionClient)
161     : m_workerContextClient(client)
162     , m_permissionClient(permissionClient)
163     , m_workerInspectorProxy(WorkerInspectorProxy::create())
164     , m_webView(0)
165     , m_mainFrame(0)
166     , m_askedToTerminate(false)
167     , m_pauseAfterDownloadState(DontPauseAfterDownload)
168     , m_waitingForDebuggerState(NotWaitingForDebugger)
169 {
170     runningWorkerInstances().add(this);
171 }
172
173 WebEmbeddedWorkerImpl::~WebEmbeddedWorkerImpl()
174 {
175     ASSERT(runningWorkerInstances().contains(this));
176     runningWorkerInstances().remove(this);
177     ASSERT(m_webView);
178
179     // Detach the client before closing the view to avoid getting called back.
180     toWebLocalFrameImpl(m_mainFrame)->setClient(0);
181
182     m_webView->close();
183     m_mainFrame->close();
184 }
185
186 void WebEmbeddedWorkerImpl::terminateAll()
187 {
188     HashSet<WebEmbeddedWorkerImpl*> instances = runningWorkerInstances();
189     for (HashSet<WebEmbeddedWorkerImpl*>::iterator it = instances.begin(), itEnd = instances.end(); it != itEnd; ++it) {
190         (*it)->terminateWorkerContext();
191     }
192 }
193
194 void WebEmbeddedWorkerImpl::startWorkerContext(
195     const WebEmbeddedWorkerStartData& data)
196 {
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();
204 }
205
206 void WebEmbeddedWorkerImpl::terminateWorkerContext()
207 {
208     if (m_askedToTerminate)
209         return;
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();
216         return;
217     }
218     if (m_pauseAfterDownloadState == IsPausedAfterDownload) {
219         // This may delete 'this'.
220         m_workerContextClient->workerContextFailedToStart();
221         return;
222     }
223     if (m_workerThread)
224         m_workerThread->stop();
225     m_workerInspectorProxy->workerThreadTerminated();
226 }
227
228 void WebEmbeddedWorkerImpl::resumeAfterDownload()
229 {
230     ASSERT(!m_askedToTerminate);
231     bool wasPaused = (m_pauseAfterDownloadState == IsPausedAfterDownload);
232     m_pauseAfterDownloadState = DontPauseAfterDownload;
233
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;
238     else if (wasPaused)
239         startWorkerThread();
240 }
241
242 void WebEmbeddedWorkerImpl::resumeWorkerContext()
243 {
244 }
245
246 void WebEmbeddedWorkerImpl::attachDevTools(const WebString& hostId)
247 {
248     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
249     if (devtoolsAgent)
250         devtoolsAgent->attach(hostId);
251 }
252
253 void WebEmbeddedWorkerImpl::reattachDevTools(const WebString& hostId, const WebString& savedState)
254 {
255     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
256     if (devtoolsAgent)
257         devtoolsAgent->reattach(hostId, savedState);
258     resumeStartup();
259 }
260
261 void WebEmbeddedWorkerImpl::detachDevTools()
262 {
263     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
264     if (devtoolsAgent)
265         devtoolsAgent->detach();
266 }
267
268 void WebEmbeddedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
269 {
270     if (m_askedToTerminate)
271         return;
272     WebDevToolsAgent* devtoolsAgent = m_webView->devToolsAgent();
273     if (devtoolsAgent)
274         devtoolsAgent->dispatchOnInspectorBackend(message);
275 }
276
277 void WebEmbeddedWorkerImpl::postMessageToPageInspector(const String& message)
278 {
279     WorkerInspectorProxy::PageInspector* pageInspector = m_workerInspectorProxy->pageInspector();
280     if (!pageInspector)
281         return;
282     pageInspector->dispatchMessageFromWorker(message);
283 }
284
285 void WebEmbeddedWorkerImpl::prepareShadowPageForLoader()
286 {
287     // Create 'shadow page', which is never displayed and is used mainly to
288     // provide a context for loading on the main thread.
289     //
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.
293     ASSERT(!m_webView);
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);
301
302     WebLocalFrameImpl* webFrame = toWebLocalFrameImpl(m_webView->mainFrame());
303
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.
306     CString content("");
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())));
310 }
311
312 void WebEmbeddedWorkerImpl::willSendRequest(
313     WebLocalFrame* frame, unsigned, WebURLRequest& request,
314     const WebURLResponse& redirectResponse)
315 {
316     if (m_networkProvider)
317         m_networkProvider->willSendRequest(frame->dataSource(), request);
318 }
319
320 void WebEmbeddedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame)
321 {
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;
330             return;
331         }
332     }
333     startScriptLoader(frame);
334 }
335
336 void WebEmbeddedWorkerImpl::sendMessageToInspectorFrontend(const WebString& message)
337 {
338     m_workerContextClient->dispatchDevToolsMessage(message);
339 }
340
341 void WebEmbeddedWorkerImpl::resumeStartup()
342 {
343     WaitingForDebuggerState waitingForDebuggerState = m_waitingForDebuggerState;
344     m_waitingForDebuggerState = NotWaitingForDebugger;
345     if (waitingForDebuggerState == WaitingForDebuggerBeforeLoadingScript)
346         startScriptLoader(toWebLocalFrameImpl(m_mainFrame));
347     else if (waitingForDebuggerState == WaitingForDebuggerAfterScriptLoaded)
348         startWorkerThread();
349 }
350
351 void WebEmbeddedWorkerImpl::saveAgentRuntimeState(const WebString& inspectorState)
352 {
353     m_workerContextClient->saveDevToolsAgentState(inspectorState);
354 }
355
356 void WebEmbeddedWorkerImpl::startScriptLoader(WebLocalFrame* frame)
357 {
358     ASSERT(!m_mainScriptLoader);
359     ASSERT(!m_networkProvider);
360     ASSERT(m_mainFrame);
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));
368 }
369
370 void WebEmbeddedWorkerImpl::onScriptLoaderFinished()
371 {
372     ASSERT(m_mainScriptLoader);
373
374     if (m_askedToTerminate)
375         return;
376
377     if (m_mainScriptLoader->failed()) {
378         m_mainScriptLoader.clear();
379         // This may delete 'this'.
380         m_workerContextClient->workerContextFailedToStart();
381         return;
382     }
383
384     Platform::current()->histogramCustomCounts("ServiceWorker.ScriptSize", m_mainScriptLoader->script().length(), 1000, 5000000, 50);
385
386     if (m_pauseAfterDownloadState == DoPauseAfterDownload) {
387         m_pauseAfterDownloadState = IsPausedAfterDownload;
388         m_workerContextClient->didPauseAfterDownload();
389         return;
390     }
391     startWorkerThread();
392 }
393
394 void WebEmbeddedWorkerImpl::startWorkerThread()
395 {
396     ASSERT(m_pauseAfterDownloadState == DontPauseAfterDownload);
397     ASSERT(!m_askedToTerminate);
398
399     Document* document = toWebLocalFrameImpl(m_mainFrame)->frame()->document();
400
401     WorkerThreadStartMode startMode = DontPauseWorkerGlobalScopeOnStart;
402     if (InspectorInstrumentation::shouldPauseDedicatedWorkerOnStart(document))
403         startMode = PauseWorkerGlobalScopeOnStart;
404
405     OwnPtrWillBeRawPtr<WorkerClients> workerClients = WorkerClients::create();
406     providePermissionClientToWorker(workerClients.get(), m_permissionClient.release());
407     provideServiceWorkerGlobalScopeClientToWorker(workerClients.get(), ServiceWorkerGlobalScopeClientImpl::create(*m_workerContextClient));
408
409     KURL scriptURL = m_mainScriptLoader->url();
410     OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData =
411         WorkerThreadStartupData::create(
412             scriptURL,
413             m_workerStartData.userAgent,
414             m_mainScriptLoader->script(),
415             startMode,
416             // FIXME: fill appropriate CSP info and policy type.
417             String(),
418             ContentSecurityPolicyHeaderTypeEnforce,
419             workerClients.release());
420
421     m_mainScriptLoader.clear();
422
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);
428 }
429
430 } // namespace blink