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 <AssertionServices/BKSProcessAssertion.h> 32#import <UIKit/UIApplication.h> 33 34#if !PLATFORM(IOS_SIMULATOR) 35 36@interface WKProcessAssertionBackgroundTaskManager : NSObject 37 38+ (WKProcessAssertionBackgroundTaskManager *)shared; 39 40- (void)incrementNeedsToRunInBackgroundCount; 41- (void)decrementNeedsToRunInBackgroundCount; 42 43@end 44 45@implementation WKProcessAssertionBackgroundTaskManager 46{ 47 unsigned _needsToRunInBackgroundCount; 48 BOOL _appIsBackground; 49 UIBackgroundTaskIdentifier _backgroundTask; 50} 51 52+ (WKProcessAssertionBackgroundTaskManager *)shared 53{ 54 static WKProcessAssertionBackgroundTaskManager *shared = [WKProcessAssertionBackgroundTaskManager new]; 55 return shared; 56} 57 58- (instancetype)init 59{ 60 self = [super init]; 61 if (!self) 62 return nil; 63 64 _appIsBackground = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground; 65 _backgroundTask = UIBackgroundTaskInvalid; 66 67 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 68 [center addObserver:self selector:@selector(_applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; 69 [center addObserver:self selector:@selector(_applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; 70 71 return self; 72} 73 74- (void)dealloc 75{ 76 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 77 [center removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; 78 [center removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; 79 80 if (_backgroundTask != UIBackgroundTaskInvalid) 81 [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; 82 83 [super dealloc]; 84} 85 86- (void)_updateBackgroundTask 87{ 88 bool shouldHoldTask = _needsToRunInBackgroundCount && _appIsBackground; 89 90 if (shouldHoldTask && _backgroundTask == UIBackgroundTaskInvalid) { 91 _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.ProcessAssertion" expirationHandler:^{ 92 NSLog(@"Background task expired while holding WebKit ProcessAssertion."); 93 [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; 94 _backgroundTask = UIBackgroundTaskInvalid; 95 }]; 96 } 97 98 if (!shouldHoldTask && _backgroundTask != UIBackgroundTaskInvalid) { 99 [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; 100 _backgroundTask = UIBackgroundTaskInvalid; 101 } 102} 103 104- (void)_applicationWillEnterForeground:(NSNotification *)notification 105{ 106 _appIsBackground = NO; 107 [self _updateBackgroundTask]; 108} 109 110- (void)_applicationDidEnterBackground:(NSNotification *)notification 111{ 112 _appIsBackground = YES; 113 [self _updateBackgroundTask]; 114} 115 116- (void)incrementNeedsToRunInBackgroundCount 117{ 118 ++_needsToRunInBackgroundCount; 119 [self _updateBackgroundTask]; 120} 121 122- (void)decrementNeedsToRunInBackgroundCount 123{ 124 --_needsToRunInBackgroundCount; 125 [self _updateBackgroundTask]; 126} 127 128@end 129 130namespace WebKit { 131 132const BKSProcessAssertionFlags suspendedTabFlags = (BKSProcessAssertionAllowIdleSleep); 133const BKSProcessAssertionFlags backgroundTabFlags = (BKSProcessAssertionAllowIdleSleep | BKSProcessAssertionPreventTaskSuspend | BKSProcessAssertionAllowSuspendOnSleep); 134const BKSProcessAssertionFlags foregroundTabFlags = (BKSProcessAssertionAllowIdleSleep | BKSProcessAssertionPreventTaskSuspend | BKSProcessAssertionAllowSuspendOnSleep | BKSProcessAssertionWantsForegroundResourcePriority | BKSProcessAssertionPreventTaskThrottleDown); 135 136static BKSProcessAssertionFlags flagsForState(AssertionState assertionState) 137{ 138 switch (assertionState) { 139 case AssertionState::Suspended: 140 return suspendedTabFlags; 141 case AssertionState::Background: 142 return backgroundTabFlags; 143 case AssertionState::Foreground: 144 return foregroundTabFlags; 145 } 146} 147 148ProcessAssertion::ProcessAssertion(pid_t pid, AssertionState assertionState) 149{ 150 m_assertionState = assertionState; 151 152 BKSProcessAssertionAcquisitionHandler handler = ^(BOOL acquired) { 153 if (!acquired) { 154 LOG_ERROR("Unable to acquire assertion for process %d", pid); 155 ASSERT_NOT_REACHED(); 156 } 157 }; 158 m_assertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:pid flags:flagsForState(assertionState) reason:BKSProcessAssertionReasonExtension name:@"Web content visible" withHandler:handler]); 159} 160 161void ProcessAssertion::setState(AssertionState assertionState) 162{ 163 if (m_assertionState == assertionState) 164 return; 165 166 m_assertionState = assertionState; 167 [m_assertion setFlags:flagsForState(assertionState)]; 168} 169 170ProcessAndUIAssertion::ProcessAndUIAssertion(pid_t pid, AssertionState assertionState) 171 : ProcessAssertion(pid, assertionState) 172{ 173 if (assertionState != AssertionState::Suspended) 174 [[WKProcessAssertionBackgroundTaskManager shared] incrementNeedsToRunInBackgroundCount]; 175} 176 177ProcessAndUIAssertion::~ProcessAndUIAssertion() 178{ 179 if (state() != AssertionState::Suspended) 180 [[WKProcessAssertionBackgroundTaskManager shared] decrementNeedsToRunInBackgroundCount]; 181} 182 183void ProcessAndUIAssertion::setState(AssertionState assertionState) 184{ 185 if ((state() == AssertionState::Suspended) && (assertionState != AssertionState::Suspended)) 186 [[WKProcessAssertionBackgroundTaskManager shared] incrementNeedsToRunInBackgroundCount]; 187 if ((state() != AssertionState::Suspended) && (assertionState == AssertionState::Suspended)) 188 [[WKProcessAssertionBackgroundTaskManager shared] decrementNeedsToRunInBackgroundCount]; 189 190 ProcessAssertion::setState(assertionState); 191} 192 193} // namespace WebKit 194 195#else // PLATFORM(IOS_SIMULATOR) 196 197namespace WebKit { 198 199ProcessAssertion::ProcessAssertion(pid_t, AssertionState assertionState) 200 : m_assertionState(assertionState) 201{ 202} 203 204void ProcessAssertion::setState(AssertionState assertionState) 205{ 206 m_assertionState = assertionState; 207} 208 209ProcessAndUIAssertion::ProcessAndUIAssertion(pid_t pid, AssertionState assertionState) 210 : ProcessAssertion(pid, assertionState) 211{ 212} 213 214ProcessAndUIAssertion::~ProcessAndUIAssertion() 215{ 216} 217 218void ProcessAndUIAssertion::setState(AssertionState assertionState) 219{ 220 ProcessAssertion::setState(assertionState); 221} 222 223} // namespace WebKit 224 225#endif // PLATFORM(IOS_SIMULATOR) 226 227#endif // PLATFORM(IOS) 228