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