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