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