Unreviewed, fix assertions in layout tests on iOS Simulator after r242666.
[WebKit-https.git] / Source / WebKit / UIProcess / ios / ProcessAssertionIOS.mm
1 /*
2  * Copyright (C) 2014-2019 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 "ProcessAssertion.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "AssertionServicesSPI.h"
32 #import "Logging.h"
33 #import <UIKit/UIApplication.h>
34 #import <wtf/HashMap.h>
35 #import <wtf/RunLoop.h>
36 #import <wtf/Vector.h>
37
38 using WebKit::ProcessAndUIAssertion;
39
40 @interface WKProcessAssertionBackgroundTaskManager : NSObject
41
42 + (WKProcessAssertionBackgroundTaskManager *)shared;
43
44 - (void)addAssertionNeedingBackgroundTask:(ProcessAndUIAssertion&)assertion;
45 - (void)removeAssertionNeedingBackgroundTask:(ProcessAndUIAssertion&)assertion;
46
47 @end
48
49 @implementation WKProcessAssertionBackgroundTaskManager
50 {
51     UIBackgroundTaskIdentifier _backgroundTask;
52     HashSet<ProcessAndUIAssertion*> _assertionsNeedingBackgroundTask;
53 }
54
55 + (WKProcessAssertionBackgroundTaskManager *)shared
56 {
57     static WKProcessAssertionBackgroundTaskManager *shared = [WKProcessAssertionBackgroundTaskManager new];
58     return shared;
59 }
60
61 - (instancetype)init
62 {
63     self = [super init];
64     if (!self)
65         return nil;
66
67     _backgroundTask = UIBackgroundTaskInvalid;
68
69     return self;
70 }
71
72 - (void)dealloc
73 {
74     [self _releaseBackgroundTask];
75     [super dealloc];
76 }
77
78 - (void)addAssertionNeedingBackgroundTask:(ProcessAndUIAssertion&)assertion
79 {
80     _assertionsNeedingBackgroundTask.add(&assertion);
81     [self _updateBackgroundTask];
82 }
83
84 - (void)removeAssertionNeedingBackgroundTask:(ProcessAndUIAssertion&)assertion
85 {
86     _assertionsNeedingBackgroundTask.remove(&assertion);
87     [self _updateBackgroundTask];
88 }
89
90 - (void)_notifyAssertionsOfImminentSuspension
91 {
92     ASSERT(RunLoop::isMain());
93
94     for (auto* assertion : copyToVector(_assertionsNeedingBackgroundTask))
95         assertion->uiAssertionWillExpireImminently();
96 }
97
98 - (void)_updateBackgroundTask
99 {
100     if (!_assertionsNeedingBackgroundTask.isEmpty() && _backgroundTask == UIBackgroundTaskInvalid) {
101         RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - beginBackgroundTaskWithName", self);
102         _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.ProcessAssertion" expirationHandler:^{
103             RELEASE_LOG_ERROR(ProcessSuspension, "Background task expired while holding WebKit ProcessAssertion (isMainThread? %d).", RunLoop::isMain());
104             // The expiration handler gets called on a non-main thread when the underlying assertion could not be taken (rdar://problem/27278419).
105             if (RunLoop::isMain())
106                 [self _notifyAssertionsOfImminentSuspension];
107             else {
108                 dispatch_sync(dispatch_get_main_queue(), ^{
109                     [self _notifyAssertionsOfImminentSuspension];
110                 });
111             }
112             [self _releaseBackgroundTask];
113         }];
114     } else if (_assertionsNeedingBackgroundTask.isEmpty())
115         [self _releaseBackgroundTask];
116 }
117
118 - (void)_releaseBackgroundTask
119 {
120     if (_backgroundTask == UIBackgroundTaskInvalid)
121         return;
122
123     RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - endBackgroundTask", self);
124     [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
125     _backgroundTask = UIBackgroundTaskInvalid;
126 }
127
128 @end
129
130 namespace WebKit {
131
132 const BKSProcessAssertionFlags suspendedTabFlags = (BKSProcessAssertionAllowIdleSleep);
133 const BKSProcessAssertionFlags backgroundTabFlags = (BKSProcessAssertionAllowIdleSleep | BKSProcessAssertionPreventTaskSuspend);
134 const BKSProcessAssertionFlags foregroundTabFlags = (BKSProcessAssertionAllowIdleSleep | BKSProcessAssertionPreventTaskSuspend | BKSProcessAssertionWantsForegroundResourcePriority | BKSProcessAssertionPreventTaskThrottleDown);
135
136 static BKSProcessAssertionFlags flagsForState(AssertionState assertionState)
137 {
138     switch (assertionState) {
139     case AssertionState::Suspended:
140         return suspendedTabFlags;
141     case AssertionState::Background:
142     case AssertionState::UnboundedNetworking:
143         return backgroundTabFlags;
144     case AssertionState::Foreground:
145         return foregroundTabFlags;
146     }
147 }
148
149 static BKSProcessAssertionReason reasonForState(AssertionState assertionState)
150 {
151     switch (assertionState) {
152     case AssertionState::UnboundedNetworking:
153         return BKSProcessAssertionReasonFinishTaskUnbounded;
154     case AssertionState::Suspended:
155     case AssertionState::Background:
156     case AssertionState::Foreground:
157         return BKSProcessAssertionReasonExtension;
158     }
159 }
160
161 ProcessAssertion::ProcessAssertion(pid_t pid, const String& name, AssertionState assertionState)
162     : m_assertionState(assertionState)
163 {
164     auto weakThis = makeWeakPtr(*this);
165     BKSProcessAssertionAcquisitionHandler handler = ^(BOOL acquired) {
166         if (!acquired) {
167             RELEASE_LOG_ERROR(ProcessSuspension, " %p - ProcessAssertion() PID %d Unable to acquire assertion for process with PID %d", this, getpid(), pid);
168             dispatch_async(dispatch_get_main_queue(), ^{
169                 if (weakThis)
170                     processAssertionWasInvalidated();
171             });
172         }
173     };
174     RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion() PID %d acquiring assertion for process with PID %d, name '%s'", this, getpid(), pid, name.utf8().data());
175     
176     m_assertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:pid flags:flagsForState(assertionState) reason:reasonForState(assertionState) name:(NSString *)name withHandler:handler]);
177     m_assertion.get().invalidationHandler = ^() {
178         dispatch_async(dispatch_get_main_queue(), ^{
179             RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion() Process assertion for process with PID %d was invalidated", this, pid);
180             if (weakThis)
181                 processAssertionWasInvalidated();
182         });
183     };
184 }
185
186 ProcessAssertion::~ProcessAssertion()
187 {
188     m_assertion.get().invalidationHandler = nil;
189
190     RELEASE_LOG(ProcessSuspension, "%p - ~ProcessAssertion() Releasing process assertion", this);
191     [m_assertion invalidate];
192 }
193
194 void ProcessAssertion::processAssertionWasInvalidated()
195 {
196     ASSERT(RunLoop::isMain());
197     RELEASE_LOG_ERROR(ProcessSuspension, "%p - ProcessAssertion::processAssertionWasInvalidated()", this);
198
199     m_validity = Validity::No;
200 }
201
202 void ProcessAssertion::setState(AssertionState assertionState)
203 {
204     if (m_assertionState == assertionState)
205         return;
206
207     RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion::setState(%u) previousState: %u", this, static_cast<unsigned>(assertionState), static_cast<unsigned>(m_assertionState));
208     m_assertionState = assertionState;
209     [m_assertion setFlags:flagsForState(assertionState)];
210 }
211
212 void ProcessAndUIAssertion::updateRunInBackgroundCount()
213 {
214     bool shouldHoldBackgroundTask = validity() != Validity::No && state() != AssertionState::Suspended;
215     if (m_isHoldingBackgroundTask == shouldHoldBackgroundTask)
216         return;
217
218     if (shouldHoldBackgroundTask)
219         [[WKProcessAssertionBackgroundTaskManager shared] addAssertionNeedingBackgroundTask:*this];
220     else
221         [[WKProcessAssertionBackgroundTaskManager shared] removeAssertionNeedingBackgroundTask:*this];
222
223     m_isHoldingBackgroundTask = shouldHoldBackgroundTask;
224 }
225
226 ProcessAndUIAssertion::ProcessAndUIAssertion(pid_t pid, const String& reason, AssertionState assertionState)
227     : ProcessAssertion(pid, reason, assertionState)
228 {
229     updateRunInBackgroundCount();
230 }
231
232 ProcessAndUIAssertion::~ProcessAndUIAssertion()
233 {
234     if (m_isHoldingBackgroundTask)
235         [[WKProcessAssertionBackgroundTaskManager shared] removeAssertionNeedingBackgroundTask:*this];
236 }
237
238 void ProcessAndUIAssertion::setState(AssertionState assertionState)
239 {
240     ProcessAssertion::setState(assertionState);
241     updateRunInBackgroundCount();
242 }
243
244 void ProcessAndUIAssertion::uiAssertionWillExpireImminently()
245 {
246     if (auto* client = this->client())
247         client->uiAssertionWillExpireImminently();
248 }
249
250 void ProcessAndUIAssertion::processAssertionWasInvalidated()
251 {
252     ProcessAssertion::processAssertionWasInvalidated();
253     updateRunInBackgroundCount();
254 }
255
256 } // namespace WebKit
257
258 #endif // PLATFORM(IOS_FAMILY)