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

Reviewed by Geoffrey 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@245899 268f45cc-cd09-0410-ab3c-d52691b4dbfc

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

index 89da80e..e03dea1 100644 (file)
@@ -1,5 +1,25 @@
 2019-05-30  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 Geoffrey 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-05-30  Chris Dumez  <cdumez@apple.com>
+
         [iOS] Third-party extensions using WKWebView are unable to render anything
         https://bugs.webkit.org/show_bug.cgi?id=198359
         <rdar://problem/51105015>
index 2aca868..2b9ab03 100644 (file)
@@ -150,7 +150,17 @@ static const Seconds releaseBackgroundTaskAfterExpirationDelay { 2_s };
         }
         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());
+            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 _cancelPendingReleaseTask];
+                [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];