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