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 "DownloadProxyMap.h"
31#include "PluginInfoStore.h"
32#include "PluginProcessManager.h"
33#include "TextChecker.h"
34#include "TextCheckerState.h"
35#include "WebBackForwardListItem.h"
36#include "WebContext.h"
37#include "WebNavigationDataStore.h"
38#include "WebNotificationManagerProxy.h"
39#include "WebPageProxy.h"
40#include "WebPluginSiteDataManager.h"
41#include "WebProcessMessages.h"
42#include "WebProcessProxyMessages.h"
43#include <WebCore/KURL.h>
44#include <WebCore/SuddenTermination.h>
45#include <stdio.h>
46#include <wtf/MainThread.h>
47#include <wtf/text/CString.h>
48#include <wtf/text/WTFString.h>
49
50#if PLATFORM(MAC)
51#include "SimplePDFPlugin.h"
52#if ENABLE(PDFKIT_PLUGIN)
53#include "PDFPlugin.h"
54#endif
55#endif
56
57#if ENABLE(CUSTOM_PROTOCOLS)
58#include "CustomProtocolManagerProxyMessages.h"
59#endif
60
61#if USE(SECURITY_FRAMEWORK)
62#include "SecItemShimProxy.h"
63#endif
64
65using namespace WebCore;
66
67#define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, connection())
68#define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(checkURLReceivedFromWebProcess(url), connection())
69
70namespace WebKit {
71
72static uint64_t generatePageID()
73{
74    static uint64_t uniquePageID;
75    return ++uniquePageID;
76}
77
78static WebProcessProxy::WebPageProxyMap& globalPageMap()
79{
80    ASSERT(isMainThread());
81    DEFINE_STATIC_LOCAL(WebProcessProxy::WebPageProxyMap, pageMap, ());
82    return pageMap;
83}
84
85PassRefPtr<WebProcessProxy> WebProcessProxy::create(PassRefPtr<WebContext> context)
86{
87    return adoptRef(new WebProcessProxy(context));
88}
89
90WebProcessProxy::WebProcessProxy(PassRefPtr<WebContext> context)
91    : m_responsivenessTimer(this)
92    , m_context(context)
93    , m_mayHaveUniversalFileReadSandboxExtension(false)
94#if ENABLE(CUSTOM_PROTOCOLS)
95    , m_customProtocolManagerProxy(this)
96#endif
97#if PLATFORM(MAC)
98    , m_processSuppressionEnabled(false)
99#endif
100{
101    connect();
102}
103
104WebProcessProxy::~WebProcessProxy()
105{
106    if (m_webConnection)
107        m_webConnection->invalidate();
108}
109
110void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
111{
112    launchOptions.processType = ProcessLauncher::WebProcess;
113    platformGetLaunchOptions(launchOptions);
114}
115
116void WebProcessProxy::connectionWillOpen(CoreIPC::Connection* connection)
117{
118    ASSERT(this->connection() == connection);
119
120#if USE(SECURITY_FRAMEWORK)
121    SecItemShimProxy::shared().initializeConnection(connection);
122#endif
123
124    for (WebPageProxyMap::iterator it = m_pageMap.begin(), end = m_pageMap.end(); it != end; ++it)
125        it->value->connectionWillOpen(connection);
126
127    m_context->processWillOpenConnection(this);
128}
129
130void WebProcessProxy::connectionWillClose(CoreIPC::Connection* connection)
131{
132    ASSERT(this->connection() == connection);
133
134    for (WebPageProxyMap::iterator it = m_pageMap.begin(), end = m_pageMap.end(); it != end; ++it)
135        it->value->connectionWillClose(connection);
136
137    m_context->processWillCloseConnection(this);
138}
139
140void WebProcessProxy::disconnect()
141{
142    clearConnection();
143
144    if (m_webConnection) {
145        m_webConnection->invalidate();
146        m_webConnection = nullptr;
147    }
148
149    m_responsivenessTimer.stop();
150
151    Vector<RefPtr<WebFrameProxy>> frames;
152    copyValuesToVector(m_frameMap, frames);
153
154    for (size_t i = 0, size = frames.size(); i < size; ++i)
155        frames[i]->disconnect();
156    m_frameMap.clear();
157
158    if (m_downloadProxyMap)
159        m_downloadProxyMap->processDidClose();
160
161    m_context->disconnectProcess(this);
162}
163
164WebPageProxy* WebProcessProxy::webPage(uint64_t pageID)
165{
166    return globalPageMap().get(pageID);
167}
168
169PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient* pageClient, WebContext*, WebPageGroup* pageGroup)
170{
171    uint64_t pageID = generatePageID();
172    RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, this, pageGroup, pageID);
173    m_pageMap.set(pageID, webPage.get());
174    globalPageMap().set(pageID, webPage.get());
175#if PLATFORM(MAC)
176    if (pageIsProcessSuppressible(webPage.get()))
177        m_processSuppressiblePages.add(pageID);
178    updateProcessSuppressionState();
179#endif
180    return webPage.release();
181}
182
183void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID)
184{
185    m_pageMap.set(pageID, webPage);
186    globalPageMap().set(pageID, webPage);
187#if PLATFORM(MAC)
188    if (pageIsProcessSuppressible(webPage))
189        m_processSuppressiblePages.add(pageID);
190    updateProcessSuppressionState();
191#endif
192}
193
194void WebProcessProxy::removeWebPage(uint64_t pageID)
195{
196    m_pageMap.remove(pageID);
197    globalPageMap().remove(pageID);
198#if PLATFORM(MAC)
199    m_processSuppressiblePages.remove(pageID);
200    updateProcessSuppressionState();
201#endif
202
203    // If this was the last WebPage open in that web process, and we have no other reason to keep it alive, let it go.
204    // We only allow this when using a network process, as otherwise the WebProcess needs to preserve its session state.
205    if (m_context->usesNetworkProcess() && canTerminateChildProcess()) {
206        abortProcessLaunchIfNeeded();
207        disconnect();
208    }
209}
210
211Vector<WebPageProxy*> WebProcessProxy::pages() const
212{
213    Vector<WebPageProxy*> result;
214    copyValuesToVector(m_pageMap, result);
215    return result;
216}
217
218WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
219{
220    return m_backForwardListItemMap.get(itemID);
221}
222
223void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item)
224{
225    // This item was just created by the UIProcess and is being added to the map for the first time
226    // so we should not already have an item for this ID.
227    ASSERT(!m_backForwardListItemMap.contains(item->itemID()));
228
229    m_backForwardListItemMap.set(item->itemID(), item);
230}
231
232void WebProcessProxy::assumeReadAccessToBaseURL(const String& urlString)
233{
234    KURL url(KURL(), urlString);
235    if (!url.isLocalFile())
236        return;
237
238    // There's a chance that urlString does not point to a directory.
239    // Get url's base URL to add to m_localPathsWithAssumedReadAccess.
240    KURL baseURL(KURL(), url.baseAsString());
241
242    // Client loads an alternate string. This doesn't grant universal file read, but the web process is assumed
243    // to have read access to this directory already.
244    m_localPathsWithAssumedReadAccess.add(baseURL.fileSystemPath());
245}
246
247bool WebProcessProxy::hasAssumedReadAccessToURL(const KURL& url) const
248{
249    if (!url.isLocalFile())
250        return false;
251
252    String path = url.fileSystemPath();
253    for (const String& assumedAccessPath : m_localPathsWithAssumedReadAccess) {
254        // There are no ".." components, because URL removes those.
255        if (path.startsWith(assumedAccessPath))
256            return true;
257    }
258
259    return false;
260}
261
262bool WebProcessProxy::checkURLReceivedFromWebProcess(const String& urlString)
263{
264    return checkURLReceivedFromWebProcess(KURL(KURL(), urlString));
265}
266
267bool WebProcessProxy::checkURLReceivedFromWebProcess(const KURL& url)
268{
269    // FIXME: Consider checking that the URL is valid. Currently, WebProcess sends invalid URLs in many cases, but it probably doesn't have good reasons to do that.
270
271    // Any other non-file URL is OK.
272    if (!url.isLocalFile())
273        return true;
274
275    // Any file URL is also OK if we've loaded a file URL through API before, granting universal read access.
276    if (m_mayHaveUniversalFileReadSandboxExtension)
277        return true;
278
279    // If we loaded a string with a file base URL before, loading resources from that subdirectory is fine.
280    if (hasAssumedReadAccessToURL(url))
281        return true;
282
283    // Items in back/forward list have been already checked.
284    // One case where we don't have sandbox extensions for file URLs in b/f list is if the list has been reinstated after a crash or a browser restart.
285    String path = url.fileSystemPath();
286    for (WebBackForwardListItemMap::iterator iter = m_backForwardListItemMap.begin(), end = m_backForwardListItemMap.end(); iter != end; ++iter) {
287        if (KURL(KURL(), iter->value->url()).fileSystemPath() == path)
288            return true;
289        if (KURL(KURL(), iter->value->originalURL()).fileSystemPath() == path)
290            return true;
291    }
292
293    // A Web process that was never asked to load a file URL should not ever ask us to do anything with a file URL.
294    WTFLogAlways("Received an unexpected URL from the web process: '%s'\n", url.string().utf8().data());
295    return false;
296}
297
298#if !PLATFORM(MAC)
299bool WebProcessProxy::fullKeyboardAccessEnabled()
300{
301    return false;
302}
303#endif
304
305void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const CoreIPC::DataReference& backForwardData)
306{
307    MESSAGE_CHECK_URL(originalURL);
308    MESSAGE_CHECK_URL(url);
309
310    WebBackForwardListItemMap::AddResult result = m_backForwardListItemMap.add(itemID, 0);
311    if (result.isNewEntry) {
312        result.iterator->value = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID);
313        return;
314    }
315
316    // Update existing item.
317    result.iterator->value->setOriginalURL(originalURL);
318    result.iterator->value->setURL(url);
319    result.iterator->value->setTitle(title);
320    result.iterator->value->setBackForwardData(backForwardData.data(), backForwardData.size());
321}
322
323#if ENABLE(NETSCAPE_PLUGIN_API)
324void WebProcessProxy::getPlugins(bool refresh, Vector<PluginInfo>& plugins, Vector<PluginInfo>& applicationPlugins)
325{
326    if (refresh)
327        m_context->pluginInfoStore().refresh();
328
329    Vector<PluginModuleInfo> pluginModules = m_context->pluginInfoStore().plugins();
330    for (size_t i = 0; i < pluginModules.size(); ++i)
331        plugins.append(pluginModules[i].info);
332
333#if PLATFORM(MAC)
334    // Add built-in PDF last, so that it's not used when a real plug-in is installed.
335    if (!m_context->omitPDFSupport()) {
336#if ENABLE(PDFKIT_PLUGIN)
337        // Add built-in PDF last, so that it's not used when a real plug-in is installed.
338        plugins.append(PDFPlugin::pluginInfo());
339        applicationPlugins.append(PDFPlugin::pluginInfo());
340#endif
341        plugins.append(SimplePDFPlugin::pluginInfo());
342        applicationPlugins.append(SimplePDFPlugin::pluginInfo());
343    }
344#endif
345}
346#endif // ENABLE(NETSCAPE_PLUGIN_API)
347
348#if ENABLE(PLUGIN_PROCESS)
349void WebProcessProxy::getPluginProcessConnection(uint64_t pluginProcessToken, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
350{
351    PluginProcessManager::shared().getPluginProcessConnection(pluginProcessToken, reply);
352}
353
354#elif ENABLE(NETSCAPE_PLUGIN_API)
355
356void WebProcessProxy::didGetSitesWithPluginData(const Vector<String>& sites, uint64_t callbackID)
357{
358    m_context->pluginSiteDataManager()->didGetSitesWithData(sites, callbackID);
359}
360
361void WebProcessProxy::didClearPluginSiteData(uint64_t callbackID)
362{
363    m_context->pluginSiteDataManager()->didClearSiteData(callbackID);
364}
365
366#endif
367
368#if ENABLE(SHARED_WORKER_PROCESS)
369void WebProcessProxy::getSharedWorkerProcessConnection(const String& /* url */, const String& /* name */, PassRefPtr<Messages::WebProcessProxy::GetSharedWorkerProcessConnection::DelayedReply>)
370{
371    // FIXME: Implement
372}
373#endif // ENABLE(SHARED_WORKER_PROCESS)
374
375#if ENABLE(NETWORK_PROCESS)
376void WebProcessProxy::getNetworkProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply> reply)
377{
378    m_context->getNetworkProcessConnection(reply);
379}
380#endif // ENABLE(NETWORK_PROCESS)
381
382void WebProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder)
383{
384    if (dispatchMessage(connection, decoder))
385        return;
386
387    if (m_context->dispatchMessage(connection, decoder))
388        return;
389
390    if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
391        didReceiveWebProcessProxyMessage(connection, decoder);
392        return;
393    }
394
395    // FIXME: Add unhandled message logging.
396}
397
398void WebProcessProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder, OwnPtr<CoreIPC::MessageEncoder>& replyEncoder)
399{
400    if (dispatchSyncMessage(connection, decoder, replyEncoder))
401        return;
402
403    if (m_context->dispatchSyncMessage(connection, decoder, replyEncoder))
404        return;
405
406    if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
407        didReceiveSyncWebProcessProxyMessage(connection, decoder, replyEncoder);
408        return;
409    }
410
411    // FIXME: Add unhandled message logging.
412}
413
414void WebProcessProxy::didClose(CoreIPC::Connection*)
415{
416    // Protect ourselves, as the call to disconnect() below may otherwise cause us
417    // to be deleted before we can finish our work.
418    RefPtr<WebProcessProxy> protect(this);
419
420    webConnection()->didClose();
421
422    Vector<RefPtr<WebPageProxy>> pages;
423    copyValuesToVector(m_pageMap, pages);
424
425    disconnect();
426
427    for (size_t i = 0, size = pages.size(); i < size; ++i)
428        pages[i]->processDidCrash();
429
430}
431
432void WebProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection* connection, CoreIPC::StringReference messageReceiverName, CoreIPC::StringReference messageName)
433{
434    WTFLogAlways("Received an invalid message \"%s.%s\" from the web process.\n", messageReceiverName.toString().data(), messageName.toString().data());
435
436    WebContext::didReceiveInvalidMessage(messageReceiverName, messageName);
437
438    // Terminate the WebProcess.
439    terminate();
440
441    // Since we've invalidated the connection we'll never get a CoreIPC::Connection::Client::didClose
442    // callback so we'll explicitly call it here instead.
443    didClose(connection);
444}
445
446void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*)
447{
448    Vector<RefPtr<WebPageProxy>> pages;
449    copyValuesToVector(m_pageMap, pages);
450    for (size_t i = 0, size = pages.size(); i < size; ++i)
451        pages[i]->processDidBecomeUnresponsive();
452}
453
454void WebProcessProxy::interactionOccurredWhileUnresponsive(ResponsivenessTimer*)
455{
456    Vector<RefPtr<WebPageProxy>> pages;
457    copyValuesToVector(m_pageMap, pages);
458    for (size_t i = 0, size = pages.size(); i < size; ++i)
459        pages[i]->interactionOccurredWhileProcessUnresponsive();
460}
461
462void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*)
463{
464    Vector<RefPtr<WebPageProxy>> pages;
465    copyValuesToVector(m_pageMap, pages);
466    for (size_t i = 0, size = pages.size(); i < size; ++i)
467        pages[i]->processDidBecomeResponsive();
468}
469
470void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, CoreIPC::Connection::Identifier connectionIdentifier)
471{
472    ChildProcessProxy::didFinishLaunching(launcher, connectionIdentifier);
473
474    m_webConnection = WebConnectionToWebProcess::create(this);
475
476    m_context->processDidFinishLaunching(this);
477
478#if PLATFORM(MAC)
479    updateProcessSuppressionState();
480#endif
481}
482
483WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
484{
485    if (!WebFrameProxyMap::isValidKey(frameID))
486        return 0;
487
488    return m_frameMap.get(frameID);
489}
490
491bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
492{
493    return WebFrameProxyMap::isValidKey(frameID) && !m_frameMap.contains(frameID);
494}
495
496void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
497{
498    ASSERT(canCreateFrame(frameID));
499    m_frameMap.set(frameID, frameProxy);
500}
501
502void WebProcessProxy::didDestroyFrame(uint64_t frameID)
503{
504    // If the page is closed before it has had the chance to send the DidCreateMainFrame message
505    // back to the UIProcess, then the frameDestroyed message will still be received because it
506    // gets sent directly to the WebProcessProxy.
507    ASSERT(WebFrameProxyMap::isValidKey(frameID));
508    m_frameMap.remove(frameID);
509}
510
511void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
512{
513    Vector<RefPtr<WebFrameProxy>> frames;
514    copyValuesToVector(m_frameMap, frames);
515    for (size_t i = 0, size = frames.size(); i < size; ++i) {
516        if (frames[i]->page() == page)
517            frames[i]->disconnect();
518    }
519}
520
521size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
522{
523    size_t result = 0;
524    for (HashMap<uint64_t, RefPtr<WebFrameProxy>>::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) {
525        if (iter->value->page() == page)
526            ++result;
527    }
528    return result;
529}
530
531bool WebProcessProxy::canTerminateChildProcess()
532{
533    if (!m_pageMap.isEmpty())
534        return false;
535
536    if (m_downloadProxyMap && !m_downloadProxyMap->isEmpty())
537        return false;
538
539    if (!m_context->shouldTerminate(this))
540        return false;
541
542    return true;
543}
544
545void WebProcessProxy::shouldTerminate(bool& shouldTerminate)
546{
547    shouldTerminate = canTerminateChildProcess();
548    if (shouldTerminate) {
549        // We know that the web process is going to terminate so disconnect it from the context.
550        disconnect();
551    }
552}
553
554void WebProcessProxy::updateTextCheckerState()
555{
556    if (canSendMessage())
557        send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
558}
559
560DownloadProxy* WebProcessProxy::createDownloadProxy()
561{
562#if ENABLE(NETWORK_PROCESS)
563    ASSERT(!m_context->usesNetworkProcess());
564#endif
565
566    if (!m_downloadProxyMap)
567        m_downloadProxyMap = adoptPtr(new DownloadProxyMap(this));
568
569    return m_downloadProxyMap->createDownloadProxy(m_context.get());
570}
571
572void WebProcessProxy::didNavigateWithNavigationData(uint64_t pageID, const WebNavigationDataStore& store, uint64_t frameID)
573{
574    WebPageProxy* page = webPage(pageID);
575    if (!page)
576        return;
577
578    WebFrameProxy* frame = webFrame(frameID);
579    MESSAGE_CHECK(frame);
580    MESSAGE_CHECK(frame->page() == page);
581
582    m_context->historyClient().didNavigateWithNavigationData(m_context.get(), page, store, frame);
583}
584
585void WebProcessProxy::didPerformClientRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
586{
587    WebPageProxy* page = webPage(pageID);
588    if (!page)
589        return;
590
591    if (sourceURLString.isEmpty() || destinationURLString.isEmpty())
592        return;
593
594    WebFrameProxy* frame = webFrame(frameID);
595    MESSAGE_CHECK(frame);
596    MESSAGE_CHECK(frame->page() == page);
597    MESSAGE_CHECK_URL(sourceURLString);
598    MESSAGE_CHECK_URL(destinationURLString);
599
600    m_context->historyClient().didPerformClientRedirect(m_context.get(), page, sourceURLString, destinationURLString, frame);
601}
602
603void WebProcessProxy::didPerformServerRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
604{
605    WebPageProxy* page = webPage(pageID);
606    if (!page)
607        return;
608
609    if (sourceURLString.isEmpty() || destinationURLString.isEmpty())
610        return;
611
612    WebFrameProxy* frame = webFrame(frameID);
613    MESSAGE_CHECK(frame);
614    MESSAGE_CHECK(frame->page() == page);
615    MESSAGE_CHECK_URL(sourceURLString);
616    MESSAGE_CHECK_URL(destinationURLString);
617
618    m_context->historyClient().didPerformServerRedirect(m_context.get(), page, sourceURLString, destinationURLString, frame);
619}
620
621void WebProcessProxy::didUpdateHistoryTitle(uint64_t pageID, const String& title, const String& url, uint64_t frameID)
622{
623    WebPageProxy* page = webPage(pageID);
624    if (!page)
625        return;
626
627    WebFrameProxy* frame = webFrame(frameID);
628    MESSAGE_CHECK(frame);
629    MESSAGE_CHECK(frame->page() == page);
630    MESSAGE_CHECK_URL(url);
631
632    m_context->historyClient().didUpdateHistoryTitle(m_context.get(), page, title, url, frame);
633}
634
635void WebProcessProxy::pageVisibilityChanged(WebKit::WebPageProxy *page)
636{
637#if PLATFORM(MAC)
638    if (pageIsProcessSuppressible(page))
639        m_processSuppressiblePages.add(page->pageID());
640    else
641        m_processSuppressiblePages.remove(page->pageID());
642    updateProcessSuppressionState();
643#else
644    UNUSED_PARAM(page);
645#endif
646}
647
648void WebProcessProxy::pagePreferencesChanged(WebKit::WebPageProxy *page)
649{
650#if PLATFORM(MAC)
651    if (pageIsProcessSuppressible(page))
652        m_processSuppressiblePages.add(page->pageID());
653    else
654        m_processSuppressiblePages.remove(page->pageID());
655    updateProcessSuppressionState();
656#else
657    UNUSED_PARAM(page);
658#endif
659}
660
661void WebProcessProxy::didSaveToPageCache()
662{
663    m_context->processDidCachePage(this);
664}
665
666void WebProcessProxy::releasePageCache()
667{
668    if (canSendMessage())
669        send(Messages::WebProcess::ReleasePageCache(), 0);
670}
671
672
673void WebProcessProxy::requestTermination()
674{
675    if (!isValid())
676        return;
677
678    ChildProcessProxy::terminate();
679
680    if (webConnection())
681        webConnection()->didClose();
682
683    disconnect();
684}
685
686
687void WebProcessProxy::enableSuddenTermination()
688{
689    if (!isValid())
690        return;
691
692    WebCore::enableSuddenTermination();
693}
694
695void WebProcessProxy::disableSuddenTermination()
696{
697    if (!isValid())
698        return;
699
700    WebCore::disableSuddenTermination();
701}
702
703} // namespace WebKit
704