[iOS] UIProcess' background task expiration handler may get called after the app...
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Jun 2019 15:46:45 +0000 (15:46 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Jun 2019 15:46:45 +0000 (15:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198380
<rdar://problem/49762471>

Reviewed by Geoff Garen.

UIProcess' background task expiration handler may get called after the app is foreground again. When
this happens, we already have a foreground assertion on behalf of the WebContent process, and the view
is visible. We would send the WillSuspendImminently IPC to the WebContent process, which would freeze
its layers, even though it is visible on screen.

To address the issue, we now check if the app is visible in the expiration handler. If it is visible,
we do not notify clients of imminent suspension. Instead, we end the background task right away and
call _updateBackgroundTask asynchronously to start a new background task if necessary.

* UIProcess/ios/ProcessAssertionIOS.mm:
(-[WKProcessAssertionBackgroundTaskManager _updateBackgroundTask]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@246064 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm

index 09f4b3b..719cc82 100644 (file)
@@ -1,3 +1,23 @@
+2019-06-04  Chris Dumez  <cdumez@apple.com>
+
+        [iOS] UIProcess' background task expiration handler may get called after the app is foreground again
+        https://bugs.webkit.org/show_bug.cgi?id=198380
+        <rdar://problem/49762471>
+
+        Reviewed by Geoff Garen.
+
+        UIProcess' background task expiration handler may get called after the app is foreground again. When
+        this happens, we already have a foreground assertion on behalf of the WebContent process, and the view
+        is visible. We would send the WillSuspendImminently IPC to the WebContent process, which would freeze
+        its layers, even though it is visible on screen.
+
+        To address the issue, we now check if the app is visible in the expiration handler. If it is visible,
+        we do not notify clients of imminent suspension. Instead, we end the background task right away and
+        call _updateBackgroundTask asynchronously to start a new background task if necessary.
+
+        * UIProcess/ios/ProcessAssertionIOS.mm:
+        (-[WKProcessAssertionBackgroundTaskManager _updateBackgroundTask]):
+
 2019-06-04  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK] Crash when re-entering AC mode after r245957
index f852b22..f5d2f64 100644 (file)
@@ -197,6 +197,7 @@ static bool isBackgroundState(BKSApplicationState state)
 - (void)_backgroundTaskExpired
 {
     RELEASE_LOG_ERROR(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - _backgroundTaskExpired", self);
+    ASSERT(_applicationIsBackgrounded);
     [self _cancelTimeoutTask];
 
     // Tell our child processes they will suspend imminently.
@@ -222,6 +223,17 @@ static bool isBackgroundState(BKSApplicationState state)
         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, _applicationIsBackgrounded? %d).", RunLoop::isMain(), _applicationIsBackgrounded);
+            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;
+            }
+            
             [self _backgroundTaskExpired];
         }];
     } else if (_assertionsNeedingBackgroundTask.isEmpty())