• 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 "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