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 | |
87 | namespace WebKit { |
88 | using namespace WebCore; |
89 | |
90 | static 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 | |
101 | static HashMap<ProcessIdentifier, WebProcessProxy*>& allProcesses() |
102 | { |
103 | ASSERT(isMainThreadOrCheckDisabled()); |
104 | static NeverDestroyed<HashMap<ProcessIdentifier, WebProcessProxy*>> map; |
105 | return map; |
106 | } |
107 | |
108 | WebProcessProxy* WebProcessProxy::processForIdentifier(ProcessIdentifier identifier) |
109 | { |
110 | return allProcesses().get(identifier); |
111 | } |
112 | |
113 | PageIdentifier WebProcessProxy::generatePageID() |
114 | { |
115 | return PageIdentifier::generate(); |
116 | } |
117 | |
118 | static WebProcessProxy::WebPageProxyMap& globalPageMap() |
119 | { |
120 | ASSERT(isMainThreadOrCheckDisabled()); |
121 | static NeverDestroyed<WebProcessProxy::WebPageProxyMap> pageMap; |
122 | return pageMap; |
123 | } |
124 | |
125 | void 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 | |
134 | Ref<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 | |
142 | WebProcessProxy::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 | |
166 | WebProcessProxy::~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 | |
202 | void 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 | |
219 | void WebProcessProxy::setWebsiteDataStore(WebsiteDataStore& dataStore) |
220 | { |
221 | ASSERT(!m_websiteDataStore); |
222 | m_websiteDataStore = &dataStore; |
223 | } |
224 | |
225 | void 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) |
259 | void WebProcessProxy::platformGetLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions) |
260 | { |
261 | } |
262 | #endif |
263 | |
264 | void 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 | |
288 | void 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 | |
300 | void 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 | |
335 | WebPageProxy* WebProcessProxy::webPage(PageIdentifier pageID) |
336 | { |
337 | return globalPageMap().get(pageID); |
338 | } |
339 | |
340 | #if ENABLE(RESOURCE_LOAD_STATISTICS) |
341 | void WebProcessProxy::notifyPageStatisticsAndDataRecordsProcessed() |
342 | { |
343 | for (auto& page : globalPageMap()) |
344 | page.value->postMessageToInjectedBundle("WebsiteDataScanForRegistrableDomainsFinished" , nullptr); |
345 | } |
346 | |
347 | void WebProcessProxy::notifyWebsiteDataScanForRegistrableDomainsFinished() |
348 | { |
349 | for (auto& page : globalPageMap()) |
350 | page.value->postMessageToInjectedBundle("WebsiteDataScanForRegistrableDomainsFinished" , nullptr); |
351 | } |
352 | |
353 | void WebProcessProxy::notifyWebsiteDataDeletionForRegistrableDomainsFinished() |
354 | { |
355 | for (auto& page : globalPageMap()) |
356 | page.value->postMessageToInjectedBundle("WebsiteDataDeletionForRegistrableDomainsFinished" , nullptr); |
357 | } |
358 | |
359 | void WebProcessProxy::notifyPageStatisticsTelemetryFinished(API::Object* messageBody) |
360 | { |
361 | for (auto& page : globalPageMap()) |
362 | page.value->postMessageToInjectedBundle("ResourceLoadStatisticsTelemetryFinished" , messageBody); |
363 | } |
364 | #endif |
365 | |
366 | Ref<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 | |
376 | void 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 | |
392 | void 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 | |
403 | void 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 | |
420 | void 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 | |
433 | void 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 | |
447 | void WebProcessProxy::addWebUserContentControllerProxy(WebUserContentControllerProxy& proxy, WebPageCreationParameters& parameters) |
448 | { |
449 | m_webUserContentControllerProxies.add(&proxy); |
450 | proxy.addProcess(*this, parameters); |
451 | } |
452 | |
453 | void WebProcessProxy::didDestroyWebUserContentControllerProxy(WebUserContentControllerProxy& proxy) |
454 | { |
455 | ASSERT(m_webUserContentControllerProxies.contains(&proxy)); |
456 | m_webUserContentControllerProxies.remove(&proxy); |
457 | } |
458 | |
459 | void 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 | |
478 | bool 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 | |
501 | bool WebProcessProxy::checkURLReceivedFromWebProcess(const String& urlString) |
502 | { |
503 | return checkURLReceivedFromWebProcess(URL(URL(), urlString)); |
504 | } |
505 | |
506 | bool 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) |
540 | bool WebProcessProxy::fullKeyboardAccessEnabled() |
541 | { |
542 | return false; |
543 | } |
544 | #endif |
545 | |
546 | bool 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 | |
555 | bool 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 | |
569 | void 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) |
579 | void 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) |
604 | void WebProcessProxy::getPluginProcessConnection(uint64_t pluginProcessToken, Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply&& reply) |
605 | { |
606 | PluginProcessManager::singleton().getPluginProcessConnection(pluginProcessToken, WTFMove(reply)); |
607 | } |
608 | #endif |
609 | |
610 | void WebProcessProxy::getNetworkProcessConnection(Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply&& reply) |
611 | { |
612 | m_processPool->getNetworkProcessConnection(*this, WTFMove(reply)); |
613 | } |
614 | |
615 | #if !PLATFORM(COCOA) |
616 | bool WebProcessProxy::platformIsBeingDebugged() const |
617 | { |
618 | return false; |
619 | } |
620 | #endif |
621 | |
622 | #if !PLATFORM(MAC) |
623 | bool WebProcessProxy::shouldAllowNonValidInjectedCode() const |
624 | { |
625 | return false; |
626 | } |
627 | #endif |
628 | |
629 | void 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 | |
645 | void 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 | |
661 | void WebProcessProxy::didClose(IPC::Connection&) |
662 | { |
663 | RELEASE_LOG_IF(isReleaseLoggingAllowed(), Process, "%p - WebProcessProxy didClose (web process crash)" , this); |
664 | processDidTerminateOrFailedToLaunch(); |
665 | } |
666 | |
667 | void 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 | |
712 | void 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 | |
726 | void 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 | |
748 | void WebProcessProxy::didBecomeResponsive() |
749 | { |
750 | m_isResponsive = NoOrMaybe::Maybe; |
751 | |
752 | for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values())) |
753 | page->processDidBecomeResponsive(); |
754 | } |
755 | |
756 | void WebProcessProxy::willChangeIsResponsive() |
757 | { |
758 | for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values())) |
759 | page->willChangeProcessIsResponsive(); |
760 | } |
761 | |
762 | void WebProcessProxy::didChangeIsResponsive() |
763 | { |
764 | for (auto& page : copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values())) |
765 | page->didChangeProcessIsResponsive(); |
766 | } |
767 | |
768 | bool WebProcessProxy::mayBecomeUnresponsive() |
769 | { |
770 | return !platformIsBeingDebugged(); |
771 | } |
772 | |
773 | void 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 | |
804 | WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const |
805 | { |
806 | if (!WebFrameProxyMap::isValidKey(frameID)) |
807 | return 0; |
808 | |
809 | return m_frameMap.get(frameID); |
810 | } |
811 | |
812 | bool WebProcessProxy::canCreateFrame(uint64_t frameID) const |
813 | { |
814 | return WebFrameProxyMap::isValidKey(frameID) && !m_frameMap.contains(frameID); |
815 | } |
816 | |
817 | void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy& frameProxy) |
818 | { |
819 | m_frameMap.set(frameID, &frameProxy); |
820 | } |
821 | |
822 | void 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 | |
831 | void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page) |
832 | { |
833 | for (auto& frame : copyToVector(m_frameMap.values())) { |
834 | if (frame->page() == page) |
835 | frame->webProcessWillShutDown(); |
836 | } |
837 | } |
838 | |
839 | size_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 | |
849 | auto WebProcessProxy::visiblePageToken() const -> VisibleWebPageToken |
850 | { |
851 | return m_visiblePageCounter.count(); |
852 | } |
853 | |
854 | RefPtr<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 | |
863 | bool WebProcessProxy::isResponsive() const |
864 | { |
865 | return m_responsivenessTimer.isResponsive() && m_backgroundResponsivenessTimer.isResponsive(); |
866 | } |
867 | |
868 | void WebProcessProxy::didDestroyUserGestureToken(uint64_t identifier) |
869 | { |
870 | ASSERT(UserInitiatedActionMap::isValidKey(identifier)); |
871 | m_userInitiatedActionMap.remove(identifier); |
872 | } |
873 | |
874 | bool 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 | |
885 | void 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 | |
902 | bool 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 | |
913 | void 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 | |
923 | void WebProcessProxy::updateTextCheckerState() |
924 | { |
925 | if (canSendMessage()) |
926 | send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0); |
927 | } |
928 | |
929 | void WebProcessProxy::didSaveToPageCache() |
930 | { |
931 | m_processPool->processDidCachePage(this); |
932 | } |
933 | |
934 | void WebProcessProxy::releasePageCache() |
935 | { |
936 | if (canSendMessage()) |
937 | send(Messages::WebProcess::ReleasePageCache(), 0); |
938 | } |
939 | |
940 | void WebProcessProxy::windowServerConnectionStateChanged() |
941 | { |
942 | for (const auto& page : m_pageMap.values()) |
943 | page->activityStateDidChange(ActivityState::IsVisuallyIdle); |
944 | } |
945 | |
946 | void 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 | |
963 | void 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 | |
980 | void 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 | |
997 | void 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 | |
1018 | bool WebProcessProxy::isReleaseLoggingAllowed() const |
1019 | { |
1020 | return !m_websiteDataStore || m_websiteDataStore->sessionID().isAlwaysOnLoggingAllowed(); |
1021 | } |
1022 | |
1023 | void WebProcessProxy::stopResponsivenessTimer() |
1024 | { |
1025 | responsivenessTimer().stop(); |
1026 | } |
1027 | |
1028 | void WebProcessProxy::enableSuddenTermination() |
1029 | { |
1030 | if (state() != State::Running) |
1031 | return; |
1032 | |
1033 | ASSERT(m_numberOfTimesSuddenTerminationWasDisabled); |
1034 | WebCore::enableSuddenTermination(); |
1035 | --m_numberOfTimesSuddenTerminationWasDisabled; |
1036 | } |
1037 | |
1038 | void WebProcessProxy::disableSuddenTermination() |
1039 | { |
1040 | if (state() != State::Running) |
1041 | return; |
1042 | |
1043 | WebCore::disableSuddenTermination(); |
1044 | ++m_numberOfTimesSuddenTerminationWasDisabled; |
1045 | } |
1046 | |
1047 | RefPtr<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 | |
1104 | RefPtr<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 | |
1149 | void WebProcessProxy::sendProcessWillSuspendImminently() |
1150 | { |
1151 | if (canSendMessage()) |
1152 | send(Messages::WebProcess::ProcessWillSuspendImminently(), 0); |
1153 | } |
1154 | |
1155 | void WebProcessProxy::sendPrepareToSuspend() |
1156 | { |
1157 | if (canSendMessage()) |
1158 | send(Messages::WebProcess::PrepareToSuspend(), 0); |
1159 | } |
1160 | |
1161 | void WebProcessProxy::sendCancelPrepareToSuspend() |
1162 | { |
1163 | if (canSendMessage()) |
1164 | send(Messages::WebProcess::CancelPrepareToSuspend(), 0); |
1165 | } |
1166 | |
1167 | void WebProcessProxy::sendProcessDidResume() |
1168 | { |
1169 | if (canSendMessage()) |
1170 | send(Messages::WebProcess::ProcessDidResume(), 0); |
1171 | } |
1172 | |
1173 | void WebProcessProxy::processReadyToSuspend() |
1174 | { |
1175 | m_throttler.processReadyToSuspend(); |
1176 | } |
1177 | |
1178 | void WebProcessProxy::didCancelProcessSuspension() |
1179 | { |
1180 | m_throttler.didCancelProcessSuspension(); |
1181 | } |
1182 | |
1183 | void 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 | |
1224 | void 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 | |
1237 | void 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 | |
1250 | void 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 | |
1269 | void 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 | |
1282 | bool WebProcessProxy::isJITEnabled() const |
1283 | { |
1284 | return processPool().configuration().isJITEnabled(); |
1285 | } |
1286 | |
1287 | void 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 | |
1297 | void WebProcessProxy::didReceiveBackgroundResponsivenessPing() |
1298 | { |
1299 | m_backgroundResponsivenessTimer.didReceiveBackgroundResponsivenessPong(); |
1300 | } |
1301 | |
1302 | void WebProcessProxy::processTerminated() |
1303 | { |
1304 | m_responsivenessTimer.processTerminated(); |
1305 | m_backgroundResponsivenessTimer.processTerminated(); |
1306 | } |
1307 | |
1308 | void WebProcessProxy::logDiagnosticMessageForResourceLimitTermination(const String& limitKey) |
1309 | { |
1310 | if (pageCount()) |
1311 | (*pages().begin())->logDiagnosticMessage(DiagnosticLoggingKeys::simulatedPageCrashKey(), limitKey, ShouldSample::No); |
1312 | } |
1313 | |
1314 | void WebProcessProxy::didExceedInactiveMemoryLimitWhileActive() |
1315 | { |
1316 | for (auto& page : pages()) |
1317 | page->didExceedInactiveMemoryLimitWhileActive(); |
1318 | } |
1319 | |
1320 | void 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 | |
1327 | void 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 | |
1334 | void 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 | |
1370 | void WebProcessProxy::updateBackgroundResponsivenessTimer() |
1371 | { |
1372 | m_backgroundResponsivenessTimer.updateState(); |
1373 | } |
1374 | |
1375 | #if !PLATFORM(COCOA) |
1376 | const HashSet<String>& WebProcessProxy::platformPathsWithAssumedReadAccess() |
1377 | { |
1378 | static NeverDestroyed<HashSet<String>> platformPathsWithAssumedReadAccess; |
1379 | return platformPathsWithAssumedReadAccess; |
1380 | } |
1381 | #endif |
1382 | |
1383 | void 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 | |
1390 | void 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 | |
1400 | void 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 | |
1408 | void WebProcessProxy::messagePortClosed(const MessagePortIdentifier& port) |
1409 | { |
1410 | UIMessagePortChannelProvider::singleton().registry().didCloseMessagePort(port); |
1411 | } |
1412 | |
1413 | void 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 | |
1427 | void WebProcessProxy::didDeliverMessagePortMessages(uint64_t messageBatchIdentifier) |
1428 | { |
1429 | auto callback = m_messageBatchDeliveryCompletionHandlers.take(messageBatchIdentifier); |
1430 | ASSERT(callback); |
1431 | callback(); |
1432 | } |
1433 | |
1434 | void 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 | |
1448 | void 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 | |
1455 | void 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 | |
1466 | void 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 | |
1475 | void WebProcessProxy::didCollectPrewarmInformation(const WebCore::RegistrableDomain& domain, const WebCore::PrewarmInformation& prewarmInformation) |
1476 | { |
1477 | processPool().didCollectPrewarmInformation(domain, prewarmInformation); |
1478 | } |
1479 | |
1480 | void WebProcessProxy::activePagesDomainsForTesting(CompletionHandler<void(Vector<String>&&)>&& completionHandler) |
1481 | { |
1482 | connection()->sendWithAsyncReply(Messages::WebProcess::GetActivePagesOriginsForTesting(), WTFMove(completionHandler)); |
1483 | } |
1484 | |
1485 | void 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 | |
1504 | void WebProcessProxy::incrementSuspendedPageCount() |
1505 | { |
1506 | ++m_suspendedPageCount; |
1507 | if (m_suspendedPageCount == 1) |
1508 | send(Messages::WebProcess::SetHasSuspendedPageProxy(true), 0); |
1509 | } |
1510 | |
1511 | void WebProcessProxy::decrementSuspendedPageCount() |
1512 | { |
1513 | ASSERT(m_suspendedPageCount); |
1514 | --m_suspendedPageCount; |
1515 | if (!m_suspendedPageCount) |
1516 | send(Messages::WebProcess::SetHasSuspendedPageProxy(false), 0); |
1517 | } |
1518 | |
1519 | WebProcessPool& WebProcessProxy::processPool() const |
1520 | { |
1521 | ASSERT(m_processPool); |
1522 | return *m_processPool.get(); |
1523 | } |
1524 | |
1525 | #if PLATFORM(WATCHOS) |
1526 | |
1527 | void 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 | |
1536 | void 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 | |