1/* 2 * Copyright (C) 2010, 2011 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "WebProcessProxy.h" 28 29#include "DataReference.h" 30#include "PluginInfoStore.h" 31#include "PluginProcessManager.h" 32#include "TextChecker.h" 33#include "TextCheckerState.h" 34#include "WebBackForwardListItem.h" 35#include "WebContext.h" 36#include "WebNavigationDataStore.h" 37#include "WebPageProxy.h" 38#include "WebProcessMessages.h" 39#include "WebProcessProxyMessages.h" 40#include <WebCore/KURL.h> 41#include <wtf/text/CString.h> 42#include <wtf/text/WTFString.h> 43 44using namespace WebCore; 45using namespace std; 46 47namespace WebKit { 48 49template<typename HashMap> 50static inline bool isGoodKey(const typename HashMap::KeyType& key) 51{ 52 return key != HashTraits<typename HashMap::KeyType>::emptyValue() && !HashTraits<typename HashMap::KeyType>::isDeletedValue(key); 53} 54 55static uint64_t generatePageID() 56{ 57 static uint64_t uniquePageID = 1; 58 return uniquePageID++; 59} 60 61PassRefPtr<WebProcessProxy> WebProcessProxy::create(PassRefPtr<WebContext> context) 62{ 63 return adoptRef(new WebProcessProxy(context)); 64} 65 66WebProcessProxy::WebProcessProxy(PassRefPtr<WebContext> context) 67 : m_responsivenessTimer(this) 68 , m_context(context) 69{ 70 connect(); 71} 72 73WebProcessProxy::~WebProcessProxy() 74{ 75 if (m_connection) 76 m_connection->invalidate(); 77 78 for (size_t i = 0; i < m_pendingMessages.size(); ++i) 79 m_pendingMessages[i].first.releaseArguments(); 80 81 if (m_processLauncher) { 82 m_processLauncher->invalidate(); 83 m_processLauncher = 0; 84 } 85 86 if (m_threadLauncher) { 87 m_threadLauncher->invalidate(); 88 m_threadLauncher = 0; 89 } 90} 91 92void WebProcessProxy::connect() 93{ 94 if (m_context->processModel() == ProcessModelSharedSecondaryThread) { 95 ASSERT(!m_threadLauncher); 96 m_threadLauncher = ThreadLauncher::create(this); 97 } else { 98 ASSERT(!m_processLauncher); 99 100 ProcessLauncher::LaunchOptions launchOptions; 101 launchOptions.processType = ProcessLauncher::WebProcess; 102 103#if PLATFORM(MAC) 104 // We want the web process to match the architecture of the UI process. 105 launchOptions.architecture = ProcessLauncher::LaunchOptions::MatchCurrentArchitecture; 106 launchOptions.executableHeap = false; 107#endif 108 m_processLauncher = ProcessLauncher::create(this, launchOptions); 109 } 110} 111 112void WebProcessProxy::disconnect() 113{ 114 if (m_connection) { 115 m_connection->invalidate(); 116 m_connection = nullptr; 117 } 118 119 m_responsivenessTimer.stop(); 120 121 Vector<RefPtr<WebFrameProxy> > frames; 122 copyValuesToVector(m_frameMap, frames); 123 124 for (size_t i = 0, size = frames.size(); i < size; ++i) 125 frames[i]->disconnect(); 126 m_frameMap.clear(); 127 128 m_context->disconnectProcess(this); 129} 130 131bool WebProcessProxy::sendMessage(CoreIPC::MessageID messageID, PassOwnPtr<CoreIPC::ArgumentEncoder> arguments, unsigned messageSendFlags) 132{ 133 // If we're waiting for the web process to launch, we need to stash away the messages so we can send them once we have 134 // a CoreIPC connection. 135 if (isLaunching()) { 136 m_pendingMessages.append(make_pair(CoreIPC::Connection::OutgoingMessage(messageID, arguments), messageSendFlags)); 137 return true; 138 } 139 140 // If the web process has exited, m_connection will be null here. 141 if (!m_connection) 142 return false; 143 144 return m_connection->sendMessage(messageID, arguments, messageSendFlags); 145} 146 147bool WebProcessProxy::isLaunching() const 148{ 149 if (m_processLauncher) 150 return m_processLauncher->isLaunching(); 151 if (m_threadLauncher) 152 return m_threadLauncher->isLaunching(); 153 154 return false; 155} 156 157void WebProcessProxy::terminate() 158{ 159 if (m_processLauncher) 160 m_processLauncher->terminateProcess(); 161} 162 163WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) const 164{ 165 return m_pageMap.get(pageID); 166} 167 168PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient* pageClient, WebContext* context, WebPageGroup* pageGroup) 169{ 170 ASSERT(context->process() == this); 171 172 unsigned pageID = generatePageID(); 173 RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, this, pageGroup, pageID); 174 m_pageMap.set(pageID, webPage.get()); 175 return webPage.release(); 176} 177 178void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID) 179{ 180 m_pageMap.set(pageID, webPage); 181} 182 183void WebProcessProxy::removeWebPage(uint64_t pageID) 184{ 185 m_pageMap.remove(pageID); 186} 187 188WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const 189{ 190 return m_backForwardListItemMap.get(itemID).get(); 191} 192 193void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item) 194{ 195 // This item was just created by the UIProcess and is being added to the map for the first time 196 // so we should not already have an item for this ID. 197 ASSERT(!m_backForwardListItemMap.contains(item->itemID())); 198 199 m_backForwardListItemMap.set(item->itemID(), item); 200} 201 202void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const CoreIPC::DataReference& backForwardData) 203{ 204 std::pair<WebBackForwardListItemMap::iterator, bool> result = m_backForwardListItemMap.add(itemID, 0); 205 if (result.second) { 206 // New item. 207 result.first->second = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID); 208 return; 209 } 210 211 // Update existing item. 212 result.first->second->setOriginalURL(originalURL); 213 result.first->second->setURL(url); 214 result.first->second->setTitle(title); 215 result.first->second->setBackForwardData(backForwardData.data(), backForwardData.size()); 216} 217 218#if ENABLE(PLUGIN_PROCESS) 219void WebProcessProxy::getPluginProcessConnection(const String& pluginPath, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply) 220{ 221 PluginProcessManager::shared().getPluginProcessConnection(context()->pluginInfoStore(), pluginPath, reply); 222} 223 224void WebProcessProxy::pluginSyncMessageSendTimedOut(const String& pluginPath) 225{ 226 PluginProcessManager::shared().pluginSyncMessageSendTimedOut(pluginPath); 227} 228#endif 229 230void WebProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments) 231{ 232 if (messageID.is<CoreIPC::MessageClassWebProcessProxy>()) { 233 didReceiveWebProcessProxyMessage(connection, messageID, arguments); 234 return; 235 } 236 237 if (messageID.is<CoreIPC::MessageClassWebContext>() 238 || messageID.is<CoreIPC::MessageClassWebContextLegacy>() 239 || messageID.is<CoreIPC::MessageClassDownloadProxy>() 240 || messageID.is<CoreIPC::MessageClassWebApplicationCacheManagerProxy>() 241 || messageID.is<CoreIPC::MessageClassWebCookieManagerProxy>() 242 || messageID.is<CoreIPC::MessageClassWebDatabaseManagerProxy>() 243 || messageID.is<CoreIPC::MessageClassWebGeolocationManagerProxy>() 244 || messageID.is<CoreIPC::MessageClassWebIconDatabase>() 245 || messageID.is<CoreIPC::MessageClassWebKeyValueStorageManagerProxy>() 246 || messageID.is<CoreIPC::MessageClassWebMediaCacheManagerProxy>() 247 || messageID.is<CoreIPC::MessageClassWebResourceCacheManagerProxy>()) { 248 m_context->didReceiveMessage(connection, messageID, arguments); 249 return; 250 } 251 252 uint64_t pageID = arguments->destinationID(); 253 if (!pageID) 254 return; 255 256 WebPageProxy* pageProxy = webPage(pageID); 257 if (!pageProxy) 258 return; 259 260 pageProxy->didReceiveMessage(connection, messageID, arguments); 261} 262 263CoreIPC::SyncReplyMode WebProcessProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply) 264{ 265 if (messageID.is<CoreIPC::MessageClassWebProcessProxy>()) 266 return didReceiveSyncWebProcessProxyMessage(connection, messageID, arguments, reply); 267 268 if (messageID.is<CoreIPC::MessageClassWebContext>() || messageID.is<CoreIPC::MessageClassWebContextLegacy>() 269 || messageID.is<CoreIPC::MessageClassDownloadProxy>() || messageID.is<CoreIPC::MessageClassWebIconDatabase>()) 270 return m_context->didReceiveSyncMessage(connection, messageID, arguments, reply); 271 272 uint64_t pageID = arguments->destinationID(); 273 if (!pageID) 274 return CoreIPC::AutomaticReply; 275 276 WebPageProxy* pageProxy = webPage(pageID); 277 if (!pageProxy) 278 return CoreIPC::AutomaticReply; 279 280 pageProxy->didReceiveSyncMessage(connection, messageID, arguments, reply); 281 return CoreIPC::AutomaticReply; 282} 283 284void WebProcessProxy::didClose(CoreIPC::Connection*) 285{ 286 // Protect ourselves, as the call to disconnect() below may otherwise cause us 287 // to be deleted before we can finish our work. 288 RefPtr<WebProcessProxy> protect(this); 289 290 Vector<RefPtr<WebPageProxy> > pages; 291 copyValuesToVector(m_pageMap, pages); 292 293 disconnect(); 294 295 for (size_t i = 0, size = pages.size(); i < size; ++i) 296 pages[i]->processDidCrash(); 297} 298 299void WebProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID) 300{ 301 // We received an invalid message from the web process, invalidate our connection and kill it. 302 m_connection->invalidate(); 303 304 terminate(); 305} 306 307void WebProcessProxy::syncMessageSendTimedOut(CoreIPC::Connection*) 308{ 309} 310 311void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*) 312{ 313 Vector<RefPtr<WebPageProxy> > pages; 314 copyValuesToVector(m_pageMap, pages); 315 for (size_t i = 0, size = pages.size(); i < size; ++i) 316 pages[i]->processDidBecomeUnresponsive(); 317} 318 319void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*) 320{ 321 Vector<RefPtr<WebPageProxy> > pages; 322 copyValuesToVector(m_pageMap, pages); 323 for (size_t i = 0, size = pages.size(); i < size; ++i) 324 pages[i]->processDidBecomeResponsive(); 325} 326 327void WebProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connection::Identifier connectionIdentifier) 328{ 329 didFinishLaunching(connectionIdentifier); 330} 331 332void WebProcessProxy::didFinishLaunching(ThreadLauncher*, CoreIPC::Connection::Identifier connectionIdentifier) 333{ 334 didFinishLaunching(connectionIdentifier); 335} 336 337void WebProcessProxy::didFinishLaunching(CoreIPC::Connection::Identifier connectionIdentifier) 338{ 339 ASSERT(!m_connection); 340 341 m_connection = CoreIPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main()); 342#if PLATFORM(MAC) 343 m_connection->setShouldCloseConnectionOnMachExceptions(); 344#elif PLATFORM(QT) || PLATFORM(GTK) 345 m_connection->setShouldCloseConnectionOnProcessTermination(processIdentifier()); 346#endif 347 348 m_connection->open(); 349 350 for (size_t i = 0; i < m_pendingMessages.size(); ++i) { 351 CoreIPC::Connection::OutgoingMessage& outgoingMessage = m_pendingMessages[i].first; 352 unsigned messageSendFlags = m_pendingMessages[i].second; 353 m_connection->sendMessage(outgoingMessage.messageID(), adoptPtr(outgoingMessage.arguments()), messageSendFlags); 354 } 355 356 m_pendingMessages.clear(); 357 358 // Tell the context that we finished launching. 359 m_context->processDidFinishLaunching(this); 360} 361 362WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const 363{ 364 return isGoodKey<WebFrameProxyMap>(frameID) ? m_frameMap.get(frameID).get() : 0; 365} 366 367bool WebProcessProxy::canCreateFrame(uint64_t frameID) const 368{ 369 return isGoodKey<WebFrameProxyMap>(frameID) && !m_frameMap.contains(frameID); 370} 371 372void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy) 373{ 374 ASSERT(canCreateFrame(frameID)); 375 m_frameMap.set(frameID, frameProxy); 376} 377 378void WebProcessProxy::didDestroyFrame(uint64_t frameID) 379{ 380 // If the page is closed before it has had the chance to send the DidCreateMainFrame message 381 // back to the UIProcess, then the frameDestroyed message will still be received because it 382 // gets sent directly to the WebProcessProxy. 383 ASSERT(isGoodKey<WebFrameProxyMap>(frameID)); 384 m_frameMap.remove(frameID); 385} 386 387void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page) 388{ 389 Vector<RefPtr<WebFrameProxy> > frames; 390 copyValuesToVector(m_frameMap, frames); 391 for (size_t i = 0, size = frames.size(); i < size; ++i) { 392 if (frames[i]->page() == page) 393 frames[i]->disconnect(); 394 } 395} 396 397size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const 398{ 399 size_t result = 0; 400 for (HashMap<uint64_t, RefPtr<WebFrameProxy> >::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) { 401 if (iter->second->page() == page) 402 ++result; 403 } 404 return result; 405} 406 407void WebProcessProxy::shouldTerminate(bool& shouldTerminate) 408{ 409 if (!m_pageMap.isEmpty() || !m_context->shouldTerminate(this)) { 410 shouldTerminate = false; 411 return; 412 } 413 414 shouldTerminate = true; 415 416 // We know that the web process is going to terminate so disconnect it from the context. 417 disconnect(); 418} 419 420void WebProcessProxy::updateTextCheckerState() 421{ 422 if (!isValid()) 423 return; 424 425 send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0); 426} 427 428} // namespace WebKit 429