• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 "WebWorkerBase.h"
33 
34 #include "CrossThreadTask.h"
35 #include "DatabaseTask.h"
36 #include "MessagePortChannel.h"
37 #include "PlatformMessagePortChannel.h"
38 
39 #include "WebDataSourceImpl.h"
40 #include "WebFileError.h"
41 #include "WebFrameClient.h"
42 #include "WebFrameImpl.h"
43 #include "WebMessagePortChannel.h"
44 #include "WebRuntimeFeatures.h"
45 #include "WebSettings.h"
46 #include "WebView.h"
47 #include "WebWorkerClient.h"
48 
49 #include "WorkerContext.h"
50 #include "WorkerFileSystemCallbacksBridge.h"
51 #include "WorkerScriptController.h"
52 #include "WorkerThread.h"
53 #include <wtf/MainThread.h>
54 
55 using namespace WebCore;
56 
57 namespace WebKit {
58 
59 #if ENABLE(WORKERS)
60 
61 static const char allowDatabaseMode[] = "allowDatabaseMode";
62 static const char openFileSystemMode[] = "openFileSystemMode";
63 
64 namespace {
65 
66 // This class is used to route the result of the WebWorkerBase::allowDatabase
67 // call back to the worker context.
68 class AllowDatabaseMainThreadBridge : public ThreadSafeRefCounted<AllowDatabaseMainThreadBridge> {
69 public:
create(WebWorkerBase * worker,const WTF::String & mode,WebCommonWorkerClient * commonClient,WebFrame * frame,const WTF::String & name,const WTF::String & displayName,unsigned long estimatedSize)70     static PassRefPtr<AllowDatabaseMainThreadBridge> create(WebWorkerBase* worker, const WTF::String& mode, WebCommonWorkerClient* commonClient, WebFrame* frame, const WTF::String& name, const WTF::String& displayName, unsigned long estimatedSize)
71     {
72         return adoptRef(new AllowDatabaseMainThreadBridge(worker, mode, commonClient, frame, name, displayName, estimatedSize));
73     }
74 
75     // These methods are invoked on the worker context.
cancel()76     void cancel()
77     {
78         MutexLocker locker(m_mutex);
79         m_worker = 0;
80     }
81 
result()82     bool result()
83     {
84         return m_result;
85     }
86 
87     // This method is invoked on the main thread.
signalCompleted(bool result)88     void signalCompleted(bool result)
89     {
90         MutexLocker locker(m_mutex);
91         if (m_worker)
92             m_worker->postTaskForModeToWorkerContext(createCallbackTask(&didComplete, this, result), m_mode);
93     }
94 
95 private:
AllowDatabaseMainThreadBridge(WebWorkerBase * worker,const WTF::String & mode,WebCommonWorkerClient * commonClient,WebFrame * frame,const WTF::String & name,const WTF::String & displayName,unsigned long estimatedSize)96     AllowDatabaseMainThreadBridge(WebWorkerBase* worker, const WTF::String& mode, WebCommonWorkerClient* commonClient, WebFrame* frame, const WTF::String& name, const WTF::String& displayName, unsigned long estimatedSize)
97         : m_worker(worker)
98         , m_mode(mode)
99     {
100         worker->dispatchTaskToMainThread(createCallbackTask(&allowDatabaseTask, commonClient, frame, String(name), String(displayName), estimatedSize, this));
101     }
102 
allowDatabaseTask(WebCore::ScriptExecutionContext * context,WebCommonWorkerClient * commonClient,WebFrame * frame,const WTF::String name,const WTF::String displayName,unsigned long estimatedSize,PassRefPtr<AllowDatabaseMainThreadBridge> bridge)103     static void allowDatabaseTask(WebCore::ScriptExecutionContext* context, WebCommonWorkerClient* commonClient, WebFrame* frame, const WTF::String name, const WTF::String displayName, unsigned long estimatedSize, PassRefPtr<AllowDatabaseMainThreadBridge> bridge)
104     {
105         if (!commonClient)
106             bridge->signalCompleted(false);
107         else
108             bridge->signalCompleted(commonClient->allowDatabase(frame, name, displayName, estimatedSize));
109     }
110 
didComplete(WebCore::ScriptExecutionContext * context,PassRefPtr<AllowDatabaseMainThreadBridge> bridge,bool result)111     static void didComplete(WebCore::ScriptExecutionContext* context, PassRefPtr<AllowDatabaseMainThreadBridge> bridge, bool result)
112     {
113         bridge->m_result = result;
114     }
115 
116     bool m_result;
117     Mutex m_mutex;
118     WebWorkerBase* m_worker;
119     WTF::String m_mode;
120 };
121 
122 }
123 
124 // This function is called on the main thread to force to initialize some static
125 // values used in WebKit before any worker thread is started. This is because in
126 // our worker processs, we do not run any WebKit code in main thread and thus
127 // when multiple workers try to start at the same time, we might hit crash due
128 // to contention for initializing static values.
initializeWebKitStaticValues()129 static void initializeWebKitStaticValues()
130 {
131     static bool initialized = false;
132     if (!initialized) {
133         initialized = true;
134         // Note that we have to pass a URL with valid protocol in order to follow
135         // the path to do static value initializations.
136         RefPtr<SecurityOrigin> origin =
137             SecurityOrigin::create(KURL(ParsedURLString, "http://localhost"));
138         origin.release();
139     }
140 }
141 
WebWorkerBase()142 WebWorkerBase::WebWorkerBase()
143     : m_webView(0)
144     , m_askedToTerminate(false)
145 {
146     initializeWebKitStaticValues();
147 }
148 
~WebWorkerBase()149 WebWorkerBase::~WebWorkerBase()
150 {
151     ASSERT(m_webView);
152     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
153     if (webFrame)
154         webFrame->setClient(0);
155     m_webView->close();
156 }
157 
stopWorkerThread()158 void WebWorkerBase::stopWorkerThread()
159 {
160     if (m_askedToTerminate)
161         return;
162     m_askedToTerminate = true;
163     if (m_workerThread)
164         m_workerThread->stop();
165 }
166 
initializeLoader(const WebURL & url)167 void WebWorkerBase::initializeLoader(const WebURL& url)
168 {
169     // Create 'shadow page'. This page is never displayed, it is used to proxy the
170     // loading requests from the worker context to the rest of WebKit and Chromium
171     // infrastructure.
172     ASSERT(!m_webView);
173     m_webView = WebView::create(0);
174     m_webView->settings()->setOfflineWebApplicationCacheEnabled(WebRuntimeFeatures::isApplicationCacheEnabled());
175     m_webView->initializeMainFrame(this);
176 
177     WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
178 
179     // Construct substitute data source for the 'shadow page'. We only need it
180     // to have same origin as the worker so the loading checks work correctly.
181     CString content("");
182     int len = static_cast<int>(content.length());
183     RefPtr<SharedBuffer> buf(SharedBuffer::create(content.data(), len));
184     SubstituteData substData(buf, String("text/html"), String("UTF-8"), KURL());
185     webFrame->frame()->loader()->load(ResourceRequest(url), substData, false);
186 
187     // This document will be used as 'loading context' for the worker.
188     m_loadingDocument = webFrame->frame()->document();
189 }
190 
dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)191 void WebWorkerBase::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)
192 {
193     callOnMainThread(invokeTaskMethod, task.leakPtr());
194 }
195 
invokeTaskMethod(void * param)196 void WebWorkerBase::invokeTaskMethod(void* param)
197 {
198     ScriptExecutionContext::Task* task =
199         static_cast<ScriptExecutionContext::Task*>(param);
200     task->performTask(0);
201     delete task;
202 }
203 
didCreateDataSource(WebFrame *,WebDataSource * ds)204 void WebWorkerBase::didCreateDataSource(WebFrame*, WebDataSource* ds)
205 {
206     // Tell the loader to load the data into the 'shadow page' synchronously,
207     // so we can grab the resulting Document right after load.
208     static_cast<WebDataSourceImpl*>(ds)->setDeferMainResourceDataLoad(false);
209 }
210 
createApplicationCacheHost(WebFrame *,WebApplicationCacheHostClient * appcacheHostClient)211 WebApplicationCacheHost* WebWorkerBase::createApplicationCacheHost(WebFrame*, WebApplicationCacheHostClient* appcacheHostClient)
212 {
213     if (commonClient())
214         return commonClient()->createApplicationCacheHost(appcacheHostClient);
215     return 0;
216 }
217 
allowDatabase(WebFrame *,const WebString & name,const WebString & displayName,unsigned long estimatedSize)218 bool WebWorkerBase::allowDatabase(WebFrame*, const WebString& name, const WebString& displayName, unsigned long estimatedSize)
219 {
220     WorkerRunLoop& runLoop = m_workerThread->runLoop();
221     WorkerScriptController* controller = WorkerScriptController::controllerForContext();
222     WorkerContext* workerContext = controller->workerContext();
223 
224     // Create a unique mode just for this synchronous call.
225     String mode = allowDatabaseMode;
226     mode.append(String::number(runLoop.createUniqueId()));
227 
228     RefPtr<AllowDatabaseMainThreadBridge> bridge = AllowDatabaseMainThreadBridge::create(this, mode, commonClient(), m_webView->mainFrame(), String(name), String(displayName), estimatedSize);
229 
230     // Either the bridge returns, or the queue gets terminated.
231     if (runLoop.runInMode(workerContext, mode) == MessageQueueTerminated) {
232         bridge->cancel();
233         return false;
234     }
235 
236     return bridge->result();
237 }
238 
239 #if ENABLE(FILE_SYSTEM)
openFileSystemForWorker(WebFileSystem::Type type,long long size,bool create,WebFileSystemCallbacks * callbacks,bool synchronous)240 void WebWorkerBase::openFileSystemForWorker(WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks, bool synchronous)
241 {
242     WorkerRunLoop& runLoop = m_workerThread->runLoop();
243     WorkerScriptController* controller = WorkerScriptController::controllerForContext();
244     WorkerContext* workerContext = controller->workerContext();
245 
246     // Create a unique mode for this openFileSystem call.
247     String mode = openFileSystemMode;
248     mode.append(String::number(runLoop.createUniqueId()));
249 
250     RefPtr<WorkerFileSystemCallbacksBridge> bridge = WorkerFileSystemCallbacksBridge::create(this, workerContext, callbacks);
251     bridge->postOpenFileSystemToMainThread(commonClient(), type, size, create, mode);
252 
253     if (synchronous) {
254         if (runLoop.runInMode(workerContext, mode) == MessageQueueTerminated)
255             bridge->stop();
256     }
257 }
258 #endif
259 
260 // WorkerObjectProxy -----------------------------------------------------------
261 
postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)262 void WebWorkerBase::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,
263                                               PassOwnPtr<MessagePortChannelArray> channels)
264 {
265     dispatchTaskToMainThread(createCallbackTask(&postMessageTask, this,
266                                                 message->toWireString(), channels));
267 }
268 
postMessageTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,String message,PassOwnPtr<MessagePortChannelArray> channels)269 void WebWorkerBase::postMessageTask(ScriptExecutionContext* context,
270                                     WebWorkerBase* thisPtr,
271                                     String message,
272                                     PassOwnPtr<MessagePortChannelArray> channels)
273 {
274     if (!thisPtr->client())
275         return;
276 
277     WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
278     for (size_t i = 0; i < webChannels.size(); ++i) {
279         webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
280         webChannels[i]->setClient(0);
281     }
282 
283     thisPtr->client()->postMessageToWorkerObject(message, webChannels);
284 }
285 
postExceptionToWorkerObject(const String & errorMessage,int lineNumber,const String & sourceURL)286 void WebWorkerBase::postExceptionToWorkerObject(const String& errorMessage,
287                                                 int lineNumber,
288                                                 const String& sourceURL)
289 {
290     dispatchTaskToMainThread(createCallbackTask(&postExceptionTask, this,
291                                                 errorMessage, lineNumber,
292                                                 sourceURL));
293 }
294 
postExceptionTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,const String & errorMessage,int lineNumber,const String & sourceURL)295 void WebWorkerBase::postExceptionTask(ScriptExecutionContext* context,
296                                       WebWorkerBase* thisPtr,
297                                       const String& errorMessage,
298                                       int lineNumber, const String& sourceURL)
299 {
300     if (!thisPtr->commonClient())
301         return;
302 
303     thisPtr->commonClient()->postExceptionToWorkerObject(errorMessage,
304                                                          lineNumber,
305                                                          sourceURL);
306 }
307 
postConsoleMessageToWorkerObject(MessageSource source,MessageType type,MessageLevel level,const String & message,int lineNumber,const String & sourceURL)308 void WebWorkerBase::postConsoleMessageToWorkerObject(MessageSource source,
309                                                      MessageType type,
310                                                      MessageLevel level,
311                                                      const String& message,
312                                                      int lineNumber,
313                                                      const String& sourceURL)
314 {
315     dispatchTaskToMainThread(createCallbackTask(&postConsoleMessageTask, this,
316                                                 source, type, level,
317                                                 message, lineNumber, sourceURL));
318 }
319 
postConsoleMessageTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,int source,int type,int level,const String & message,int lineNumber,const String & sourceURL)320 void WebWorkerBase::postConsoleMessageTask(ScriptExecutionContext* context,
321                                            WebWorkerBase* thisPtr,
322                                            int source,
323                                            int type, int level,
324                                            const String& message,
325                                            int lineNumber,
326                                            const String& sourceURL)
327 {
328     if (!thisPtr->commonClient())
329         return;
330     thisPtr->commonClient()->postConsoleMessageToWorkerObject(source,
331                                                               type, level, message,
332                                                               lineNumber, sourceURL);
333 }
334 
confirmMessageFromWorkerObject(bool hasPendingActivity)335 void WebWorkerBase::confirmMessageFromWorkerObject(bool hasPendingActivity)
336 {
337     dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, this,
338                                                 hasPendingActivity));
339 }
340 
confirmMessageTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,bool hasPendingActivity)341 void WebWorkerBase::confirmMessageTask(ScriptExecutionContext* context,
342                                        WebWorkerBase* thisPtr,
343                                        bool hasPendingActivity)
344 {
345     if (!thisPtr->client())
346         return;
347     thisPtr->client()->confirmMessageFromWorkerObject(hasPendingActivity);
348 }
349 
reportPendingActivity(bool hasPendingActivity)350 void WebWorkerBase::reportPendingActivity(bool hasPendingActivity)
351 {
352     dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask,
353                                                 this, hasPendingActivity));
354 }
355 
reportPendingActivityTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,bool hasPendingActivity)356 void WebWorkerBase::reportPendingActivityTask(ScriptExecutionContext* context,
357                                               WebWorkerBase* thisPtr,
358                                               bool hasPendingActivity)
359 {
360     if (!thisPtr->client())
361         return;
362     thisPtr->client()->reportPendingActivity(hasPendingActivity);
363 }
364 
workerContextClosed()365 void WebWorkerBase::workerContextClosed()
366 {
367     dispatchTaskToMainThread(createCallbackTask(&workerContextClosedTask,
368                                                 this));
369 }
370 
workerContextClosedTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr)371 void WebWorkerBase::workerContextClosedTask(ScriptExecutionContext* context,
372                                             WebWorkerBase* thisPtr)
373 {
374     if (thisPtr->commonClient())
375         thisPtr->commonClient()->workerContextClosed();
376 
377     thisPtr->stopWorkerThread();
378 }
379 
workerContextDestroyed()380 void WebWorkerBase::workerContextDestroyed()
381 {
382     dispatchTaskToMainThread(createCallbackTask(&workerContextDestroyedTask,
383                                                 this));
384 }
385 
workerContextDestroyedTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr)386 void WebWorkerBase::workerContextDestroyedTask(ScriptExecutionContext* context,
387                                                WebWorkerBase* thisPtr)
388 {
389     if (thisPtr->commonClient())
390         thisPtr->commonClient()->workerContextDestroyed();
391     // The lifetime of this proxy is controlled by the worker context.
392     delete thisPtr;
393 }
394 
395 // WorkerLoaderProxy -----------------------------------------------------------
396 
postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)397 void WebWorkerBase::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
398 {
399     ASSERT(m_loadingDocument->isDocument());
400     m_loadingDocument->postTask(task);
401 }
402 
postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task> task,const String & mode)403 void WebWorkerBase::postTaskForModeToWorkerContext(
404     PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
405 {
406     m_workerThread->runLoop().postTaskForMode(task, mode);
407 }
408 
409 #endif // ENABLE(WORKERS)
410 
411 } // namespace WebKit
412