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: 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. 76 void cancel() 77 { 78 MutexLocker locker(m_mutex); 79 m_worker = 0; 80 } 81 82 bool result() 83 { 84 return m_result; 85 } 86 87 // This method is invoked on the main thread. 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: 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 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 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. 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 142 WebWorkerBase::WebWorkerBase() 143 : m_webView(0) 144 , m_askedToTerminate(false) 145 { 146 initializeWebKitStaticValues(); 147 } 148 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 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 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 191 void WebWorkerBase::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task) 192 { 193 callOnMainThread(invokeTaskMethod, task.leakPtr()); 194 } 195 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 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 211 WebApplicationCacheHost* WebWorkerBase::createApplicationCacheHost(WebFrame*, WebApplicationCacheHostClient* appcacheHostClient) 212 { 213 if (commonClient()) 214 return commonClient()->createApplicationCacheHost(appcacheHostClient); 215 return 0; 216 } 217 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) 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 262 void WebWorkerBase::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, 263 PassOwnPtr<MessagePortChannelArray> channels) 264 { 265 dispatchTaskToMainThread(createCallbackTask(&postMessageTask, this, 266 message->toWireString(), channels)); 267 } 268 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 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 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 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 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 335 void WebWorkerBase::confirmMessageFromWorkerObject(bool hasPendingActivity) 336 { 337 dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, this, 338 hasPendingActivity)); 339 } 340 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 350 void WebWorkerBase::reportPendingActivity(bool hasPendingActivity) 351 { 352 dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask, 353 this, hasPendingActivity)); 354 } 355 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 365 void WebWorkerBase::workerContextClosed() 366 { 367 dispatchTaskToMainThread(createCallbackTask(&workerContextClosedTask, 368 this)); 369 } 370 371 void WebWorkerBase::workerContextClosedTask(ScriptExecutionContext* context, 372 WebWorkerBase* thisPtr) 373 { 374 if (thisPtr->commonClient()) 375 thisPtr->commonClient()->workerContextClosed(); 376 377 thisPtr->stopWorkerThread(); 378 } 379 380 void WebWorkerBase::workerContextDestroyed() 381 { 382 dispatchTaskToMainThread(createCallbackTask(&workerContextDestroyedTask, 383 this)); 384 } 385 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 397 void WebWorkerBase::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task) 398 { 399 ASSERT(m_loadingDocument->isDocument()); 400 m_loadingDocument->postTask(task); 401 } 402 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