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 "GenericWorkerTask.h"
35 #include "MessagePortChannel.h"
36 #include "PlatformMessagePortChannel.h"
37
38 #include "WebDataSourceImpl.h"
39 #include "WebFrameClient.h"
40 #include "WebFrameImpl.h"
41 #include "WebMessagePortChannel.h"
42 #include "WebView.h"
43 #include "WebWorkerClient.h"
44
45 #include "WorkerThread.h"
46 #include <wtf/MainThread.h>
47
48 using namespace WebCore;
49
50 namespace WebKit {
51
52 #if ENABLE(WORKERS)
53
54 // Dummy WebViewDelegate - we only need it in Worker process to load a
55 // 'shadow page' which will initialize WebCore loader.
56 class WorkerWebFrameClient : public WebFrameClient {
57 public:
58 // Tell the loader to load the data into the 'shadow page' synchronously,
59 // so we can grab the resulting Document right after load.
didCreateDataSource(WebFrame * frame,WebDataSource * ds)60 virtual void didCreateDataSource(WebFrame* frame, WebDataSource* ds)
61 {
62 static_cast<WebDataSourceImpl*>(ds)->setDeferMainResourceDataLoad(false);
63 }
64
65 // Lazy allocate and leak this instance.
sharedInstance()66 static WorkerWebFrameClient* sharedInstance()
67 {
68 static WorkerWebFrameClient client;
69 return &client;
70 }
71
72 private:
WorkerWebFrameClient()73 WorkerWebFrameClient()
74 {
75 }
76 };
77
78 // This function is called on the main thread to force to initialize some static
79 // values used in WebKit before any worker thread is started. This is because in
80 // our worker processs, we do not run any WebKit code in main thread and thus
81 // when multiple workers try to start at the same time, we might hit crash due
82 // to contention for initializing static values.
initializeWebKitStaticValues()83 static void initializeWebKitStaticValues()
84 {
85 static bool initialized = false;
86 if (!initialized) {
87 initialized = true;
88 // Note that we have to pass a URL with valid protocol in order to follow
89 // the path to do static value initializations.
90 RefPtr<SecurityOrigin> origin =
91 SecurityOrigin::create(KURL(ParsedURLString, "http://localhost"));
92 origin.release();
93 }
94 }
95
WebWorkerBase()96 WebWorkerBase::WebWorkerBase()
97 : m_webView(0)
98 , m_askedToTerminate(false)
99 {
100 initializeWebKitStaticValues();
101 }
102
~WebWorkerBase()103 WebWorkerBase::~WebWorkerBase()
104 {
105 ASSERT(m_webView);
106 m_webView->close();
107 }
108
stopWorkerThread()109 void WebWorkerBase::stopWorkerThread()
110 {
111 if (m_askedToTerminate)
112 return;
113 m_askedToTerminate = true;
114 if (m_workerThread)
115 m_workerThread->stop();
116 }
117
initializeLoader(const WebURL & url)118 void WebWorkerBase::initializeLoader(const WebURL& url)
119 {
120 // Create 'shadow page'. This page is never displayed, it is used to proxy the
121 // loading requests from the worker context to the rest of WebKit and Chromium
122 // infrastructure.
123 ASSERT(!m_webView);
124 m_webView = WebView::create(0);
125 m_webView->initializeMainFrame(WorkerWebFrameClient::sharedInstance());
126
127 WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame());
128
129 // Construct substitute data source for the 'shadow page'. We only need it
130 // to have same origin as the worker so the loading checks work correctly.
131 CString content("");
132 int len = static_cast<int>(content.length());
133 RefPtr<SharedBuffer> buf(SharedBuffer::create(content.data(), len));
134 SubstituteData substData(buf, String("text/html"), String("UTF-8"), KURL());
135 webFrame->frame()->loader()->load(ResourceRequest(url), substData, false);
136
137 // This document will be used as 'loading context' for the worker.
138 m_loadingDocument = webFrame->frame()->document();
139 }
140
dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)141 void WebWorkerBase::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)
142 {
143 return callOnMainThread(invokeTaskMethod, task.release());
144 }
145
invokeTaskMethod(void * param)146 void WebWorkerBase::invokeTaskMethod(void* param)
147 {
148 ScriptExecutionContext::Task* task =
149 static_cast<ScriptExecutionContext::Task*>(param);
150 task->performTask(0);
151 delete task;
152 }
153
154 // WorkerObjectProxy -----------------------------------------------------------
155
postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,PassOwnPtr<MessagePortChannelArray> channels)156 void WebWorkerBase::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message,
157 PassOwnPtr<MessagePortChannelArray> channels)
158 {
159 dispatchTaskToMainThread(createCallbackTask(&postMessageTask, this,
160 message->toWireString(), channels));
161 }
162
postMessageTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,String message,PassOwnPtr<MessagePortChannelArray> channels)163 void WebWorkerBase::postMessageTask(ScriptExecutionContext* context,
164 WebWorkerBase* thisPtr,
165 String message,
166 PassOwnPtr<MessagePortChannelArray> channels)
167 {
168 if (!thisPtr->client())
169 return;
170
171 WebMessagePortChannelArray webChannels(channels.get() ? channels->size() : 0);
172 for (size_t i = 0; i < webChannels.size(); ++i) {
173 webChannels[i] = (*channels)[i]->channel()->webChannelRelease();
174 webChannels[i]->setClient(0);
175 }
176
177 thisPtr->client()->postMessageToWorkerObject(message, webChannels);
178 }
179
postExceptionToWorkerObject(const String & errorMessage,int lineNumber,const String & sourceURL)180 void WebWorkerBase::postExceptionToWorkerObject(const String& errorMessage,
181 int lineNumber,
182 const String& sourceURL)
183 {
184 dispatchTaskToMainThread(createCallbackTask(&postExceptionTask, this,
185 errorMessage, lineNumber,
186 sourceURL));
187 }
188
postExceptionTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,const String & errorMessage,int lineNumber,const String & sourceURL)189 void WebWorkerBase::postExceptionTask(ScriptExecutionContext* context,
190 WebWorkerBase* thisPtr,
191 const String& errorMessage,
192 int lineNumber, const String& sourceURL)
193 {
194 if (!thisPtr->commonClient())
195 return;
196
197 thisPtr->commonClient()->postExceptionToWorkerObject(errorMessage,
198 lineNumber,
199 sourceURL);
200 }
201
postConsoleMessageToWorkerObject(MessageDestination destination,MessageSource source,MessageType type,MessageLevel level,const String & message,int lineNumber,const String & sourceURL)202 void WebWorkerBase::postConsoleMessageToWorkerObject(MessageDestination destination,
203 MessageSource source,
204 MessageType type,
205 MessageLevel level,
206 const String& message,
207 int lineNumber,
208 const String& sourceURL)
209 {
210 dispatchTaskToMainThread(createCallbackTask(&postConsoleMessageTask, this,
211 static_cast<int>(destination),
212 static_cast<int>(source),
213 static_cast<int>(type),
214 static_cast<int>(level),
215 message, lineNumber, sourceURL));
216 }
217
postConsoleMessageTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,int destination,int source,int type,int level,const String & message,int lineNumber,const String & sourceURL)218 void WebWorkerBase::postConsoleMessageTask(ScriptExecutionContext* context,
219 WebWorkerBase* thisPtr,
220 int destination, int source,
221 int type, int level,
222 const String& message,
223 int lineNumber,
224 const String& sourceURL)
225 {
226 if (!thisPtr->commonClient())
227 return;
228 thisPtr->commonClient()->postConsoleMessageToWorkerObject(destination, source,
229 type, level, message,
230 lineNumber, sourceURL);
231 }
232
confirmMessageFromWorkerObject(bool hasPendingActivity)233 void WebWorkerBase::confirmMessageFromWorkerObject(bool hasPendingActivity)
234 {
235 dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, this,
236 hasPendingActivity));
237 }
238
confirmMessageTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,bool hasPendingActivity)239 void WebWorkerBase::confirmMessageTask(ScriptExecutionContext* context,
240 WebWorkerBase* thisPtr,
241 bool hasPendingActivity)
242 {
243 if (!thisPtr->client())
244 return;
245 thisPtr->client()->confirmMessageFromWorkerObject(hasPendingActivity);
246 }
247
reportPendingActivity(bool hasPendingActivity)248 void WebWorkerBase::reportPendingActivity(bool hasPendingActivity)
249 {
250 dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask,
251 this, hasPendingActivity));
252 }
253
reportPendingActivityTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr,bool hasPendingActivity)254 void WebWorkerBase::reportPendingActivityTask(ScriptExecutionContext* context,
255 WebWorkerBase* thisPtr,
256 bool hasPendingActivity)
257 {
258 if (!thisPtr->client())
259 return;
260 thisPtr->client()->reportPendingActivity(hasPendingActivity);
261 }
262
workerContextClosed()263 void WebWorkerBase::workerContextClosed()
264 {
265 dispatchTaskToMainThread(createCallbackTask(&workerContextClosedTask,
266 this));
267 }
268
workerContextClosedTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr)269 void WebWorkerBase::workerContextClosedTask(ScriptExecutionContext* context,
270 WebWorkerBase* thisPtr)
271 {
272 if (thisPtr->commonClient())
273 thisPtr->commonClient()->workerContextClosed();
274
275 thisPtr->stopWorkerThread();
276 }
277
workerContextDestroyed()278 void WebWorkerBase::workerContextDestroyed()
279 {
280 dispatchTaskToMainThread(createCallbackTask(&workerContextDestroyedTask,
281 this));
282 }
283
workerContextDestroyedTask(ScriptExecutionContext * context,WebWorkerBase * thisPtr)284 void WebWorkerBase::workerContextDestroyedTask(ScriptExecutionContext* context,
285 WebWorkerBase* thisPtr)
286 {
287 if (thisPtr->commonClient())
288 thisPtr->commonClient()->workerContextDestroyed();
289 // The lifetime of this proxy is controlled by the worker context.
290 delete thisPtr;
291 }
292
293 // WorkerLoaderProxy -----------------------------------------------------------
294
postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)295 void WebWorkerBase::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task)
296 {
297 ASSERT(m_loadingDocument->isDocument());
298 m_loadingDocument->postTask(task);
299 }
300
postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task> task,const String & mode)301 void WebWorkerBase::postTaskForModeToWorkerContext(
302 PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
303 {
304 m_workerThread->runLoop().postTaskForMode(task, mode);
305 }
306
307 #endif // ENABLE(WORKERS)
308
309 } // namespace WebKit
310