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 "WebSharedWorkerImpl.h" 33 34 #include "public/platform/WebFileError.h" 35 #include "public/platform/WebMessagePortChannel.h" 36 #include "public/platform/WebString.h" 37 #include "public/platform/WebURL.h" 38 #include "wtf/MainThread.h" 39 #include "WebDataSourceImpl.h" 40 #include "WebFrameClient.h" 41 #include "WebFrameImpl.h" 42 #include "WebRuntimeFeatures.h" 43 #include "WebSettings.h" 44 #include "WebSharedWorkerClient.h" 45 #include "WebView.h" 46 #include "WorkerFileSystemClient.h" 47 #include "core/dom/CrossThreadTask.h" 48 #include "core/dom/Document.h" 49 #include "core/dom/MessageEvent.h" 50 #include "core/dom/MessagePortChannel.h" 51 #include "core/dom/ScriptExecutionContext.h" 52 #include "core/dom/default/chromium/PlatformMessagePortChannelChromium.h" 53 #include "core/html/HTMLFormElement.h" 54 #include "core/inspector/WorkerDebuggerAgent.h" 55 #include "core/inspector/WorkerInspectorController.h" 56 #include "core/loader/FrameLoadRequest.h" 57 #include "core/loader/FrameLoader.h" 58 #include "core/page/Page.h" 59 #include "core/page/PageGroup.h" 60 #include "core/workers/SharedWorkerGlobalScope.h" 61 #include "core/workers/SharedWorkerThread.h" 62 #include "core/workers/WorkerClients.h" 63 #include "core/workers/WorkerGlobalScope.h" 64 #include "core/workers/WorkerLoaderProxy.h" 65 #include "core/workers/WorkerThread.h" 66 #include "core/workers/WorkerThreadStartupData.h" 67 #include "modules/webdatabase/DatabaseTask.h" 68 #include "weborigin/KURL.h" 69 #include "weborigin/SecurityOrigin.h" 70 71 using namespace WebCore; 72 73 namespace WebKit { 74 75 // This function is called on the main thread to force to initialize some static 76 // values used in WebKit before any worker thread is started. This is because in 77 // our worker processs, we do not run any WebKit code in main thread and thus 78 // when multiple workers try to start at the same time, we might hit crash due 79 // to contention for initializing static values. 80 static void initializeWebKitStaticValues() 81 { 82 static bool initialized = false; 83 if (!initialized) { 84 initialized = true; 85 // Note that we have to pass a URL with valid protocol in order to follow 86 // the path to do static value initializations. 87 RefPtr<SecurityOrigin> origin = 88 SecurityOrigin::create(KURL(ParsedURLString, "http://localhost")); 89 origin.release(); 90 } 91 } 92 93 WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client) 94 : m_webView(0) 95 , m_askedToTerminate(false) 96 , m_client(client) 97 , m_pauseWorkerContextOnStart(false) 98 { 99 initializeWebKitStaticValues(); 100 } 101 102 WebSharedWorkerImpl::~WebSharedWorkerImpl() 103 { 104 ASSERT(m_webView); 105 WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame()); 106 if (webFrame) 107 webFrame->setClient(0); 108 m_webView->close(); 109 } 110 111 void WebSharedWorkerImpl::stopWorkerThread() 112 { 113 if (m_askedToTerminate) 114 return; 115 m_askedToTerminate = true; 116 if (m_workerThread) 117 m_workerThread->stop(); 118 } 119 120 void WebSharedWorkerImpl::initializeLoader(const WebURL& url) 121 { 122 // Create 'shadow page'. This page is never displayed, it is used to proxy the 123 // loading requests from the worker context to the rest of WebKit and Chromium 124 // infrastructure. 125 ASSERT(!m_webView); 126 m_webView = WebView::create(0); 127 m_webView->settings()->setOfflineWebApplicationCacheEnabled(WebRuntimeFeatures::isApplicationCacheEnabled()); 128 // FIXME: Settings information should be passed to the Worker process from Browser process when the worker 129 // is created (similar to RenderThread::OnCreateNewView). 130 m_webView->initializeMainFrame(this); 131 132 WebFrameImpl* webFrame = static_cast<WebFrameImpl*>(m_webView->mainFrame()); 133 134 // Construct substitute data source for the 'shadow page'. We only need it 135 // to have same origin as the worker so the loading checks work correctly. 136 CString content(""); 137 int length = static_cast<int>(content.length()); 138 RefPtr<SharedBuffer> buffer(SharedBuffer::create(content.data(), length)); 139 webFrame->frame()->loader()->load(FrameLoadRequest(0, ResourceRequest(url), SubstituteData(buffer, "text/html", "UTF-8", KURL()))); 140 141 // This document will be used as 'loading context' for the worker. 142 m_loadingDocument = webFrame->frame()->document(); 143 } 144 145 void WebSharedWorkerImpl::didCreateDataSource(WebFrame*, WebDataSource* ds) 146 { 147 // Tell the loader to load the data into the 'shadow page' synchronously, 148 // so we can grab the resulting Document right after load. 149 static_cast<WebDataSourceImpl*>(ds)->setDeferMainResourceDataLoad(false); 150 } 151 152 WebApplicationCacheHost* WebSharedWorkerImpl::createApplicationCacheHost(WebFrame*, WebApplicationCacheHostClient* appcacheHostClient) 153 { 154 if (client()) 155 return client()->createApplicationCacheHost(appcacheHostClient); 156 return 0; 157 } 158 159 // WorkerObjectProxy ----------------------------------------------------------- 160 161 void WebSharedWorkerImpl::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> message, 162 PassOwnPtr<MessagePortChannelArray> channels) 163 { 164 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageTask, AllowCrossThreadAccess(this), 165 message->toWireString(), channels)); 166 } 167 168 void WebSharedWorkerImpl::postMessageTask(ScriptExecutionContext* context, 169 WebSharedWorkerImpl* thisPtr, 170 String message, 171 PassOwnPtr<MessagePortChannelArray> channels) 172 { 173 if (!thisPtr->client()) 174 return; 175 176 WebMessagePortChannelArray webChannels(channels ? channels->size() : 0); 177 for (size_t i = 0; i < webChannels.size(); ++i) { 178 webChannels[i] = (*channels)[i]->channel()->webChannelRelease(); 179 webChannels[i]->setClient(0); 180 } 181 182 thisPtr->client()->postMessageToWorkerObject(message, webChannels); 183 } 184 185 void WebSharedWorkerImpl::postExceptionToWorkerObject(const String& errorMessage, 186 int lineNumber, 187 int columnNumber, 188 const String& sourceURL) 189 { 190 WebWorkerBase::dispatchTaskToMainThread( 191 createCallbackTask(&postExceptionTask, AllowCrossThreadAccess(this), 192 errorMessage, lineNumber, 193 sourceURL)); 194 } 195 196 void WebSharedWorkerImpl::postExceptionTask(ScriptExecutionContext* context, 197 WebSharedWorkerImpl* thisPtr, 198 const String& errorMessage, 199 int lineNumber, const String& sourceURL) 200 { 201 if (!thisPtr->client()) 202 return; 203 204 thisPtr->client()->postExceptionToWorkerObject(errorMessage, 205 lineNumber, 206 sourceURL); 207 } 208 209 void WebSharedWorkerImpl::postConsoleMessageToWorkerObject(MessageSource source, 210 MessageLevel level, 211 const String& message, 212 int lineNumber, 213 const String& sourceURL) 214 { 215 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postConsoleMessageTask, AllowCrossThreadAccess(this), source, level, message, lineNumber, sourceURL)); 216 } 217 218 void WebSharedWorkerImpl::postConsoleMessageTask(ScriptExecutionContext* context, 219 WebSharedWorkerImpl* thisPtr, 220 int source, 221 int level, 222 const String& message, 223 int lineNumber, 224 const String& sourceURL) 225 { 226 if (!thisPtr->client()) 227 return; 228 thisPtr->client()->postConsoleMessageToWorkerObject(source, level, message, lineNumber, sourceURL); 229 } 230 231 void WebSharedWorkerImpl::postMessageToPageInspector(const String& message) 232 { 233 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageToPageInspectorTask, AllowCrossThreadAccess(this), message)); 234 } 235 236 void WebSharedWorkerImpl::postMessageToPageInspectorTask(ScriptExecutionContext*, WebSharedWorkerImpl* thisPtr, const String& message) 237 { 238 if (!thisPtr->client()) 239 return; 240 thisPtr->client()->dispatchDevToolsMessage(message); 241 } 242 243 void WebSharedWorkerImpl::updateInspectorStateCookie(const WTF::String& cookie) 244 { 245 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&updateInspectorStateCookieTask, AllowCrossThreadAccess(this), cookie)); 246 } 247 248 void WebSharedWorkerImpl::updateInspectorStateCookieTask(ScriptExecutionContext*, WebSharedWorkerImpl* thisPtr, const String& cookie) 249 { 250 if (!thisPtr->client()) 251 return; 252 thisPtr->client()->saveDevToolsAgentState(cookie); 253 } 254 255 void WebSharedWorkerImpl::confirmMessageFromWorkerObject(bool hasPendingActivity) 256 { 257 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&confirmMessageTask, AllowCrossThreadAccess(this), 258 hasPendingActivity)); 259 } 260 261 void WebSharedWorkerImpl::confirmMessageTask(ScriptExecutionContext* context, 262 WebSharedWorkerImpl* thisPtr, 263 bool hasPendingActivity) 264 { 265 if (!thisPtr->client()) 266 return; 267 thisPtr->client()->confirmMessageFromWorkerObject(hasPendingActivity); 268 } 269 270 void WebSharedWorkerImpl::reportPendingActivity(bool hasPendingActivity) 271 { 272 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&reportPendingActivityTask, 273 AllowCrossThreadAccess(this), 274 hasPendingActivity)); 275 } 276 277 void WebSharedWorkerImpl::reportPendingActivityTask(ScriptExecutionContext* context, 278 WebSharedWorkerImpl* thisPtr, 279 bool hasPendingActivity) 280 { 281 if (!thisPtr->client()) 282 return; 283 thisPtr->client()->reportPendingActivity(hasPendingActivity); 284 } 285 286 void WebSharedWorkerImpl::workerGlobalScopeClosed() 287 { 288 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerGlobalScopeClosedTask, 289 AllowCrossThreadAccess(this))); 290 } 291 292 void WebSharedWorkerImpl::workerGlobalScopeClosedTask(ScriptExecutionContext* context, 293 WebSharedWorkerImpl* thisPtr) 294 { 295 if (thisPtr->client()) 296 thisPtr->client()->workerContextClosed(); 297 298 thisPtr->stopWorkerThread(); 299 } 300 301 void WebSharedWorkerImpl::workerGlobalScopeDestroyed() 302 { 303 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&workerGlobalScopeDestroyedTask, 304 AllowCrossThreadAccess(this))); 305 } 306 307 void WebSharedWorkerImpl::workerGlobalScopeDestroyedTask(ScriptExecutionContext* context, 308 WebSharedWorkerImpl* thisPtr) 309 { 310 if (thisPtr->client()) 311 thisPtr->client()->workerContextDestroyed(); 312 // The lifetime of this proxy is controlled by the worker context. 313 delete thisPtr; 314 } 315 316 // WorkerLoaderProxy ----------------------------------------------------------- 317 318 void WebSharedWorkerImpl::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task) 319 { 320 ASSERT(m_loadingDocument->isDocument()); 321 m_loadingDocument->postTask(task); 322 } 323 324 bool WebSharedWorkerImpl::postTaskForModeToWorkerGlobalScope( 325 PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode) 326 { 327 m_workerThread->runLoop().postTaskForMode(task, mode); 328 return true; 329 } 330 331 WebWorkerBase* WebSharedWorkerImpl::toWebWorkerBase() 332 { 333 return this; 334 } 335 336 337 bool WebSharedWorkerImpl::isStarted() 338 { 339 // Should not ever be called from the worker thread (this API is only called on WebSharedWorkerProxy on the renderer thread). 340 ASSERT_NOT_REACHED(); 341 return workerThread(); 342 } 343 344 void WebSharedWorkerImpl::connect(WebMessagePortChannel* webChannel, ConnectListener* listener) 345 { 346 // Convert the WebMessagePortChanel to a WebCore::MessagePortChannel. 347 RefPtr<PlatformMessagePortChannel> platform_channel = 348 PlatformMessagePortChannel::create(webChannel); 349 webChannel->setClient(platform_channel.get()); 350 OwnPtr<MessagePortChannel> channel = 351 MessagePortChannel::create(platform_channel); 352 353 workerThread()->runLoop().postTask( 354 createCallbackTask(&connectTask, channel.release())); 355 if (listener) 356 listener->connected(); 357 } 358 359 void WebSharedWorkerImpl::connectTask(ScriptExecutionContext* context, PassOwnPtr<MessagePortChannel> channel) 360 { 361 // Wrap the passed-in channel in a MessagePort, and send it off via a connect event. 362 RefPtr<MessagePort> port = MessagePort::create(*context); 363 port->entangle(channel); 364 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 365 ASSERT_WITH_SECURITY_IMPLICATION(workerGlobalScope->isSharedWorkerGlobalScope()); 366 workerGlobalScope->dispatchEvent(createConnectEvent(port)); 367 } 368 369 void WebSharedWorkerImpl::startWorkerContext(const WebURL& url, const WebString& name, const WebString& userAgent, const WebString& sourceCode, const WebString& contentSecurityPolicy, WebContentSecurityPolicyType policyType, long long) 370 { 371 initializeLoader(url); 372 373 WorkerThreadStartMode startMode = m_pauseWorkerContextOnStart ? PauseWorkerGlobalScopeOnStart : DontPauseWorkerGlobalScopeOnStart; 374 OwnPtr<WorkerClients> workerClients = WorkerClients::create(); 375 provideLocalFileSystemToWorker(workerClients.get(), WorkerFileSystemClient::create()); 376 OwnPtr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(url, userAgent, sourceCode, startMode, contentSecurityPolicy, static_cast<WebCore::ContentSecurityPolicy::HeaderType>(policyType), workerClients.release()); 377 setWorkerThread(SharedWorkerThread::create(name, *this, *this, startupData.release())); 378 379 workerThread()->start(); 380 } 381 382 void WebSharedWorkerImpl::terminateWorkerContext() 383 { 384 stopWorkerThread(); 385 } 386 387 void WebSharedWorkerImpl::clientDestroyed() 388 { 389 m_client = 0; 390 } 391 392 void WebSharedWorkerImpl::pauseWorkerContextOnStart() 393 { 394 m_pauseWorkerContextOnStart = true; 395 } 396 397 static void resumeWorkerContextTask(ScriptExecutionContext* context, bool) 398 { 399 toWorkerGlobalScope(context)->workerInspectorController()->resume(); 400 } 401 402 void WebSharedWorkerImpl::resumeWorkerContext() 403 { 404 m_pauseWorkerContextOnStart = false; 405 if (workerThread()) 406 workerThread()->runLoop().postTaskForMode(createCallbackTask(resumeWorkerContextTask, true), WorkerDebuggerAgent::debuggerTaskMode); 407 } 408 409 static void connectToWorkerContextInspectorTask(ScriptExecutionContext* context, bool) 410 { 411 toWorkerGlobalScope(context)->workerInspectorController()->connectFrontend(); 412 } 413 414 void WebSharedWorkerImpl::attachDevTools() 415 { 416 workerThread()->runLoop().postTaskForMode(createCallbackTask(connectToWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode); 417 } 418 419 static void reconnectToWorkerContextInspectorTask(ScriptExecutionContext* context, const String& savedState) 420 { 421 WorkerInspectorController* ic = toWorkerGlobalScope(context)->workerInspectorController(); 422 ic->restoreInspectorStateFromCookie(savedState); 423 ic->resume(); 424 } 425 426 void WebSharedWorkerImpl::reattachDevTools(const WebString& savedState) 427 { 428 workerThread()->runLoop().postTaskForMode(createCallbackTask(reconnectToWorkerContextInspectorTask, String(savedState)), WorkerDebuggerAgent::debuggerTaskMode); 429 } 430 431 static void disconnectFromWorkerContextInspectorTask(ScriptExecutionContext* context, bool) 432 { 433 toWorkerGlobalScope(context)->workerInspectorController()->disconnectFrontend(); 434 } 435 436 void WebSharedWorkerImpl::detachDevTools() 437 { 438 workerThread()->runLoop().postTaskForMode(createCallbackTask(disconnectFromWorkerContextInspectorTask, true), WorkerDebuggerAgent::debuggerTaskMode); 439 } 440 441 static void dispatchOnInspectorBackendTask(ScriptExecutionContext* context, const String& message) 442 { 443 toWorkerGlobalScope(context)->workerInspectorController()->dispatchMessageFromFrontend(message); 444 } 445 446 void WebSharedWorkerImpl::dispatchDevToolsMessage(const WebString& message) 447 { 448 workerThread()->runLoop().postTaskForMode(createCallbackTask(dispatchOnInspectorBackendTask, String(message)), WorkerDebuggerAgent::debuggerTaskMode); 449 WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(workerThread()); 450 } 451 452 WebSharedWorker* WebSharedWorker::create(WebSharedWorkerClient* client) 453 { 454 return new WebSharedWorkerImpl(client); 455 } 456 457 } // namespace WebKit 458