source: webkit/trunk/Source/WebKit/UIProcess/ApplicationStateTracker.mm

Last change on this file was 295493, checked in by [email protected], 3 years ago

Start/stop ProcessStateMonitor with ApplicationStateTracker
https://bugs.webkit.org/show_bug.cgi?id=241427

Reviewed by Geoffrey Garen.

We want ProcessStateMonitor to be started when application is in background and to be stopped when application is in
foreground. In r294405, we start/stop ProcessStateMonitor on receiving UIApplicationDidEnterBackgroundNotification and
UIApplicationWillEnterForegroundNotification notifications. However, UIApplicationWillEnterForegroundNotification can be
sent later than UISceneWillEnterForegroundNotification. In ApplicationStateTracke, we start creating foreground activity
for web process on receiving UISceneWillEnterForegroundNotification. At this time,
UIApplicationWillEnterForegroundNotification may not be received yet and ProcessStateMonitor may still be
working. ProcessStateMonitor may forbid creating new activity after it sets process shouldSuspend (see r294405), so we
need to make sure ProcessStateMonitor is stopped before starting new foreground activity.

To achieve that, we now decide when to start/stop ProcessStateMonitor with ApplicationStateTracker. We start
ProcessStateMonitor when all trackers are in the background, and stop it when at least one tracker is in foreground.

  • Source/WebKit/UIProcess/ApplicationStateTracker.mm:

(WebKit::allApplicationStateTrackers):
(WebKit::updateApplicationBackgroundState):
(WebKit::ApplicationStateTracker::ApplicationStateTracker):
(WebKit::ApplicationStateTracker::~ApplicationStateTracker):
(WebKit::ApplicationStateTracker::applicationDidEnterBackground):
(WebKit::ApplicationStateTracker::applicationWillEnterForeground):

  • Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm:

(WebKit::WebProcessPool::setProcessesShouldSuspend):

  • Source/WebKit/UIProcess/ProcessAssertion.h:
  • Source/WebKit/UIProcess/WebProcessPool.h:
  • Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:

(-[WKProcessAssertionBackgroundTaskManager _releaseBackgroundTask]):
(-[WKProcessAssertionBackgroundTaskManager setProcessStateMonitorEnabled:]):
(WebKit::ProcessAndUIAssertion::setProcessStateMonitorEnabled):

  • Source/WebKit/UIProcess/ios/ProcessStateMonitor.mm:

(WebKit::ProcessStateMonitor::checkRemainingRunTime):

  • Source/WebKit/UIProcess/ios/WebProcessProxyIOS.mm:

(WebKit::WebProcessProxy::platformInitialize):

Canonical link: https://commits.webkit.org/251498@main

