Remove macOS Sierra results in LayoutTests/platform
[WebKit-https.git] / Source / WebKit / UIProcess / ios / ProcessAssertionIOS.mm
index 0c9adf3..37a6982 100644 (file)
@@ -30,6 +30,7 @@
 
 #import "AssertionServicesSPI.h"
 #import "Logging.h"
+#import "WebProcessPool.h"
 #import <UIKit/UIApplication.h>
 #import <wtf/HashMap.h>
 #import <wtf/RunLoop.h>
 
 using WebKit::ProcessAndUIAssertion;
 
+// This gives some time to our child processes to process the ProcessWillSuspendImminently IPC but makes sure we release
+// the background task before the UIKit timeout (We get killed if we do not release the background task within 5 seconds
+// on the expiration handler getting called).
+static const Seconds releaseBackgroundTaskAfterExpirationDelay { 2_s };
+
 @interface WKProcessAssertionBackgroundTaskManager : NSObject
 
 + (WKProcessAssertionBackgroundTaskManager *)shared;
@@ -50,6 +56,8 @@ using WebKit::ProcessAndUIAssertion;
 {
     UIBackgroundTaskIdentifier _backgroundTask;
     HashSet<ProcessAndUIAssertion*> _assertionsNeedingBackgroundTask;
+    BOOL _applicationIsBackgrounded;
+    dispatch_block_t _pendingTaskReleaseTask;
 }
 
 + (WKProcessAssertionBackgroundTaskManager *)shared
@@ -66,6 +74,19 @@ using WebKit::ProcessAndUIAssertion;
 
     _backgroundTask = UIBackgroundTaskInvalid;
 
+    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication] queue:nil usingBlock:^(NSNotification *) {
+        _applicationIsBackgrounded = NO;
+        [self _cancelPendingReleaseTask];
+        [self _updateBackgroundTask];
+    }];
+
+    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication] queue:nil usingBlock:^(NSNotification *) {
+        _applicationIsBackgrounded = YES;
+        
+        if (_backgroundTask == UIBackgroundTaskInvalid)
+            WebKit::WebProcessPool::notifyProcessPoolsApplicationIsAboutToSuspend();
+    }];
+
     return self;
 }
 
@@ -95,12 +116,56 @@ using WebKit::ProcessAndUIAssertion;
         assertion->uiAssertionWillExpireImminently();
 }
 
+
+- (void)_scheduleReleaseTask
+{
+    ASSERT(!_pendingTaskReleaseTask);
+    if (_pendingTaskReleaseTask)
+        return;
+
+    RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - _scheduleReleaseTask because the expiration handler has been called", self);
+    _pendingTaskReleaseTask = dispatch_block_create((dispatch_block_flags_t)0, ^{
+        _pendingTaskReleaseTask = nil;
+        [self _releaseBackgroundTask];
+    });
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, releaseBackgroundTaskAfterExpirationDelay.value() * NSEC_PER_SEC), dispatch_get_main_queue(), _pendingTaskReleaseTask);
+#if !__has_feature(objc_arc)
+    // dispatch_async() does a Block_copy() / Block_release() on behalf of the caller.
+    Block_release(_pendingTaskReleaseTask);
+#endif
+}
+
+- (void)_cancelPendingReleaseTask
+{
+    if (!_pendingTaskReleaseTask)
+        return;
+
+    RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - _cancelPendingReleaseTask because the application is foreground again", self);
+    dispatch_block_cancel(_pendingTaskReleaseTask);
+    _pendingTaskReleaseTask = nil;
+}
+
 - (void)_updateBackgroundTask
 {
     if (!_assertionsNeedingBackgroundTask.isEmpty() && _backgroundTask == UIBackgroundTaskInvalid) {
+        if (_applicationIsBackgrounded) {
+            RELEASE_LOG_ERROR(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager: Ignored request to start a new background task because the application is already in the background", self);
+            return;
+        }
         RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - beginBackgroundTaskWithName", self);
         _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.ProcessAssertion" expirationHandler:^{
             RELEASE_LOG_ERROR(ProcessSuspension, "Background task expired while holding WebKit ProcessAssertion (isMainThread? %d).", RunLoop::isMain());
+            if (!_applicationIsBackgrounded) {
+                // We've received the invalidation warning after the app has become foreground again. In this case, we should not
+                // warn clients of imminent suspension. To be safe (avoid potential killing), we end the task right away and call
+                // _updateBackgroundTask asynchronously to start a new task if necessary.
+                [self _releaseBackgroundTask];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [self _updateBackgroundTask];
+                });
+                return;
+            }
+
             // The expiration handler gets called on a non-main thread when the underlying assertion could not be taken (rdar://problem/27278419).
             if (RunLoop::isMain())
                 [self _notifyAssertionsOfImminentSuspension];
@@ -109,7 +174,8 @@ using WebKit::ProcessAndUIAssertion;
                     [self _notifyAssertionsOfImminentSuspension];
                 });
             }
-            [self _releaseBackgroundTask];
+
+            [self _scheduleReleaseTask];
         }];
     } else if (_assertionsNeedingBackgroundTask.isEmpty())
         [self _releaseBackgroundTask];
@@ -121,6 +187,8 @@ using WebKit::ProcessAndUIAssertion;
         return;
 
     RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - endBackgroundTask", self);
+    if (_applicationIsBackgrounded)
+        WebKit::WebProcessPool::notifyProcessPoolsApplicationIsAboutToSuspend();
     [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
     _backgroundTask = UIBackgroundTaskInvalid;
 }
@@ -167,6 +235,8 @@ static BKSProcessAssertionReason toBKSProcessAssertionReason(AssertionReason rea
         return BKSProcessAssertionReasonFinishTask;
     case AssertionReason::FinishTaskUnbounded:
         return BKSProcessAssertionReasonFinishTaskUnbounded;
+    case AssertionReason::MediaPlayback:
+        return BKSProcessAssertionReasonMediaPlayback;
     }
 }