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