WeakPtrFactory should populate m_ref lazily.
[WebKit-https.git] / Source / WebKit / UIProcess / ios / ProcessAssertionIOS.mm
1 /*
2  * Copyright (C) 2014 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)
30
31 #import "AssertionServicesSPI.h"
32 #import "Logging.h"
33 #import <UIKit/UIApplication.h>
34 #import <wtf/HashSet.h>
35 #import <wtf/RunLoop.h>
36 #import <wtf/Vector.h>
37
38 #if !PLATFORM(IOS_SIMULATOR)
39
40 using WebKit::ProcessAssertionClient;
41
42 @interface WKProcessAssertionBackgroundTaskManager : NSObject
43
44 + (WKProcessAssertionBackgroundTaskManager *)shared;
45
46 - (void)incrementNeedsToRunInBackgroundCount;
47 - (void)decrementNeedsToRunInBackgroundCount;
48
49 - (void)addClient:(ProcessAssertionClient&)client;
50 - (void)removeClient:(ProcessAssertionClient&)client;
51
52 @end
53
54 @implementation WKProcessAssertionBackgroundTaskManager
55 {
56     unsigned _needsToRunInBackgroundCount;
57     UIBackgroundTaskIdentifier _backgroundTask;
58     HashSet<ProcessAssertionClient*> _clients;
59 }
60
61 + (WKProcessAssertionBackgroundTaskManager *)shared
62 {
63     static WKProcessAssertionBackgroundTaskManager *shared = [WKProcessAssertionBackgroundTaskManager new];
64     return shared;
65 }
66
67 - (instancetype)init
68 {
69     self = [super init];
70     if (!self)
71         return nil;
72
73     _backgroundTask = UIBackgroundTaskInvalid;
74
75     return self;
76 }
77
78 - (void)dealloc
79 {
80     if (_backgroundTask != UIBackgroundTaskInvalid)
81         [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
82
83     [super dealloc];
84 }
85
86 - (void)addClient:(ProcessAssertionClient&)client
87 {
88     _clients.add(&client);
89 }
90
91 - (void)removeClient:(ProcessAssertionClient&)client
92 {
93     _clients.remove(&client);
94 }
95
96 - (void)_notifyClientsOfImminentSuspension
97 {
98     ASSERT(RunLoop::isMain());
99     Vector<ProcessAssertionClient*> clientsToNotify;
100     copyToVector(_clients, clientsToNotify);
101     for (auto* client : clientsToNotify)
102         client->assertionWillExpireImminently();
103 }
104
105 - (void)_updateBackgroundTask
106 {
107     if (_needsToRunInBackgroundCount && _backgroundTask == UIBackgroundTaskInvalid) {
108         RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - beginBackgroundTaskWithName", self);
109         _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.ProcessAssertion" expirationHandler:^{
110             RELEASE_LOG_ERROR(ProcessSuspension, "Background task expired while holding WebKit ProcessAssertion (isMainThread? %d).", RunLoop::isMain());
111             // The expiration handler gets called on a non-main thread when the underlying assertion could not be taken (rdar://problem/27278419).
112             if (RunLoop::isMain())
113                 [self _notifyClientsOfImminentSuspension];
114             else {
115                 dispatch_sync(dispatch_get_main_queue(), ^{
116                     [self _notifyClientsOfImminentSuspension];
117                 });
118             }
119             [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
120             _backgroundTask = UIBackgroundTaskInvalid;
121         }];
122     }
123
124     if (!_needsToRunInBackgroundCount && _backgroundTask != UIBackgroundTaskInvalid) {
125         RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - endBackgroundTask", self);
126         [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
127         _backgroundTask = UIBackgroundTaskInvalid;
128     }
129 }
130
131 - (void)incrementNeedsToRunInBackgroundCount
132 {
133     ++_needsToRunInBackgroundCount;
134     [self _updateBackgroundTask];
135 }
136
137 - (void)decrementNeedsToRunInBackgroundCount
138 {
139     --_needsToRunInBackgroundCount;
140     [self _updateBackgroundTask];
141 }
142
143 @end
144
145 namespace WebKit {
146
147 const BKSProcessAssertionFlags suspendedTabFlags = (BKSProcessAssertionAllowIdleSleep);
148 const BKSProcessAssertionFlags backgroundTabFlags = (BKSProcessAssertionAllowIdleSleep | BKSProcessAssertionPreventTaskSuspend);
149 const BKSProcessAssertionFlags foregroundTabFlags = (BKSProcessAssertionAllowIdleSleep | BKSProcessAssertionPreventTaskSuspend | BKSProcessAssertionWantsForegroundResourcePriority | BKSProcessAssertionPreventTaskThrottleDown);
150
151 static BKSProcessAssertionFlags flagsForState(AssertionState assertionState)
152 {
153     switch (assertionState) {
154     case AssertionState::Suspended:
155         return suspendedTabFlags;
156     case AssertionState::Background:
157         return backgroundTabFlags;
158     case AssertionState::Foreground:
159         return foregroundTabFlags;
160     }
161 }
162
163 ProcessAssertion::ProcessAssertion(pid_t pid, AssertionState assertionState, Function<void()>&& invalidationCallback)
164     : m_invalidationCallback(WTFMove(invalidationCallback))
165     , m_assertionState(assertionState)
166 {
167     auto weakThis = createWeakPtr();
168     BKSProcessAssertionAcquisitionHandler handler = ^(BOOL acquired) {
169         if (!acquired) {
170             RELEASE_LOG_ERROR(ProcessSuspension, " %p - ProcessAssertion() Unable to acquire assertion for process with PID %d", this, pid);
171             ASSERT_NOT_REACHED();
172             dispatch_async(dispatch_get_main_queue(), ^{
173                 if (weakThis)
174                     markAsInvalidated();
175             });
176         }
177     };
178     RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion() Acquiring assertion for process with PID %d", this, pid);
179     m_assertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:pid flags:flagsForState(assertionState) reason:BKSProcessAssertionReasonExtension name:@"Web content visible" withHandler:handler]);
180     m_assertion.get().invalidationHandler = ^() {
181         dispatch_async(dispatch_get_main_queue(), ^{
182             RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion() Process assertion for process with PID %d was invalidated", this, pid);
183             if (weakThis)
184                 markAsInvalidated();
185         });
186     };
187 }
188
189 ProcessAssertion::~ProcessAssertion()
190 {
191     m_assertion.get().invalidationHandler = nil;
192
193     if (ProcessAssertionClient* client = this->client())
194         [[WKProcessAssertionBackgroundTaskManager shared] removeClient:*client];
195
196     RELEASE_LOG(ProcessSuspension, "%p - ~ProcessAssertion() Releasing process assertion", this);
197     [m_assertion invalidate];
198 }
199
200 void ProcessAssertion::markAsInvalidated()
201 {
202     ASSERT(RunLoop::isMain());
203
204     m_validity = Validity::No;
205     if (m_invalidationCallback)
206         m_invalidationCallback();
207 }
208
209 void ProcessAssertion::setState(AssertionState assertionState)
210 {
211     if (m_assertionState == assertionState)
212         return;
213
214     RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion::setState(%u)", this, static_cast<unsigned>(assertionState));
215     m_assertionState = assertionState;
216     [m_assertion setFlags:flagsForState(assertionState)];
217 }
218
219 void ProcessAndUIAssertion::updateRunInBackgroundCount()
220 {
221     bool shouldHoldBackgroundAssertion = validity() != Validity::No && state() != AssertionState::Suspended;
222
223     if (shouldHoldBackgroundAssertion) {
224         if (!m_isHoldingBackgroundAssertion)
225             [[WKProcessAssertionBackgroundTaskManager shared] incrementNeedsToRunInBackgroundCount];
226     } else {
227         if (m_isHoldingBackgroundAssertion)
228             [[WKProcessAssertionBackgroundTaskManager shared] decrementNeedsToRunInBackgroundCount];
229     }
230
231     m_isHoldingBackgroundAssertion = shouldHoldBackgroundAssertion;
232 }
233
234 ProcessAndUIAssertion::ProcessAndUIAssertion(pid_t pid, AssertionState assertionState)
235     : ProcessAssertion(pid, assertionState, [this] { updateRunInBackgroundCount(); })
236 {
237     updateRunInBackgroundCount();
238 }
239
240 ProcessAndUIAssertion::~ProcessAndUIAssertion()
241 {
242     if (m_isHoldingBackgroundAssertion)
243         [[WKProcessAssertionBackgroundTaskManager shared] decrementNeedsToRunInBackgroundCount];
244 }
245
246 void ProcessAndUIAssertion::setState(AssertionState assertionState)
247 {
248     ProcessAssertion::setState(assertionState);
249     updateRunInBackgroundCount();
250 }
251
252 void ProcessAndUIAssertion::setClient(ProcessAssertionClient& newClient)
253 {
254     [[WKProcessAssertionBackgroundTaskManager shared] addClient:newClient];
255     if (ProcessAssertionClient* oldClient = this->client())
256         [[WKProcessAssertionBackgroundTaskManager shared] removeClient:*oldClient];
257     ProcessAssertion::setClient(newClient);
258 }
259
260 } // namespace WebKit
261
262 #else // PLATFORM(IOS_SIMULATOR)
263
264 namespace WebKit {
265
266 ProcessAssertion::ProcessAssertion(pid_t, AssertionState assertionState, Function<void()>&&)
267     : m_assertionState(assertionState)
268 {
269 }
270
271 ProcessAssertion::~ProcessAssertion()
272 {
273 }
274
275 void ProcessAssertion::setState(AssertionState assertionState)
276 {
277     m_assertionState = assertionState;
278 }
279
280 ProcessAndUIAssertion::ProcessAndUIAssertion(pid_t pid, AssertionState assertionState)
281     : ProcessAssertion(pid, assertionState)
282 {
283 }
284
285 ProcessAndUIAssertion::~ProcessAndUIAssertion()
286 {
287 }
288
289 void ProcessAndUIAssertion::setState(AssertionState assertionState)
290 {
291     ProcessAssertion::setState(assertionState);
292 }
293
294 void ProcessAndUIAssertion::setClient(ProcessAssertionClient& newClient)
295 {
296     ProcessAssertion::setClient(newClient);
297 }
298
299 } // namespace WebKit
300
301 #endif // PLATFORM(IOS_SIMULATOR)
302
303 #endif // PLATFORM(IOS)