1/*
2 * Copyright (C) 2010-2017 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 "APIPageGroupHandle.h"
31#include "APIPageHandle.h"
32#include "DataReference.h"
33#include "DownloadProxyMap.h"
34#include "Logging.h"
35#include "PluginInfoStore.h"
36#include "PluginProcessManager.h"
37#include "ProvisionalPageProxy.h"
38#include "TextChecker.h"
39#include "TextCheckerState.h"
40#include "UIMessagePortChannelProvider.h"
41#include "UserData.h"
42#include "WebBackForwardListItem.h"
43#include "WebInspectorUtilities.h"
44#include "WebNavigationDataStore.h"
45#include "WebNotificationManagerProxy.h"
46#include "WebPageGroup.h"
47#include "WebPageProxy.h"
48#include "WebPasteboardProxy.h"
49#include "WebProcessCache.h"
50#include "WebProcessMessages.h"
51#include "WebProcessPool.h"
52#include "WebProcessProxyMessages.h"
53#include "WebUserContentControllerProxy.h"
54#include "WebsiteData.h"
55#include "WebsiteDataFetchOption.h"
56#include <WebCore/DiagnosticLoggingKeys.h>
57#include <WebCore/PrewarmInformation.h>
58#include <WebCore/PublicSuffix.h>
59#include <WebCore/SuddenTermination.h>
60#include <stdio.h>
61#include <wtf/Algorithms.h>
62#include <wtf/NeverDestroyed.h>
63#include <wtf/RunLoop.h>
64#include <wtf/URL.h>
65#include <wtf/text/CString.h>
66#include <wtf/text/StringBuilder.h>
67#include <wtf/text/WTFString.h>
68
69#if PLATFORM(COCOA)
70#include "ObjCObjectGraph.h"
71#include "PDFPlugin.h"
72#include "UserMediaCaptureManagerProxy.h"
73#include "VersionChecks.h"
74#endif
75
76#if PLATFORM(MAC)
77#include "HighPerformanceGPUManager.h"
78#endif
79
80#if ENABLE(SEC_ITEM_SHIM)
81#include "SecItemShimProxy.h"
82#endif
83
84#define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, connection())
85#define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(checkURLReceivedFromWebProcess(url), connection())
86
87namespace WebKit {
88using namespace WebCore;
89
90static bool isMainThreadOrCheckDisabled()
91{
92#if PLATFORM(IOS_FAMILY)
93 return LIKELY(RunLoop::isMain()) || !linkedOnOrAfter(SDKVersion::FirstWithMainThreadReleaseAssertionInWebPageProxy);
94#elif PLATFORM(MAC)
95 return LIKELY(RunLoop::isMain()) || !linkedOnOrAfter(SDKVersion::FirstWithMainThreadReleaseAssertionInWebPageProxy);
96#else
97 return RunLoop::isMain();
98#endif
99}
100
101static HashMap<ProcessIdentifier, WebProcessProxy*>& allProcesses()
102{
103 ASSERT(isMainThreadOrCheckDisabled());
104 static NeverDestroyed<HashMap<ProcessIdentifier, WebProcessProxy*>> map;
105 return map;
106}
107
108WebProcessProxy* WebProcessProxy::processForIdentifier(ProcessIdentifier identifier)
109{
110 return allProcesses().get(identifier);
111}
112
113uint64_t WebProcessProxy::generatePageID()
114{
115 static uint64_t uniquePageID;
116 return ++uniquePageID;
117}
118
119static WebProcessProxy::WebPageProxyMap& globalPageMap()
120{
121 ASSERT(isMainThreadOrCheckDisabled());
122 static NeverDestroyed<WebProcessProxy::WebPageProxyMap> pageMap;
123 return pageMap;
124}
125
126void WebProcessProxy::forWebPagesWithOrigin(PAL::SessionID sessionID, const SecurityOriginData& origin, const Function<void(WebPageProxy&)>& callback)
127{
128 for (auto* page : globalPageMap().values()) {
129 if (page->sessionID() != sessionID || SecurityOriginData::fromURL(URL { { }, page->currentURL() }) != origin)
130 continue;
131 callback(*page);
132 }
133}
134
135Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool, WebsiteDataStore* websiteDataStore, IsPrewarmed isPrewarmed, ShouldLaunchProcess shouldLaunchProcess)
136{
137 auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore, isPrewarmed));
138 if (shouldLaunchProcess == ShouldLaunchProcess::Yes)
139 proxy->connect();
140 return proxy;
141}
142
143WebProcessProxy::WebProcessProxy(WebProcessPool& processPool, WebsiteDataStore* websiteDataStore, IsPrewarmed isPrewarmed)
144 : AuxiliaryProcessProxy(processPool.alwaysRunsAtBackgroundPriority())
145 , m_responsivenessTimer(*this)
146 , m_backgroundResponsivenessTimer(*this)
147 , m_processPool(processPool, isPrewarmed == IsPrewarmed::Yes ? IsWeak::Yes : IsWeak::No)
148 , m_mayHaveUniversalFileReadSandboxExtension(false)
149 , m_numberOfTimesSuddenTerminationWasDisabled(0)
150 , m_throttler(*this, processPool.shouldTakeUIBackgroundAssertion())
151 , m_isResponsive(NoOrMaybe::Maybe)
152 , m_visiblePageCounter([this](RefCounterEvent) { updateBackgroundResponsivenessTimer(); })
153 , m_websiteDataStore(websiteDataStore)
154#if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
155 , m_userMediaCaptureManagerProxy(std::make_unique<UserMediaCaptureManagerProxy>(*this))
156#endif
157 , m_isPrewarmed(isPrewarmed == IsPrewarmed::Yes)
158{
159 RELEASE_ASSERT(isMainThreadOrCheckDisabled());
160
161 auto result = allProcesses().add(coreProcessIdentifier(), this);
162 ASSERT_UNUSED(result, result.isNewEntry);
163
164 WebPasteboardProxy::singleton().addWebProcessProxy(*this);
165}
166
167WebProcessProxy::~WebProcessProxy()
168{
169 RELEASE_ASSERT(isMainThreadOrCheckDisabled());
170 ASSERT(m_pageURLRetainCountMap.isEmpty());
171
172 auto result = allProcesses().remove(coreProcessIdentifier());
173 ASSERT_UNUSED(result, result);
174
175 WebPasteboardProxy::singleton().removeWebProcessProxy(*this);
176
177#if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
178 if (state() == State::Running)
179 processPool().stopDisplayLinks(*connection());
180#endif
181
182 auto isResponsiveCallbacks = WTFMove(m_isResponsiveCallbacks);
183 for (auto& callback : isResponsiveCallbacks)
184 callback(false);
185
186 if (m_webConnection)
187 m_webConnection->invalidate();
188
189 while (m_numberOfTimesSuddenTerminationWasDisabled-- > 0)
190 WebCore::enableSuddenTermination();
191
192 for (auto& callback : m_localPortActivityCompletionHandlers.values())
193 callback(MessagePortChannelProvider::HasActivity::No);
194
195#if PLATFORM(MAC)
196 HighPerformanceGPUManager::singleton().removeProcessRequiringHighPerformance(this);
197#endif
198}
199
200void WebProcessProxy::setIsInProcessCache(bool value)
201{
202 ASSERT(m_isInProcessCache != value);
203 m_isInProcessCache = value;
204
205 send(Messages::WebProcess::SetIsInProcessCache(m_isInProcessCache), 0);
206
207 if (m_isInProcessCache) {
208 // WebProcessProxy objects normally keep the process pool alive but we do not want this to be the case
209 // for cached processes or it would leak the pool.
210 m_processPool.setIsWeak(IsWeak::Yes);
211 } else {
212 RELEASE_ASSERT(m_processPool);
213 m_processPool.setIsWeak(IsWeak::No);
214 }
215}
216
217void WebProcessProxy::setWebsiteDataStore(WebsiteDataStore& dataStore)
218{
219 ASSERT(!m_websiteDataStore);
220 m_websiteDataStore = &dataStore;
221}
222
223void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
224{
225 launchOptions.processType = ProcessLauncher::ProcessType::Web;
226
227 AuxiliaryProcessProxy::getLaunchOptions(launchOptions);
228
229 if (!m_processPool->customWebContentServiceBundleIdentifier().isEmpty())
230 launchOptions.customWebContentServiceBundleIdentifier = m_processPool->customWebContentServiceBundleIdentifier().ascii();
231 if (WebKit::isInspectorProcessPool(processPool()))
232 launchOptions.extraInitializationData.add("inspector-process"_s, "1"_s);
233
234 auto overrideLanguages = m_processPool->configuration().overrideLanguages();
235 if (overrideLanguages.size()) {
236 StringBuilder languageString;
237 for (size_t i = 0; i < overrideLanguages.size(); ++i) {
238 if (i)
239 languageString.append(',');
240 languageString.append(overrideLanguages[i]);
241 }
242 launchOptions.extraInitializationData.add("OverrideLanguages"_s, languageString.toString());
243 }
244
245 launchOptions.nonValidInjectedCodeAllowed = shouldAllowNonValidInjectedCode();
246
247 if (isPrewarmed())
248 launchOptions.extraInitializationData.add("is-prewarmed"_s, "1"_s);
249
250 if (processPool().shouldMakeNextWebProcessLaunchFailForTesting()) {
251 processPool().setShouldMakeNextWebProcessLaunchFailForTesting(false);
252 launchOptions.shouldMakeProcessLaunchFailForTesting = true;
253 }
254}
255
256#if !PLATFORM(GTK) && !PLATFORM(WPE)
257void WebProcessProxy::platformGetLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
258{
259}
260#endif
261
262void WebProcessProxy::connectionWillOpen(IPC::Connection& connection)
263{
264 ASSERT(this->connection() == &connection);
265
266 // Throttling IPC messages coming from the WebProcesses so that the UIProcess stays responsive, even
267 // if one of the WebProcesses misbehaves.
268 connection.enableIncomingMessagesThrottling();
269
270 // Use this flag to force synchronous messages to be treated as asynchronous messages in the WebProcess.
271 // Otherwise, the WebProcess would process incoming synchronous IPC while waiting for a synchronous IPC
272 // reply from the UIProcess, which would be unsafe.
273 connection.setOnlySendMessagesAsDispatchWhenWaitingForSyncReplyWhenProcessingSuchAMessage(true);
274
275#if ENABLE(SEC_ITEM_SHIM)
276 SecItemShimProxy::singleton().initializeConnection(connection);
277#endif
278
279 for (auto& page : m_pageMap.values())
280 page->connectionWillOpen(connection);
281
282 for (auto* provisionalPage : m_provisionalPages)
283 provisionalPage->connectionWillOpen(connection);
284}
285
286void WebProcessProxy::processWillShutDown(IPC::Connection& connection)
287{
288 ASSERT_UNUSED(connection, this->connection() == &connection);
289
290#if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
291 processPool().stopDisplayLinks(connection);
292#endif
293
294 for (auto& page : m_pageMap.values())
295 page->webProcessWillShutDown();
296}
297
298void WebProcessProxy::shutDown()
299{
300 RELEASE_ASSERT(isMainThreadOrCheckDisabled());
301
302 shutDownProcess();
303
304 if (m_webConnection) {
305 m_webConnection->invalidate();
306 m_webConnection = nullptr;
307 }
308
309 m_responsivenessTimer.invalidate();
310 m_backgroundResponsivenessTimer.invalidate();
311 m_tokenForHoldingLockedFiles = nullptr;
312
313 for (auto& frame : copyToVector(m_frameMap.values()))
314 frame->webProcessWillShutDown();
315 m_frameMap.clear();
316
317 for (auto* visitedLinkStore : m_visitedLinkStoresWithUsers.keys())
318 visitedLinkStore->removeProcess(*this);
319 m_visitedLinkStoresWithUsers.clear();
320
321 for (auto* webUserContentControllerProxy : m_webUserContentControllerProxies)
322 webUserContentControllerProxy->removeProcess(*this);
323 m_webUserContentControllerProxies.clear();
324
325 m_userInitiatedActionMap.clear();
326
327 for (auto& port : m_processEntangledPorts)
328 UIMessagePortChannelProvider::singleton().registry().didCloseMessagePort(port);
329
330 m_processPool->disconnectProcess(this);
331}
332
333WebPageProxy* WebProcessProxy::webPage(uint64_t pageID)
334{
335 return globalPageMap().get(pageID);
336}
337
338#if ENABLE(RESOURCE_LOAD_STATISTICS)
339void WebProcessProxy::notifyPageStatisticsAndDataRecordsProcessed()
340{
341 for (auto& page : globalPageMap())
342 page.value->postMessageToInjectedBundle("WebsiteDataScanForRegistrableDomainsFinished", nullptr);
343}
344
345void WebProcessProxy::notifyWebsiteDataScanForRegistrableDomainsFinished()
346{
347 for (auto& page : globalPageMap())
348 page.value->postMessageToInjectedBundle("WebsiteDataScanForRegistrableDomainsFinished", nullptr);
349}
350
351void WebProcessProxy::notifyWebsiteDataDeletionForRegistrableDomainsFinished()
352{
353 for (auto& page : globalPageMap())
354 page.value->postMessageToInjectedBundle("WebsiteDataDeletionForRegistrableDomainsFinished", nullptr);
355}
356
357void WebProcessProxy::notifyPageStatisticsTelemetryFinished(API::Object* messageBody)
358{
359 for (auto& page : globalPageMap())
360 page.value->postMessageToInjectedBundle("ResourceLoadStatisticsTelemetryFinished", messageBody);
361}
362#endif
363
364Ref<WebPageProxy> WebProcessProxy::createWebPage(PageClient& pageClient, Ref<API::PageConfiguration>&& pageConfiguration)
365{
366 uint64_t pageID = generatePageID();
367 Ref<WebPageProxy> webPage = WebPageProxy::create(pageClient, *this, pageID, WTFMove(pageConfiguration));
368
369 addExistingWebPage(webPage.get(), BeginsUsingDataStore::Yes);
370
371 return webPage;
372}
373
374void WebProcessProxy::addExistingWebPage(WebPageProxy& webPage, BeginsUsingDataStore beginsUsingDataStore)
375{
376 ASSERT(!m_pageMap.contains(webPage.pageID()));
377 ASSERT(!globalPageMap().contains(webPage.pageID()));
378 ASSERT(!m_isInProcessCache);
379 ASSERT(!m_websiteDataStore || m_websiteDataStore == &webPage.websiteDataStore());
380
381 if (beginsUsingDataStore == BeginsUsingDataStore::Yes)
382 m_processPool->pageBeginUsingWebsiteDataStore(webPage.pageID(), webPage.websiteDataStore());
383
384 m_pageMap.set(webPage.pageID(), &webPage);
385 globalPageMap().set(webPage.pageID(), &webPage);
386
387 updateBackgroundResponsivenessTimer();
388}
389
390void WebProcessProxy::markIsNoLongerInPrewarmedPool()
391{
392 ASSERT(m_isPrewarmed);
393
394 m_isPrewarmed = false;
395 RELEASE_ASSERT(m_processPool);
396 m_processPool.setIsWeak(IsWeak::No);
397
398 send(Messages::WebProcess::MarkIsNoLongerPrewarmed(), 0);
399}
400
401void WebProcessProxy::removeWebPage(WebPageProxy& webPage, EndsUsingDataStore endsUsingDataStore)
402{
403 auto* removedPage = m_pageMap.take(webPage.pageID());
404 ASSERT_UNUSED(removedPage, removedPage == &webPage);
405 removedPage = globalPageMap().take(webPage.pageID());
406 ASSERT_UNUSED(removedPage, removedPage == &webPage);
407
408 if (endsUsingDataStore == EndsUsingDataStore::Yes)
409 m_processPool->pageEndUsingWebsiteDataStore(webPage.pageID(), webPage.websiteDataStore());
410
411 removeVisitedLinkStoreUser(webPage.visitedLinkStore(), webPage.pageID());
412
413 updateBackgroundResponsivenessTimer();
414
415 maybeShutDown();
416}
417
418void WebProcessProxy::addVisitedLinkStoreUser(VisitedLinkStore& visitedLinkStore, uint64_t pageID)
419{
420 auto& users = m_visitedLinkStoresWithUsers.ensure(&visitedLinkStore, [] {
421 return HashSet<uint64_t> { };
422 }).iterator->value;
423
424 ASSERT(!users.contains(pageID));
425 users.add(pageID);
426
427 if (users.size() == 1 && state() == State::Running)
428 visitedLinkStore.addProcess(*this);
429}
430
431void WebProcessProxy::removeVisitedLinkStoreUser(VisitedLinkStore& visitedLinkStore, uint64_t pageID)
432{
433 auto it = m_visitedLinkStoresWithUsers.find(&visitedLinkStore);
434 if (it == m_visitedLinkStoresWithUsers.end())
435 return;
436
437 auto& users = it->value;
438 users.remove(pageID);
439 if (users.isEmpty()) {
440 m_visitedLinkStoresWithUsers.remove(it);
441 visitedLinkStore.removeProcess(*this);
442 }
443}
444
445void WebProcessProxy::addWebUserContentControllerProxy(WebUserContentControllerProxy& proxy, WebPageCreationParameters& parameters)
446{
447 m_webUserContentControllerProxies.add(&proxy);
448 proxy.addProcess(*this, parameters);
449}
450
451void WebProcessProxy::didDestroyWebUserContentControllerProxy(WebUserContentControllerProxy& proxy)
452{
453 ASSERT(m_webUserContentControllerProxies.contains(&proxy));
454 m_webUserContentControllerProxies.remove(&proxy);
455}
456
457void WebProcessProxy::assumeReadAccessToBaseURL(WebPageProxy& page, const String& urlString)
458{
459 URL url(URL(), urlString);
460 if (!url.isLocalFile())
461 return;
462
463 // There's a chance that urlString does not point to a directory.
464 // Get url's base URL to add to m_localPathsWithAssumedReadAccess.
465 URL baseURL(URL(), url.baseAsString());
466 String path = baseURL.fileSystemPath();
467 if (path.isNull())
468 return;
469
470 // Client loads an alternate string. This doesn't grant universal file read, but the web process is assumed
471 // to have read access to this directory already.
472 m_localPathsWithAssumedReadAccess.add(path);
473 page.addPreviouslyVisitedPath(path);
474}
475
476bool WebProcessProxy::hasAssumedReadAccessToURL(const URL& url) const
477{
478 if (!url.isLocalFile())
479 return false;
480
481 String path = url.fileSystemPath();
482 auto startsWithURLPath = [&path](const String& assumedAccessPath) {
483 // There are no ".." components, because URL removes those.
484 return path.startsWith(assumedAccessPath);
485 };
486
487 auto& platformPaths = platformPathsWithAssumedReadAccess();
488 auto platformPathsEnd = platformPaths.end();
489 if (std::find_if(platformPaths.begin(), platformPathsEnd, startsWithURLPath) != platformPathsEnd)
490 return true;
491
492 auto localPathsEnd = m_localPathsWithAssumedReadAccess.end();
493 if (std::find_if(m_localPathsWithAssumedReadAccess.begin(), localPathsEnd, startsWithURLPath) != localPathsEnd)
494 return true;
495
496 return false;
497}
498
499bool WebProcessProxy::checkURLReceivedFromWebProcess(const String& urlString)
500{
501 return checkURLReceivedFromWebProcess(URL(URL(), urlString));
502}
503
504bool WebProcessProxy::checkURLReceivedFromWebProcess(const URL& url)
505{
506 // 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.
507
508 // Any other non-file URL is OK.
509 if (!url.isLocalFile())
510 return true;
511
512 // Any file URL is also OK if we've loaded a file URL through API before, granting universal read access.
513 if (m_mayHaveUniversalFileReadSandboxExtension)
514 return true;
515
516 // If we loaded a string with a file base URL before, loading resources from that subdirectory is fine.
517 if (hasAssumedReadAccessToURL(url))
518 return true;
519
520 // Items in back/forward list have been already checked.
521 // 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.
522 String path = url.fileSystemPath();
523 for (auto& item : WebBackForwardListItem::allItems().values()) {
524 URL itemURL(URL(), item->url());
525 if (itemURL.isLocalFile() && itemURL.fileSystemPath() == path)
526 return true;
527 URL itemOriginalURL(URL(), item->originalURL());
528 if (itemOriginalURL.isLocalFile() && itemOriginalURL.fileSystemPath() == path)
529 return true;
530 }
531
532 // A Web process that was never asked to load a file URL should not ever ask us to do anything with a file URL.
533 WTFLogAlways("Received an unexpected URL from the web process: '%s'\n", url.string().utf8().data());
534 return false;
535}
536
537#if !PLATFORM(COCOA)
538bool WebProcessProxy::fullKeyboardAccessEnabled()
539{
540 return false;
541}
542#endif
543
544bool WebProcessProxy::hasProvisionalPageWithID(uint64_t pageID) const
545{
546 for (auto* provisionalPage : m_provisionalPages) {
547 if (provisionalPage->page().pageID() == pageID)
548 return true;
549 }
550 return false;
551}
552
553bool WebProcessProxy::isAllowedToUpdateBackForwardItem(WebBackForwardListItem& item) const
554{
555 if (m_pageMap.contains(item.pageID()))
556 return true;
557
558 if (hasProvisionalPageWithID(item.pageID()))
559 return true;
560
561 if (item.suspendedPage() && item.suspendedPage()->page().pageID() == item.pageID() && &item.suspendedPage()->process() == this)
562 return true;
563
564 return false;
565}
566
567void WebProcessProxy::updateBackForwardItem(const BackForwardListItemState& itemState)
568{
569 auto* item = WebBackForwardListItem::itemForID(itemState.identifier);
570 if (!item || !isAllowedToUpdateBackForwardItem(*item))
571 return;
572
573 item->setPageState(itemState.pageState);
574}
575
576#if ENABLE(NETSCAPE_PLUGIN_API)
577void WebProcessProxy::getPlugins(bool refresh, CompletionHandler<void(Vector<PluginInfo>&& plugins, Vector<PluginInfo>&& applicationPlugins, Optional<Vector<WebCore::SupportedPluginIdentifier>>&& supportedPluginIdentifiers)>&& completionHandler)
578{
579 if (refresh)
580 m_processPool->pluginInfoStore().refresh();
581
582 auto supportedPluginIdentifiers = m_processPool->pluginInfoStore().supportedPluginIdentifiers();
583
584 Vector<PluginInfo> plugins;
585 Vector<PluginModuleInfo> pluginModules = m_processPool->pluginInfoStore().plugins();
586 for (size_t i = 0; i < pluginModules.size(); ++i)
587 plugins.append(pluginModules[i].info);
588
589 Vector<PluginInfo> applicationPlugins;
590#if ENABLE(PDFKIT_PLUGIN)
591 // Add built-in PDF last, so that it's not used when a real plug-in is installed.
592 if (!m_processPool->omitPDFSupport()) {
593 plugins.append(PDFPlugin::pluginInfo());
594 applicationPlugins.append(PDFPlugin::pluginInfo());
595 }
596#endif
597 completionHandler(WTFMove(plugins), WTFMove(applicationPlugins), WTFMove(supportedPluginIdentifiers));
598}
599#endif // ENABLE(NETSCAPE_PLUGIN_API)
600
601#if ENABLE(NETSCAPE_PLUGIN_API)
602void WebProcessProxy::getPluginProcessConnection(uint64_t pluginProcessToken, Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply&& reply)
603{
604 PluginProcessManager::singleton().getPluginProcessConnection(pluginProcessToken, WTFMove(reply));
605}
606#endif
607
608void WebProcessProxy::getNetworkProcessConnection(Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply&& reply)
609{
610 m_processPool->getNetworkProcessConnection(*this, WTFMove(reply));
611}
612
613#if !PLATFORM(COCOA)
614bool WebProcessProxy::platformIsBeingDebugged() const
615{
616 return false;
617}
618#endif
619
620#if !PLATFORM(MAC)
621bool WebProcessProxy::shouldAllowNonValidInjectedCode() const
622{
623 return false;
624}
625#endif
626
627void WebProcessProxy::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
628{
629 if (dispatchMessage(connection, decoder))
630 return;
631
632 if (m_processPool->dispatchMessage(connection, decoder))
633 return;
634
635 if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
636 didReceiveWebProcessProxyMessage(connection, decoder);
637 return;
638 }
639
640 // FIXME: Add unhandled message logging.
641}
642
643void WebProcessProxy::didReceiveSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, std::unique_ptr<IPC::Encoder>& replyEncoder)
644{
645 if (dispatchSyncMessage(connection, decoder, replyEncoder))
646 return;
647
648 if (m_processPool->dispatchSyncMessage(connection, decoder, replyEncoder))
649 return;
650
651 if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
652 didReceiveSyncWebProcessProxyMessage(connection, decoder, replyEncoder);
653 return;
654 }
655
656 // FIXME: Add unhandled message logging.
657}
658
659void WebProcessProxy::didClose(IPC::Connection&)
660{
661 RELEASE_LOG_IF(isReleaseLoggingAllowed(), Process, "%p - WebProcessProxy didClose (web process crash)", this);
662 processDidTerminateOrFailedToLaunch();
663}
664
665void WebProcessProxy::processDidTerminateOrFailedToLaunch()
666{
667 // Protect ourselves, as the call to disconnect() below may otherwise cause us
668 // to be deleted before we can finish our work.
669 Ref<WebProcessProxy> protect(*this);
670
671#if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
672 m_userMediaCaptureManagerProxy->clear();
673#endif
674
675 if (auto* webConnection = this->webConnection())
676 webConnection->didClose();
677
678 auto pages = copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values());
679 auto provisionalPages = WTF::map(m_provisionalPages, [](auto* provisionalPage) { return makeWeakPtr(provisionalPage); });
680
681 auto isResponsiveCallbacks = WTFMove(m_isResponsiveCallbacks);
682 for (auto& callback : isResponsiveCallbacks)
683 callback(false);
684
685 if (m_isInProcessCache) {
686 processPool().webProcessCache().removeProcess(*this, WebProcessCache::ShouldShutDownProcess::No);
687 ASSERT(!m_isInProcessCache);
688 }
689
690 shutDown();
691
692#if ENABLE(PUBLIC_SUFFIX_LIST)
693 if (pages.size() == 1) {
694 auto& page = *pages[0];
695 String domain = topPrivatelyControlledDomain(URL({ }, page.currentURL()).host().toString());
696 if (!domain.isEmpty())
697 page.logDiagnosticMessageWithEnhancedPrivacy(WebCore::DiagnosticLoggingKeys::domainCausingCrashKey(), domain, WebCore::ShouldSample::No);
698 }
699#endif
700
701 for (auto& page : pages)
702 page->processDidTerminate(ProcessTerminationReason::Crash);
703
704 for (auto& provisionalPage : provisionalPages) {
705 if (provisionalPage)
706 provisionalPage->processDidTerminate();
707 }
708}
709
710void WebProcessProxy::didReceiveInvalidMessage(IPC::Connection& connection, IPC::StringReference messageReceiverName, IPC::StringReference messageName)
711{
712 WTFLogAlways("Received an invalid message \"%s.%s\" from the web process.\n", messageReceiverName.toString().data(), messageName.toString().data());
713
714 WebProcessPool::didReceiveInvalidMessage(messageReceiverName, messageName);
715
716 // Terminate the WebProcess.
717 terminate();
718
719 // Since we've invalidated the connection we'll never get a IPC::Connection::Client::didClose
720 // callback so we'll explicitly call it here instead.
721 didClose(connection);
722}
723
724void WebProcessProxy::didBecomeUnresponsive()
725{
726 auto protectedThis = makeRef(*this);
727
728 m_isResponsive = NoOrMaybe::No;
729
730 auto isResponsiveCallbacks = WTFMove(m_isResponsiveCallbacks);
731
732 for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values()))
733 page->processDidBecomeUnresponsive();
734
735 bool isWebProcessResponsive = false;
736 for (auto& callback : isResponsiveCallbacks)
737 callback(isWebProcessResponsive);
738
739 // If the service worker process becomes unresponsive, kill it ourselves since there are no native clients to do it.
740 if (isServiceWorkerProcess()) {
741 RELEASE_LOG_ERROR(PerformanceLogging, "%p - WebProcessProxy::didBecomeUnresponsive() Terminating Service Worker process with pid %d because it is unresponsive", this, processIdentifier());
742 terminate();
743 }
744}
745
746void WebProcessProxy::didBecomeResponsive()
747{
748 m_isResponsive = NoOrMaybe::Maybe;
749
750 for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values()))
751 page->processDidBecomeResponsive();
752}
753
754void WebProcessProxy::willChangeIsResponsive()
755{
756 for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values()))
757 page->willChangeProcessIsResponsive();
758}
759
760void WebProcessProxy::didChangeIsResponsive()
761{
762 for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values()))
763 page->didChangeProcessIsResponsive();
764}
765
766bool WebProcessProxy::mayBecomeUnresponsive()
767{
768 return !platformIsBeingDebugged();
769}
770
771void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connection::Identifier connectionIdentifier)
772{
773 RELEASE_ASSERT(isMainThreadOrCheckDisabled());
774
775 AuxiliaryProcessProxy::didFinishLaunching(launcher, connectionIdentifier);
776
777 if (!IPC::Connection::identifierIsValid(connectionIdentifier)) {
778 RELEASE_LOG_IF(isReleaseLoggingAllowed(), Process, "%p - WebProcessProxy didFinishLaunching - invalid connection identifier (web process failed to launch)", this);
779 processDidTerminateOrFailedToLaunch();
780 return;
781 }
782
783 RELEASE_ASSERT(!m_webConnection);
784 m_webConnection = WebConnectionToWebProcess::create(this);
785
786 m_processPool->processDidFinishLaunching(this);
787 m_backgroundResponsivenessTimer.updateState();
788
789 for (auto* visitedLinkStore : m_visitedLinkStoresWithUsers.keys())
790 visitedLinkStore->addProcess(*this);
791
792#if PLATFORM(IOS_FAMILY)
793 if (connection()) {
794 if (xpc_connection_t xpcConnection = connection()->xpcConnection())
795 m_throttler.didConnectToProcess(xpc_connection_get_pid(xpcConnection));
796 }
797
798 unblockAccessibilityServerIfNeeded();
799#endif
800}
801
802WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
803{
804 if (!WebFrameProxyMap::isValidKey(frameID))
805 return 0;
806
807 return m_frameMap.get(frameID);
808}
809
810bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
811{
812 return WebFrameProxyMap::isValidKey(frameID) && !m_frameMap.contains(frameID);
813}
814
815void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy& frameProxy)
816{
817 m_frameMap.set(frameID, &frameProxy);
818}
819
820void WebProcessProxy::didDestroyFrame(uint64_t frameID)
821{
822 // If the page is closed before it has had the chance to send the DidCreateMainFrame message
823 // back to the UIProcess, then the frameDestroyed message will still be received because it
824 // gets sent directly to the WebProcessProxy.
825 ASSERT(WebFrameProxyMap::isValidKey(frameID));
826 m_frameMap.remove(frameID);
827}
828
829void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
830{
831 for (auto& frame : copyToVector(m_frameMap.values())) {
832 if (frame->page() == page)
833 frame->webProcessWillShutDown();
834 }
835}
836
837size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
838{
839 size_t result = 0;
840 for (auto& frame : m_frameMap.values()) {
841 if (frame->page() == page)
842 ++result;
843 }
844 return result;
845}
846
847auto WebProcessProxy::visiblePageToken() const -> VisibleWebPageToken
848{
849 return m_visiblePageCounter.count();
850}
851
852RefPtr<API::UserInitiatedAction> WebProcessProxy::userInitiatedActivity(uint64_t identifier)
853{
854 if (!UserInitiatedActionMap::isValidKey(identifier) || !identifier)
855 return nullptr;
856
857 auto result = m_userInitiatedActionMap.ensure(identifier, [] { return API::UserInitiatedAction::create(); });
858 return result.iterator->value;
859}
860
861bool WebProcessProxy::isResponsive() const
862{
863 return m_responsivenessTimer.isResponsive() && m_backgroundResponsivenessTimer.isResponsive();
864}
865
866void WebProcessProxy::didDestroyUserGestureToken(uint64_t identifier)
867{
868 ASSERT(UserInitiatedActionMap::isValidKey(identifier));
869 m_userInitiatedActionMap.remove(identifier);
870}
871
872bool WebProcessProxy::canBeAddedToWebProcessCache() const
873{
874 if (isServiceWorkerProcess())
875 return false;
876
877 if (WebKit::isInspectorProcessPool(processPool()))
878 return false;
879
880 return true;
881}
882
883void WebProcessProxy::maybeShutDown(AllowProcessCaching allowProcessCaching)
884{
885 if (processPool().dummyProcessProxy() == this && m_pageMap.isEmpty()) {
886 ASSERT(state() == State::Terminated);
887 m_processPool->disconnectProcess(this);
888 return;
889 }
890
891 if (state() == State::Terminated || !canTerminateAuxiliaryProcess())
892 return;
893
894 if (allowProcessCaching == AllowProcessCaching::Yes && canBeAddedToWebProcessCache() && processPool().webProcessCache().addProcessIfPossible(*this))
895 return;
896
897 shutDown();
898}
899
900bool WebProcessProxy::canTerminateAuxiliaryProcess()
901{
902 if (!m_pageMap.isEmpty() || m_suspendedPageCount || !m_provisionalPages.isEmpty() || m_isInProcessCache)
903 return false;
904
905 if (!m_processPool->shouldTerminate(this))
906 return false;
907
908 return true;
909}
910
911void WebProcessProxy::shouldTerminate(CompletionHandler<void(bool)>&& completionHandler)
912{
913 bool shouldTerminate = canTerminateAuxiliaryProcess();
914 if (shouldTerminate) {
915 // We know that the web process is going to terminate so start shutting it down in the UI process.
916 shutDown();
917 }
918 completionHandler(shouldTerminate);
919}
920
921void WebProcessProxy::updateTextCheckerState()
922{
923 if (canSendMessage())
924 send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
925}
926
927void WebProcessProxy::didSaveToPageCache()
928{
929 m_processPool->processDidCachePage(this);
930}
931
932void WebProcessProxy::releasePageCache()
933{
934 if (canSendMessage())
935 send(Messages::WebProcess::ReleasePageCache(), 0);
936}
937
938void WebProcessProxy::windowServerConnectionStateChanged()
939{
940 for (const auto& page : m_pageMap.values())
941 page->activityStateDidChange(ActivityState::IsVisuallyIdle);
942}
943
944void WebProcessProxy::fetchWebsiteData(PAL::SessionID sessionID, OptionSet<WebsiteDataType> dataTypes, CompletionHandler<void(WebsiteData)>&& completionHandler)
945{
946 ASSERT(canSendMessage());
947
948 auto token = throttler().backgroundActivityToken();
949 RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is taking a background assertion because the Web process is fetching Website data", this);
950
951 connection()->sendWithAsyncReply(Messages::WebProcess::FetchWebsiteData(sessionID, dataTypes), [this, protectedThis = makeRef(*this), token, completionHandler = WTFMove(completionHandler), sessionID] (auto reply) mutable {
952#if RELEASE_LOG_DISABLED
953 UNUSED_PARAM(sessionID);
954 UNUSED_PARAM(this);
955#endif
956 completionHandler(WTFMove(reply));
957 RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is releasing a background assertion because the Web process is done fetching Website data", this);
958 });
959}
960
961void WebProcessProxy::deleteWebsiteData(PAL::SessionID sessionID, OptionSet<WebsiteDataType> dataTypes, WallTime modifiedSince, CompletionHandler<void()>&& completionHandler)
962{
963 ASSERT(canSendMessage());
964
965 auto token = throttler().backgroundActivityToken();
966 RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is taking a background assertion because the Web process is deleting Website data", this);
967
968 connection()->sendWithAsyncReply(Messages::WebProcess::DeleteWebsiteData(sessionID, dataTypes, modifiedSince), [this, protectedThis = makeRef(*this), token, completionHandler = WTFMove(completionHandler), sessionID] () mutable {
969#if RELEASE_LOG_DISABLED
970 UNUSED_PARAM(this);
971 UNUSED_PARAM(sessionID);
972#endif
973 completionHandler();
974 RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is releasing a background assertion because the Web process is done deleting Website data", this);
975 });
976}
977
978void WebProcessProxy::deleteWebsiteDataForOrigins(PAL::SessionID sessionID, OptionSet<WebsiteDataType> dataTypes, const Vector<WebCore::SecurityOriginData>& origins, CompletionHandler<void()>&& completionHandler)
979{
980 ASSERT(canSendMessage());
981
982 auto token = throttler().backgroundActivityToken();
983 RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is taking a background assertion because the Web process is deleting Website data for several origins", this);
984
985 connection()->sendWithAsyncReply(Messages::WebProcess::DeleteWebsiteDataForOrigins(sessionID, dataTypes, origins), [this, protectedThis = makeRef(*this), token, completionHandler = WTFMove(completionHandler), sessionID] () mutable {
986#if RELEASE_LOG_DISABLED
987 UNUSED_PARAM(this);
988 UNUSED_PARAM(sessionID);
989#endif
990 completionHandler();
991 RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is releasing a background assertion because the Web process is done deleting Website data for several origins", this);
992 });
993}
994
995void WebProcessProxy::requestTermination(ProcessTerminationReason reason)
996{
997 if (state() == State::Terminated)
998 return;
999
1000 auto protectedThis = makeRef(*this);
1001 RELEASE_LOG_IF(isReleaseLoggingAllowed(), Process, "%p - WebProcessProxy::requestTermination - reason %d", this, reason);
1002
1003 AuxiliaryProcessProxy::terminate();
1004
1005 if (webConnection())
1006 webConnection()->didClose();
1007
1008 auto pages = copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values());
1009
1010 shutDown();
1011
1012 for (auto& page : pages)
1013 page->processDidTerminate(reason);
1014}
1015
1016bool WebProcessProxy::isReleaseLoggingAllowed() const
1017{
1018 return !m_websiteDataStore || m_websiteDataStore->sessionID().isAlwaysOnLoggingAllowed();
1019}
1020
1021void WebProcessProxy::stopResponsivenessTimer()
1022{
1023 responsivenessTimer().stop();
1024}
1025
1026void WebProcessProxy::enableSuddenTermination()
1027{
1028 if (state() != State::Running)
1029 return;
1030
1031 ASSERT(m_numberOfTimesSuddenTerminationWasDisabled);
1032 WebCore::enableSuddenTermination();
1033 --m_numberOfTimesSuddenTerminationWasDisabled;
1034}
1035
1036void WebProcessProxy::disableSuddenTermination()
1037{
1038 if (state() != State::Running)
1039 return;
1040
1041 WebCore::disableSuddenTermination();
1042 ++m_numberOfTimesSuddenTerminationWasDisabled;
1043}
1044
1045RefPtr<API::Object> WebProcessProxy::transformHandlesToObjects(API::Object* object)
1046{
1047 struct Transformer final : UserData::Transformer {
1048 Transformer(WebProcessProxy& webProcessProxy)
1049 : m_webProcessProxy(webProcessProxy)
1050 {
1051 }
1052
1053 bool shouldTransformObject(const API::Object& object) const override
1054 {
1055 switch (object.type()) {
1056 case API::Object::Type::FrameHandle:
1057 return static_cast<const API::FrameHandle&>(object).isAutoconverting();
1058
1059 case API::Object::Type::PageHandle:
1060 return static_cast<const API::PageHandle&>(object).isAutoconverting();
1061
1062 case API::Object::Type::PageGroupHandle:
1063#if PLATFORM(COCOA)
1064 case API::Object::Type::ObjCObjectGraph:
1065#endif
1066 return true;
1067
1068 default:
1069 return false;
1070 }
1071 }
1072
1073 RefPtr<API::Object> transformObject(API::Object& object) const override
1074 {
1075 switch (object.type()) {
1076 case API::Object::Type::FrameHandle:
1077 ASSERT(static_cast<API::FrameHandle&>(object).isAutoconverting());
1078 return m_webProcessProxy.webFrame(static_cast<API::FrameHandle&>(object).frameID());
1079
1080 case API::Object::Type::PageGroupHandle:
1081 return WebPageGroup::get(static_cast<API::PageGroupHandle&>(object).webPageGroupData().pageGroupID);
1082
1083 case API::Object::Type::PageHandle:
1084 ASSERT(static_cast<API::PageHandle&>(object).isAutoconverting());
1085 return m_webProcessProxy.webPage(static_cast<API::PageHandle&>(object).pageID());
1086
1087#if PLATFORM(COCOA)
1088 case API::Object::Type::ObjCObjectGraph:
1089 return m_webProcessProxy.transformHandlesToObjects(static_cast<ObjCObjectGraph&>(object));
1090#endif
1091 default:
1092 return &object;
1093 }
1094 }
1095
1096 WebProcessProxy& m_webProcessProxy;
1097 };
1098
1099 return UserData::transform(object, Transformer(*this));
1100}
1101
1102RefPtr<API::Object> WebProcessProxy::transformObjectsToHandles(API::Object* object)
1103{
1104 struct Transformer final : UserData::Transformer {
1105 bool shouldTransformObject(const API::Object& object) const override
1106 {
1107 switch (object.type()) {
1108 case API::Object::Type::Frame:
1109 case API::Object::Type::Page:
1110 case API::Object::Type::PageGroup:
1111#if PLATFORM(COCOA)
1112 case API::Object::Type::ObjCObjectGraph:
1113#endif
1114 return true;
1115
1116 default:
1117 return false;
1118 }
1119 }
1120
1121 RefPtr<API::Object> transformObject(API::Object& object) const override
1122 {
1123 switch (object.type()) {
1124 case API::Object::Type::Frame:
1125 return API::FrameHandle::createAutoconverting(static_cast<const WebFrameProxy&>(object).frameID());
1126
1127 case API::Object::Type::Page:
1128 return API::PageHandle::createAutoconverting(static_cast<const WebPageProxy&>(object).pageID());
1129
1130 case API::Object::Type::PageGroup:
1131 return API::PageGroupHandle::create(WebPageGroupData(static_cast<const WebPageGroup&>(object).data()));
1132
1133#if PLATFORM(COCOA)
1134 case API::Object::Type::ObjCObjectGraph:
1135 return transformObjectsToHandles(static_cast<ObjCObjectGraph&>(object));
1136#endif
1137
1138 default:
1139 return &object;
1140 }
1141 }
1142 };
1143
1144 return UserData::transform(object, Transformer());
1145}
1146
1147void WebProcessProxy::sendProcessWillSuspendImminently()
1148{
1149 if (canSendMessage())
1150 send(Messages::WebProcess::ProcessWillSuspendImminently(), 0);
1151}
1152
1153void WebProcessProxy::sendPrepareToSuspend()
1154{
1155 if (canSendMessage())
1156 send(Messages::WebProcess::PrepareToSuspend(), 0);
1157}
1158
1159void WebProcessProxy::sendCancelPrepareToSuspend()
1160{
1161 if (canSendMessage())
1162 send(Messages::WebProcess::CancelPrepareToSuspend(), 0);
1163}
1164
1165void WebProcessProxy::sendProcessDidResume()
1166{
1167 if (canSendMessage())
1168 send(Messages::WebProcess::ProcessDidResume(), 0);
1169}
1170
1171void WebProcessProxy::processReadyToSuspend()
1172{
1173 m_throttler.processReadyToSuspend();
1174}
1175
1176void WebProcessProxy::didCancelProcessSuspension()
1177{
1178 m_throttler.didCancelProcessSuspension();
1179}
1180
1181void WebProcessProxy::didSetAssertionState(AssertionState state)
1182{
1183#if PLATFORM(IOS_FAMILY)
1184 if (isServiceWorkerProcess())
1185 return;
1186
1187 ASSERT(!m_backgroundToken || !m_foregroundToken);
1188
1189 switch (state) {
1190 case AssertionState::Suspended:
1191 RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::didSetAssertionState(Suspended) release all assertions for network process", this);
1192 m_foregroundToken = nullptr;
1193 m_backgroundToken = nullptr;
1194 for (auto& page : m_pageMap.values())
1195 page->processWillBecomeSuspended();
1196 break;
1197
1198 case AssertionState::Background:
1199 RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::didSetAssertionState(Background) taking background assertion for network process", this);
1200 m_backgroundToken = processPool().backgroundWebProcessToken();
1201 m_foregroundToken = nullptr;
1202 break;
1203
1204 case AssertionState::Foreground:
1205 RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::didSetAssertionState(Foreground) taking foreground assertion for network process", this);
1206 m_foregroundToken = processPool().foregroundWebProcessToken();
1207 m_backgroundToken = nullptr;
1208 for (auto& page : m_pageMap.values())
1209 page->processWillBecomeForeground();
1210 break;
1211
1212 case AssertionState::UnboundedNetworking:
1213 ASSERT_NOT_REACHED();
1214 }
1215
1216 ASSERT(!m_backgroundToken || !m_foregroundToken);
1217#else
1218 UNUSED_PARAM(state);
1219#endif
1220}
1221
1222void WebProcessProxy::webPageMediaStateDidChange(WebPageProxy&)
1223{
1224 bool newHasAudibleWebPage = WTF::anyOf(m_pageMap.values(), [] (auto& page) { return page->isPlayingAudio(); });
1225 if (m_hasAudibleWebPage == newHasAudibleWebPage)
1226 return;
1227 m_hasAudibleWebPage = newHasAudibleWebPage;
1228
1229 if (m_hasAudibleWebPage)
1230 processPool().setWebProcessIsPlayingAudibleMedia(coreProcessIdentifier());
1231 else
1232 processPool().clearWebProcessIsPlayingAudibleMedia(coreProcessIdentifier());
1233}
1234
1235void WebProcessProxy::setIsHoldingLockedFiles(bool isHoldingLockedFiles)
1236{
1237 if (!isHoldingLockedFiles) {
1238 RELEASE_LOG(ProcessSuspension, "UIProcess is releasing a background assertion because the WebContent process is no longer holding locked files");
1239 m_tokenForHoldingLockedFiles = nullptr;
1240 return;
1241 }
1242 if (!m_tokenForHoldingLockedFiles) {
1243 RELEASE_LOG(ProcessSuspension, "UIProcess is taking a background assertion because the WebContent process is holding locked files");
1244 m_tokenForHoldingLockedFiles = m_throttler.backgroundActivityToken();
1245 }
1246}
1247
1248void WebProcessProxy::isResponsive(CompletionHandler<void(bool isWebProcessResponsive)>&& callback)
1249{
1250 if (m_isResponsive == NoOrMaybe::No) {
1251 if (callback) {
1252 RunLoop::main().dispatch([callback = WTFMove(callback)]() mutable {
1253 bool isWebProcessResponsive = false;
1254 callback(isWebProcessResponsive);
1255 });
1256 }
1257 return;
1258 }
1259
1260 if (callback)
1261 m_isResponsiveCallbacks.append(WTFMove(callback));
1262
1263 responsivenessTimer().start();
1264 send(Messages::WebProcess::MainThreadPing(), 0);
1265}
1266
1267void WebProcessProxy::isResponsiveWithLazyStop()
1268{
1269 if (m_isResponsive == NoOrMaybe::No)
1270 return;
1271
1272 if (!responsivenessTimer().hasActiveTimer()) {
1273 // We do not send a ping if we are already waiting for the WebProcess.
1274 // Spamming pings on a slow web process is not helpful.
1275 responsivenessTimer().startWithLazyStop();
1276 send(Messages::WebProcess::MainThreadPing(), 0);
1277 }
1278}
1279
1280bool WebProcessProxy::isJITEnabled() const
1281{
1282 return processPool().configuration().isJITEnabled();
1283}
1284
1285void WebProcessProxy::didReceiveMainThreadPing()
1286{
1287 responsivenessTimer().stop();
1288
1289 auto isResponsiveCallbacks = WTFMove(m_isResponsiveCallbacks);
1290 bool isWebProcessResponsive = true;
1291 for (auto& callback : isResponsiveCallbacks)
1292 callback(isWebProcessResponsive);
1293}
1294
1295void WebProcessProxy::didReceiveBackgroundResponsivenessPing()
1296{
1297 m_backgroundResponsivenessTimer.didReceiveBackgroundResponsivenessPong();
1298}
1299
1300void WebProcessProxy::processTerminated()
1301{
1302 m_responsivenessTimer.processTerminated();
1303 m_backgroundResponsivenessTimer.processTerminated();
1304}
1305
1306void WebProcessProxy::logDiagnosticMessageForResourceLimitTermination(const String& limitKey)
1307{
1308 if (pageCount())
1309 (*pages().begin())->logDiagnosticMessage(DiagnosticLoggingKeys::simulatedPageCrashKey(), limitKey, ShouldSample::No);
1310}
1311
1312void WebProcessProxy::didExceedInactiveMemoryLimitWhileActive()
1313{
1314 for (auto& page : pages())
1315 page->didExceedInactiveMemoryLimitWhileActive();
1316}
1317
1318void WebProcessProxy::didExceedActiveMemoryLimit()
1319{
1320 RELEASE_LOG_ERROR(PerformanceLogging, "%p - WebProcessProxy::didExceedActiveMemoryLimit() Terminating WebProcess with pid %d that has exceeded the active memory limit", this, processIdentifier());
1321 logDiagnosticMessageForResourceLimitTermination(DiagnosticLoggingKeys::exceededActiveMemoryLimitKey());
1322 requestTermination(ProcessTerminationReason::ExceededMemoryLimit);
1323}
1324
1325void WebProcessProxy::didExceedInactiveMemoryLimit()
1326{
1327 RELEASE_LOG_ERROR(PerformanceLogging, "%p - WebProcessProxy::didExceedInactiveMemoryLimit() Terminating WebProcess with pid %d that has exceeded the inactive memory limit", this, processIdentifier());
1328 logDiagnosticMessageForResourceLimitTermination(DiagnosticLoggingKeys::exceededInactiveMemoryLimitKey());
1329 requestTermination(ProcessTerminationReason::ExceededMemoryLimit);
1330}
1331
1332void WebProcessProxy::didExceedCPULimit()
1333{
1334 auto protectedThis = makeRef(*this);
1335
1336 for (auto& page : pages()) {
1337 if (page->isPlayingAudio()) {
1338 RELEASE_LOG(PerformanceLogging, "%p - WebProcessProxy::didExceedCPULimit() WebProcess with pid %d has exceeded the background CPU limit but we are not terminating it because there is audio playing", this, processIdentifier());
1339 return;
1340 }
1341
1342 if (page->hasActiveAudioStream() || page->hasActiveVideoStream()) {
1343 RELEASE_LOG(PerformanceLogging, "%p - WebProcessProxy::didExceedCPULimit() WebProcess with pid %d has exceeded the background CPU limit but we are not terminating it because it is capturing audio / video", this, processIdentifier());
1344 return;
1345 }
1346 }
1347
1348 bool hasVisiblePage = false;
1349 for (auto& page : pages()) {
1350 if (page->isViewVisible()) {
1351 page->didExceedBackgroundCPULimitWhileInForeground();
1352 hasVisiblePage = true;
1353 }
1354 }
1355
1356 // We only notify the client that the process exceeded the CPU limit when it is visible, we do not terminate it.
1357 if (hasVisiblePage)
1358 return;
1359
1360 if (isServiceWorkerProcess())
1361 RELEASE_LOG_ERROR(PerformanceLogging, "%p - WebProcessProxy::didExceedCPULimit() Terminating Service Worker process with pid %d that has exceeded the background CPU limit", this, processIdentifier());
1362 else
1363 RELEASE_LOG_ERROR(PerformanceLogging, "%p - WebProcessProxy::didExceedCPULimit() Terminating background WebProcess with pid %d that has exceeded the background CPU limit", this, processIdentifier());
1364 logDiagnosticMessageForResourceLimitTermination(DiagnosticLoggingKeys::exceededBackgroundCPULimitKey());
1365 requestTermination(ProcessTerminationReason::ExceededCPULimit);
1366}
1367
1368void WebProcessProxy::updateBackgroundResponsivenessTimer()
1369{
1370 m_backgroundResponsivenessTimer.updateState();
1371}
1372
1373#if !PLATFORM(COCOA)
1374const HashSet<String>& WebProcessProxy::platformPathsWithAssumedReadAccess()
1375{
1376 static NeverDestroyed<HashSet<String>> platformPathsWithAssumedReadAccess;
1377 return platformPathsWithAssumedReadAccess;
1378}
1379#endif
1380
1381void WebProcessProxy::createNewMessagePortChannel(const MessagePortIdentifier& port1, const MessagePortIdentifier& port2)
1382{
1383 m_processEntangledPorts.add(port1);
1384 m_processEntangledPorts.add(port2);
1385 UIMessagePortChannelProvider::singleton().registry().didCreateMessagePortChannel(port1, port2);
1386}
1387
1388void WebProcessProxy::entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote)
1389{
1390 m_processEntangledPorts.add(local);
1391 UIMessagePortChannelProvider::singleton().registry().didEntangleLocalToRemote(local, remote, coreProcessIdentifier());
1392
1393 auto* channel = UIMessagePortChannelProvider::singleton().registry().existingChannelContainingPort(local);
1394 if (channel && channel->hasAnyMessagesPendingOrInFlight())
1395 send(Messages::WebProcess::MessagesAvailableForPort(local), 0);
1396}
1397
1398void WebProcessProxy::messagePortDisentangled(const MessagePortIdentifier& port)
1399{
1400 auto result = m_processEntangledPorts.remove(port);
1401 ASSERT_UNUSED(result, result);
1402
1403 UIMessagePortChannelProvider::singleton().registry().didDisentangleMessagePort(port);
1404}
1405
1406void WebProcessProxy::messagePortClosed(const MessagePortIdentifier& port)
1407{
1408 UIMessagePortChannelProvider::singleton().registry().didCloseMessagePort(port);
1409}
1410
1411void WebProcessProxy::takeAllMessagesForPort(const MessagePortIdentifier& port, uint64_t messagesCallbackIdentifier)
1412{
1413 UIMessagePortChannelProvider::singleton().registry().takeAllMessagesForPort(port, [this, protectedThis = makeRef(*this), messagesCallbackIdentifier](Vector<MessageWithMessagePorts>&& messages, Function<void()>&& deliveryCallback) {
1414
1415 static uint64_t currentMessageBatchIdentifier;
1416 auto result = m_messageBatchDeliveryCompletionHandlers.ensure(++currentMessageBatchIdentifier, [deliveryCallback = WTFMove(deliveryCallback)]() mutable {
1417 return WTFMove(deliveryCallback);
1418 });
1419 ASSERT_UNUSED(result, result.isNewEntry);
1420
1421 send(Messages::WebProcess::DidTakeAllMessagesForPort(WTFMove(messages), messagesCallbackIdentifier, currentMessageBatchIdentifier), 0);
1422 });
1423}
1424
1425void WebProcessProxy::didDeliverMessagePortMessages(uint64_t messageBatchIdentifier)
1426{
1427 auto callback = m_messageBatchDeliveryCompletionHandlers.take(messageBatchIdentifier);
1428 ASSERT(callback);
1429 callback();
1430}
1431
1432void WebProcessProxy::postMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& port)
1433{
1434 if (UIMessagePortChannelProvider::singleton().registry().didPostMessageToRemote(WTFMove(message), port)) {
1435 // Look up the process for that port
1436 auto* channel = UIMessagePortChannelProvider::singleton().registry().existingChannelContainingPort(port);
1437 ASSERT(channel);
1438 auto processIdentifier = channel->processForPort(port);
1439 if (processIdentifier) {
1440 if (auto* process = WebProcessProxy::processForIdentifier(*processIdentifier))
1441 process->send(Messages::WebProcess::MessagesAvailableForPort(port), 0);
1442 }
1443 }
1444}
1445
1446void WebProcessProxy::checkRemotePortForActivity(const WebCore::MessagePortIdentifier port, uint64_t callbackIdentifier)
1447{
1448 UIMessagePortChannelProvider::singleton().registry().checkRemotePortForActivity(port, [this, protectedThis = makeRef(*this), callbackIdentifier](MessagePortChannelProvider::HasActivity hasActivity) {
1449 send(Messages::WebProcess::DidCheckRemotePortForActivity(callbackIdentifier, hasActivity == MessagePortChannelProvider::HasActivity::Yes), 0);
1450 });
1451}
1452
1453void WebProcessProxy::checkProcessLocalPortForActivity(const MessagePortIdentifier& port, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback)
1454{
1455 static uint64_t currentCallbackIdentifier;
1456 auto result = m_localPortActivityCompletionHandlers.ensure(++currentCallbackIdentifier, [callback = WTFMove(callback)]() mutable {
1457 return WTFMove(callback);
1458 });
1459 ASSERT_UNUSED(result, result.isNewEntry);
1460
1461 send(Messages::WebProcess::CheckProcessLocalPortForActivity(port, currentCallbackIdentifier), 0);
1462}
1463
1464void WebProcessProxy::didCheckProcessLocalPortForActivity(uint64_t callbackIdentifier, bool isLocallyReachable)
1465{
1466 auto callback = m_localPortActivityCompletionHandlers.take(callbackIdentifier);
1467 if (!callback)
1468 return;
1469
1470 callback(isLocallyReachable ? MessagePortChannelProvider::HasActivity::Yes : MessagePortChannelProvider::HasActivity::No);
1471}
1472
1473void WebProcessProxy::didCollectPrewarmInformation(const WebCore::RegistrableDomain& domain, const WebCore::PrewarmInformation& prewarmInformation)
1474{
1475 processPool().didCollectPrewarmInformation(domain, prewarmInformation);
1476}
1477
1478void WebProcessProxy::activePagesDomainsForTesting(CompletionHandler<void(Vector<String>&&)>&& completionHandler)
1479{
1480 connection()->sendWithAsyncReply(Messages::WebProcess::GetActivePagesOriginsForTesting(), WTFMove(completionHandler));
1481}
1482
1483void WebProcessProxy::didStartProvisionalLoadForMainFrame(const URL& url)
1484{
1485 RELEASE_ASSERT(!isInProcessCache());
1486
1487 // This process has been used for several registrable domains already.
1488 if (m_registrableDomain && m_registrableDomain->isEmpty())
1489 return;
1490
1491 auto registrableDomain = WebCore::RegistrableDomain { url };
1492 if (m_registrableDomain && *m_registrableDomain != registrableDomain) {
1493 // Null out registrable domain since this process has now been used for several domains.
1494 m_registrableDomain = WebCore::RegistrableDomain { };
1495 return;
1496 }
1497
1498 // Associate the process with this registrable domain.
1499 m_registrableDomain = WTFMove(registrableDomain);
1500}
1501
1502void WebProcessProxy::incrementSuspendedPageCount()
1503{
1504 ++m_suspendedPageCount;
1505 if (m_suspendedPageCount == 1)
1506 send(Messages::WebProcess::SetHasSuspendedPageProxy(true), 0);
1507}
1508
1509void WebProcessProxy::decrementSuspendedPageCount()
1510{
1511 ASSERT(m_suspendedPageCount);
1512 --m_suspendedPageCount;
1513 if (!m_suspendedPageCount)
1514 send(Messages::WebProcess::SetHasSuspendedPageProxy(false), 0);
1515}
1516
1517#if PLATFORM(WATCHOS)
1518
1519void WebProcessProxy::takeBackgroundActivityTokenForFullscreenInput()
1520{
1521 if (m_backgroundActivityTokenForFullscreenFormControls)
1522 return;
1523
1524 m_backgroundActivityTokenForFullscreenFormControls = m_throttler.backgroundActivityToken();
1525 RELEASE_LOG(ProcessSuspension, "UIProcess is taking a background assertion because it is presenting fullscreen UI for form controls.");
1526}
1527
1528void WebProcessProxy::releaseBackgroundActivityTokenForFullscreenInput()
1529{
1530 if (!m_backgroundActivityTokenForFullscreenFormControls)
1531 return;
1532
1533 m_backgroundActivityTokenForFullscreenFormControls = nullptr;
1534 RELEASE_LOG(ProcessSuspension, "UIProcess is releasing a background assertion because it has dismissed fullscreen UI for form controls.");
1535}
1536
1537#endif
1538
1539} // namespace WebKit
1540
1541#undef MESSAGE_CHECK
1542#undef MESSAGE_CHECK_URL
1543