File size: 11.2 KB
Line 
1/*
2 * Copyright (C) 2015 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#import "config.h"
27#import "ApplicationStateTracker.h"
28
29#if PLATFORM(IOS_FAMILY)
30
31#import "EndowmentStateTracker.h"
32#import "Logging.h"
33#import "ProcessAssertion.h"
34#import "SandboxUtilities.h"
35#import "UIKitSPI.h"
36#import <wtf/ObjCRuntimeExtras.h>
37#import <wtf/cocoa/Entitlements.h>
38#import <wtf/spi/cocoa/SecuritySPI.h>
39
40@interface UIWindow (WKDetails)
41- (BOOL)_isHostedInAnotherProcess;
42@end
43
44#if HAVE(UISCENE_BASED_VIEW_SERVICE_STATE_NOTIFICATIONS)
45static NSNotificationName const viewServiceBackgroundNotificationName = @"_UIViewServiceHostSceneDidEnterBackgroundNotification";
46static NSNotificationName const viewServiceForegroundNotificationName = @"_UIViewServiceHostSceneWillEnterForegroundNotification";
47#else
48static NSNotificationName const viewServiceBackgroundNotificationName = @"_UIViewServiceHostDidEnterBackgroundNotification";
49static NSNotificationName const viewServiceForegroundNotificationName = @"_UIViewServiceHostWillEnterForegroundNotification";
50#endif
51
52namespace WebKit {
53
54static WeakHashSet<ApplicationStateTracker>& allApplicationStateTrackers()
55{
56 static NeverDestroyed<WeakHashSet<ApplicationStateTracker>> trackers;
57 return trackers;
58}
59
60static void updateApplicationBackgroundState()
61{
62 static bool s_isApplicationInBackground = false;
63 auto isAnyStateTrackerInForeground = []() -> bool {
64 return WTF::anyOf(allApplicationStateTrackers(), [](auto& tracker) {
65 return !tracker.isInBackground();
66 });
67 };
68 bool isApplicationInBackground = !isAnyStateTrackerInForeground();
69 if (s_isApplicationInBackground == isApplicationInBackground)
70 return;
71
72 s_isApplicationInBackground = isApplicationInBackground;
73 ProcessAndUIAssertion::setProcessStateMonitorEnabled(isApplicationInBackground);
74}
75
76ApplicationType applicationType(UIWindow *window)
77{
78 if (_UIApplicationIsExtension())
79 return ApplicationType::Extension;
80
81 if (WTF::processHasEntitlement("com.apple.UIKit.vends-view-services"_s) && window._isHostedInAnotherProcess)
82 return ApplicationType::ViewService;
83
84 return ApplicationType::Application;
85}
86
87ApplicationStateTracker::ApplicationStateTracker(UIView *view, SEL didEnterBackgroundSelector, SEL didFinishSnapshottingAfterEnteringBackgroundSelector, SEL willEnterForegroundSelector, SEL willBeginSnapshotSequenceSelector, SEL didCompleteSnapshotSequenceSelector)
88 : m_view(view)
89 , m_didEnterBackgroundSelector(didEnterBackgroundSelector)
90 , m_didFinishSnapshottingAfterEnteringBackgroundSelector(didFinishSnapshottingAfterEnteringBackgroundSelector)
91 , m_willEnterForegroundSelector(willEnterForegroundSelector)
92 , m_willBeginSnapshotSequenceSelector(willBeginSnapshotSequenceSelector)
93 , m_didCompleteSnapshotSequenceSelector(didCompleteSnapshotSequenceSelector)
94 , m_isInBackground(true)
95 , m_didEnterBackgroundObserver(nullptr)
96 , m_didFinishSnapshottingAfterEnteringBackgroundObserver(nullptr)
97 , m_willEnterForegroundObserver(nullptr)
98{
99 ASSERT([m_view.get() respondsToSelector:m_didEnterBackgroundSelector]);
100 ASSERT([m_view.get() respondsToSelector:m_didFinishSnapshottingAfterEnteringBackgroundSelector]);
101 ASSERT([m_view.get() respondsToSelector:m_willEnterForegroundSelector]);
102
103 UIWindow *window = [m_view.get() window];
104 RELEASE_ASSERT(window);
105
106 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
107 UIApplication *application = [UIApplication sharedApplication];
108
109 WeakPtr weakThis { *this };
110
111 m_didFinishSnapshottingAfterEnteringBackgroundObserver = [notificationCenter addObserverForName:@"_UIApplicationDidFinishSuspensionSnapshotNotification" object:application queue:nil usingBlock:[weakThis](NSNotification *) {
112 auto applicationStateTracker = weakThis.get();
113 if (!applicationStateTracker)
114 return;
115 applicationStateTracker->applicationDidFinishSnapshottingAfterEnteringBackground();
116 }];
117
118 switch (applicationType(window)) {
119 case ApplicationType::Application: {
120 m_isInBackground = window.windowScene.activationState == UISceneActivationStateBackground || window.windowScene.activationState == UISceneActivationStateUnattached;
121 RELEASE_LOG(ViewState, "%p - ApplicationStateTracker::ApplicationStateTracker(): m_isInBackground=%d", this, m_isInBackground);
122
123 m_didEnterBackgroundObserver = [notificationCenter addObserverForName:UISceneDidEnterBackgroundNotification object:nil queue:nil usingBlock:[this](NSNotification *notification) {
124 if (notification.object == [[m_view window] windowScene]) {
125 RELEASE_LOG(ViewState, "%p - ApplicationStateTracker: UISceneDidEnterBackground", this);
126 applicationDidEnterBackground();
127 }
128 }];
129
130 m_willEnterForegroundObserver = [notificationCenter addObserverForName:UISceneWillEnterForegroundNotification object:nil queue:nil usingBlock:[this](NSNotification *notification) {
131 if (notification.object == [[m_view window] windowScene]) {
132 RELEASE_LOG(ViewState, "%p - ApplicationStateTracker: UISceneWillEnterForeground", this);
133 applicationWillEnterForeground();
134 }
135 }];
136
137 m_willBeginSnapshotSequenceObserver = [notificationCenter addObserverForName:_UISceneWillBeginSystemSnapshotSequence object:nil queue:nil usingBlock:[this](NSNotification *notification) {
138 willBeginSnapshotSequence();
139 }];
140
141 m_didCompleteSnapshotSequenceObserver = [notificationCenter addObserverForName:_UISceneDidCompleteSystemSnapshotSequence object:nil queue:nil usingBlock:[this](NSNotification *notification) {
142 didCompleteSnapshotSequence();
143 }];
144 break;
145 }
146
147 case ApplicationType::Extension:
148 case ApplicationType::ViewService: {
149 UIViewController *serviceViewController = nil;
150
151 for (UIView *view = m_view.get().get(); view; view = view.superview) {
152 UIViewController *viewController = [UIViewController viewControllerForView:view];
153
154 if (viewController._hostProcessIdentifier) {
155 serviceViewController = viewController;
156 break;
157 }
158 }
159
160 ASSERT(serviceViewController);
161
162 pid_t applicationPID = serviceViewController._hostProcessIdentifier;
163 ASSERT(applicationPID);
164
165 m_isInBackground = !EndowmentStateTracker::isApplicationForeground(applicationPID);
166
167 // Workaround for <rdar://problem/34028921>. If the host application is StoreKitUIService then it is also a ViewService
168 // and is always in the background. We need to treat StoreKitUIService as foreground for the purpose of process suspension
169 // or its ViewServices will get suspended.
170 if ([serviceViewController._hostApplicationBundleIdentifier isEqualToString:@"com.apple.ios.StoreKitUIService"])
171 m_isInBackground = false;
172
173 RELEASE_LOG(ProcessSuspension, "%{public}s has PID %d, host application PID=%d, isInBackground=%d", _UIApplicationIsExtension() ? "Extension" : "ViewService", getpid(), applicationPID, m_isInBackground);
174
175 m_didEnterBackgroundObserver = [notificationCenter addObserverForName:viewServiceBackgroundNotificationName object:serviceViewController queue:nil usingBlock:[this, applicationPID](NSNotification *) {
176 RELEASE_LOG(ProcessSuspension, "%{public}s has PID %d, host application PID=%d, didEnterBackground", _UIApplicationIsExtension() ? "Extension" : "ViewService", getpid(), applicationPID);
177 applicationDidEnterBackground();
178 }];
179 m_willEnterForegroundObserver = [notificationCenter addObserverForName:viewServiceForegroundNotificationName object:serviceViewController queue:nil usingBlock:[this, applicationPID](NSNotification *) {
180 RELEASE_LOG(ProcessSuspension, "%{public}s has PID %d, host application PID=%d, willEnterForeground", _UIApplicationIsExtension() ? "Extension" : "ViewService", getpid(), applicationPID);
181 applicationWillEnterForeground();
182 }];
183
184 break;
185 }
186 }
187
188 allApplicationStateTrackers().add(*this);
189 updateApplicationBackgroundState();
190}
191
192ApplicationStateTracker::~ApplicationStateTracker()
193{
194 RELEASE_LOG(ViewState, "%p - ~ApplicationStateTracker", this);
195
196 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
197 [notificationCenter removeObserver:m_didEnterBackgroundObserver];
198 [notificationCenter removeObserver:m_didFinishSnapshottingAfterEnteringBackgroundObserver];
199 [notificationCenter removeObserver:m_willEnterForegroundObserver];
200 [notificationCenter removeObserver:m_willBeginSnapshotSequenceObserver];
201 [notificationCenter removeObserver:m_didCompleteSnapshotSequenceObserver];
202
203 allApplicationStateTrackers().remove(*this);
204 updateApplicationBackgroundState();
205}
206
207void ApplicationStateTracker::applicationDidEnterBackground()
208{
209 m_isInBackground = true;
210 updateApplicationBackgroundState();
211
212 if (auto view = m_view.get())
213 wtfObjCMsgSend<void>(view.get(), m_didEnterBackgroundSelector);
214}
215
216void ApplicationStateTracker::applicationDidFinishSnapshottingAfterEnteringBackground()
217{
218 if (auto view = m_view.get())
219 wtfObjCMsgSend<void>(view.get(), m_didFinishSnapshottingAfterEnteringBackgroundSelector);
220}
221
222void ApplicationStateTracker::applicationWillEnterForeground()
223{
224 m_isInBackground = false;
225 updateApplicationBackgroundState();
226
227 if (auto view = m_view.get())
228 wtfObjCMsgSend<void>(view.get(), m_willEnterForegroundSelector);
229}
230
231void ApplicationStateTracker::willBeginSnapshotSequence()
232{
233 RELEASE_LOG(ViewState, "%p - ApplicationStateTracker:willBeginSnapshotSequence()", this);
234 if (auto view = m_view.get())
235 wtfObjCMsgSend<void>(view.get(), m_willBeginSnapshotSequenceSelector);
236}
237
238void ApplicationStateTracker::didCompleteSnapshotSequence()
239{
240 RELEASE_LOG(ViewState, "%p - ApplicationStateTracker:didCompleteSnapshotSequence()", this);
241 if (auto view = m_view.get())
242 wtfObjCMsgSend<void>(view.get(), m_didCompleteSnapshotSequenceSelector);
243}
244
245}
246
247#endif
Note: See TracBrowser for help on using the repository browser.