[Cocoa] Awaken UIProcess if WebContent process is awakened from suspensions unexpectedly.
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Apr 2019 19:37:28 +0000 (19:37 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Apr 2019 19:37:28 +0000 (19:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196659

Reviewed by Chris Dumez.

* Platform/IPC/Connection.h:
(IPC::Connection::sendWithAsyncReply):
* Platform/spi/ios/AssertionServicesSPI.h:
* Shared/Cocoa/ProcessTaskStateObserver.h: Added.
(WebKit::ProcessTaskStateObserver::setClient):
(WebKit::ProcessTaskStateObserver::client):
(WebKit::ProcessTaskStateObserver::taskState const):
* Shared/Cocoa/ProcessTaskStateObserver.mm: Added.
(-[WKProcessTaskStateObserverDelegate process:taskStateDidChange:]):
(WebKit::toProcessTaskStateObserverTaskState):
(WebKit::ProcessTaskStateObserver::ProcessTaskStateObserver):
(WebKit::ProcessTaskStateObserver::~ProcessTaskStateObserver):
(WebKit::ProcessTaskStateObserver::setTaskState):
* UIProcess/Cocoa/WebProcessProxyCocoa.mm:
(WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
* UIProcess/ProcessAssertion.h:
* UIProcess/ProcessThrottler.cpp:
(WebKit::ProcessThrottler::updateAssertion):
* UIProcess/ProcessThrottler.h:
(WebKit::ProcessThrottler::shouldBeRunnable const):
* UIProcess/WebProcessProxy.h:
* UIProcess/WebProcessProxy.messages.in:
* UIProcess/ios/ProcessAssertionIOS.mm:
(WebKit::reasonForState):
(WebKit::toBKSProcessAssertionReason):
(WebKit::ProcessAssertion::ProcessAssertion):
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::actualPrepareToSuspend):
(WebKit::WebProcess::cancelPrepareToSuspend):
(WebKit::WebProcess::processDidResume):
* WebProcess/WebProcess.h:
* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::processTaskStateDidChange):

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

17 files changed:
Source/WebKit/ChangeLog
Source/WebKit/Platform/IPC/Connection.h
Source/WebKit/Platform/spi/ios/AssertionServicesSPI.h
Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.h [new file with mode: 0644]
Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm
Source/WebKit/UIProcess/ProcessAssertion.cpp
Source/WebKit/UIProcess/ProcessAssertion.h
Source/WebKit/UIProcess/ProcessThrottler.cpp
Source/WebKit/UIProcess/ProcessThrottler.h
Source/WebKit/UIProcess/WebProcessProxy.h
Source/WebKit/UIProcess/WebProcessProxy.messages.in
Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/WebProcess.cpp
Source/WebKit/WebProcess/WebProcess.h
Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm

index 0e27003..6a714ea 100644 (file)
@@ -1,3 +1,45 @@
+2019-04-09  Jer Noble  <jer.noble@apple.com>
+
+        [Cocoa] Awaken UIProcess if WebContent process is awakened from suspensions unexpectedly.
+        https://bugs.webkit.org/show_bug.cgi?id=196659
+
+        Reviewed by Chris Dumez.
+
+        * Platform/IPC/Connection.h:
+        (IPC::Connection::sendWithAsyncReply):
+        * Platform/spi/ios/AssertionServicesSPI.h:
+        * Shared/Cocoa/ProcessTaskStateObserver.h: Added.
+        (WebKit::ProcessTaskStateObserver::setClient):
+        (WebKit::ProcessTaskStateObserver::client):
+        (WebKit::ProcessTaskStateObserver::taskState const):
+        * Shared/Cocoa/ProcessTaskStateObserver.mm: Added.
+        (-[WKProcessTaskStateObserverDelegate process:taskStateDidChange:]):
+        (WebKit::toProcessTaskStateObserverTaskState):
+        (WebKit::ProcessTaskStateObserver::ProcessTaskStateObserver):
+        (WebKit::ProcessTaskStateObserver::~ProcessTaskStateObserver):
+        (WebKit::ProcessTaskStateObserver::setTaskState):
+        * UIProcess/Cocoa/WebProcessProxyCocoa.mm:
+        (WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
+        * UIProcess/ProcessAssertion.h:
+        * UIProcess/ProcessThrottler.cpp:
+        (WebKit::ProcessThrottler::updateAssertion):
+        * UIProcess/ProcessThrottler.h:
+        (WebKit::ProcessThrottler::shouldBeRunnable const):
+        * UIProcess/WebProcessProxy.h:
+        * UIProcess/WebProcessProxy.messages.in:
+        * UIProcess/ios/ProcessAssertionIOS.mm:
+        (WebKit::reasonForState):
+        (WebKit::toBKSProcessAssertionReason):
+        (WebKit::ProcessAssertion::ProcessAssertion):
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::actualPrepareToSuspend):
+        (WebKit::WebProcess::cancelPrepareToSuspend):
+        (WebKit::WebProcess::processDidResume):
+        * WebProcess/WebProcess.h:
+        * WebProcess/cocoa/WebProcessCocoa.mm:
+        (WebKit::WebProcess::processTaskStateDidChange):
+
 2019-04-09  Alex Christensen  <achristensen@webkit.org>
 
         Clicking "Go Back" from a safe browsing warning from an iframe should navigate the WKWebView back to the previous page
index f3739b4..36bd912 100644 (file)
@@ -177,7 +177,7 @@ public:
 
     void postConnectionDidCloseOnConnectionWorkQueue();
 
-    template<typename T, typename... Args> void sendWithAsyncReply(T&& message, CompletionHandler<void(Args...)>&& args, uint64_t destinationID = 0);
+    template<typename T, typename C> void sendWithAsyncReply(T&& message, C&& completionHandler, uint64_t destinationID = 0);
     template<typename T> bool send(T&& message, uint64_t destinationID, OptionSet<SendOption> sendOptions = { });
     template<typename T> void sendWithReply(T&& message, uint64_t destinationID, FunctionDispatcher& replyDispatcher, Function<void(Optional<typename CodingType<typename T::Reply>::Type>)>&& replyHandler);
     template<typename T> bool sendSync(T&& message, typename T::Reply&& reply, uint64_t destinationID, Seconds timeout = Seconds::infinity(), OptionSet<SendSyncOption> sendSyncOptions = { });
@@ -416,8 +416,8 @@ uint64_t nextAsyncReplyHandlerID();
 void addAsyncReplyHandler(Connection&, uint64_t, CompletionHandler<void(Decoder*)>&&);
 CompletionHandler<void(Decoder*)> takeAsyncReplyHandler(Connection&, uint64_t);
 
-template<typename T, typename... Args>
-void Connection::sendWithAsyncReply(T&& message, CompletionHandler<void(Args...)>&& completionHandler, uint64_t destinationID)
+template<typename T, typename C>
+void Connection::sendWithAsyncReply(T&& message, C&& completionHandler, uint64_t destinationID)
 {
     COMPILE_ASSERT(!T::isSync, AsyncMessageExpected);
 
index fae2a11..6ce6f2d 100644 (file)
@@ -28,6 +28,7 @@
 #if USE(APPLE_INTERNAL_SDK)
 
 #import <AssertionServices/BKSApplicationStateMonitor.h>
+#import <AssertionServices/BKSProcess.h>
 #import <AssertionServices/BKSProcessAssertion.h>
 
 #else
@@ -68,6 +69,7 @@ enum {
 typedef uint32_t BKSProcessAssertionFlags;
 
 enum {
+    BKSProcessAssertionReasonFinishTask = 4,
     BKSProcessAssertionReasonExtension = 13,
     BKSProcessAssertionReasonFinishTaskUnbounded = 10004,
 };
@@ -87,4 +89,27 @@ typedef void (^BKSProcessAssertionAcquisitionHandler)(BOOL acquired);
 - (void)invalidate;
 @end
 
+enum {
+    BKSProcessTaskStateNone,
+    BKSProcessTaskStateRunning,
+    BKSProcessTaskStateSuspended,
+};
+typedef uint32_t BKSProcessTaskState;
+
+@class BKSProcess;
+
+@protocol BKSProcessDelegate <NSObject>
+@optional
+- (void)process:(BKSProcess *)process taskStateDidChange:(BKSProcessTaskState)newState;
+@end
+
+@interface BKSProcess : NSObject
+@end
+
+@interface BKSProcess ()
++ (BKSProcess *)currentProcess;
+@property (nonatomic, readwrite, weak) id <BKSProcessDelegate> delegate;
+@property (nonatomic, readonly, assign) BKSProcessTaskState taskState;
+@end
+
 #endif
diff --git a/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.h b/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.h
new file mode 100644 (file)
index 0000000..1f96094
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if PLATFORM(IOS_FAMILY)
+
+#include <wtf/RetainPtr.h>
+
+OBJC_CLASS WKProcessTaskStateObserverDelegate;
+OBJC_CLASS BKSProcess;
+
+namespace WebKit {
+
+class ProcessTaskStateObserver {
+public:
+    class Client;
+
+    ProcessTaskStateObserver();
+    explicit ProcessTaskStateObserver(Client&);
+    ~ProcessTaskStateObserver();
+    
+    enum TaskState {
+        None,
+        Running,
+        Suspended,
+    };
+
+    class Client {
+    public:
+        virtual ~Client() = default;
+        virtual void processTaskStateDidChange(TaskState) = 0;
+    };
+
+    void setClient(Client& client) { m_client = &client; }
+    Client* client() { return m_client; }
+
+    TaskState taskState() const { return m_taskState; }
+
+private:
+    void setTaskState(TaskState);
+
+    Client* m_client { nullptr };
+    TaskState m_taskState { None };
+    RetainPtr<BKSProcess> m_process;
+    RetainPtr<WKProcessTaskStateObserverDelegate> m_delegate;
+};
+
+}
+
+#endif // PLATFORM(IOS_FAMILY)
diff --git a/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.mm b/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.mm
new file mode 100644 (file)
index 0000000..77c52ba
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "ProcessTaskStateObserver.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#import "AssertionServicesSPI.h"
+#import "Logging.h"
+#import <wtf/Function.h>
+#import <wtf/SoftLinking.h>
+
+SOFT_LINK_PRIVATE_FRAMEWORK(AssertionServices);
+SOFT_LINK_CLASS(AssertionServices, BKSProcess);
+
+typedef void(^TaskStateChangedCallbackType)(BKSProcessTaskState);
+
+@interface WKProcessTaskStateObserverDelegate : NSObject<BKSProcessDelegate>
+@property (copy) TaskStateChangedCallbackType taskStateChangedCallback;
+@end
+
+@implementation WKProcessTaskStateObserverDelegate
+- (void)process:(BKSProcess *)process taskStateDidChange:(BKSProcessTaskState)newState
+{
+    RELEASE_LOG(ProcessSuspension, "%p -[WKProcessTaskStateObserverDelegate process:taskStateDidChange:], process(%p), newState(%d)", self, process, (int)newState);
+
+    if (self.taskStateChangedCallback)
+        self.taskStateChangedCallback(newState);
+}
+@end
+
+namespace WebKit {
+
+static ProcessTaskStateObserver::TaskState toProcessTaskStateObserverTaskState(BKSProcessTaskState state)
+{
+    static_assert(static_cast<uint32_t>(BKSProcessTaskStateNone) == static_cast<uint32_t>(ProcessTaskStateObserver::None), "BKSProcessTaskState != ProcessTaskStateObserver::TaskState");
+    static_assert(static_cast<uint32_t>(BKSProcessTaskStateRunning) == static_cast<uint32_t>(ProcessTaskStateObserver::Running), "BKSProcessTaskState != ProcessTaskStateObserver::TaskState");
+    static_assert(static_cast<uint32_t>(BKSProcessTaskStateSuspended) == static_cast<uint32_t>(ProcessTaskStateObserver::Suspended), "BKSProcessTaskState != ProcessTaskStateObserver::TaskState");
+    return static_cast<ProcessTaskStateObserver::TaskState>(state);
+}
+
+ProcessTaskStateObserver::ProcessTaskStateObserver()
+    : m_process([getBKSProcessClass() currentProcess])
+    , m_delegate(adoptNS([[WKProcessTaskStateObserverDelegate alloc] init]))
+{
+    RELEASE_LOG(ProcessSuspension, "%p - ProcessTaskStateObserver::ProcessTaskStateObserver(), m_process(%p)", this, m_process.get());
+    m_delegate.get().taskStateChangedCallback = [this] (BKSProcessTaskState state) {
+        setTaskState(toProcessTaskStateObserverTaskState(state));
+    };
+    m_process.get().delegate = m_delegate.get();
+}
+
+ProcessTaskStateObserver::ProcessTaskStateObserver(Client& client)
+    : ProcessTaskStateObserver()
+{
+    setClient(client);
+}
+
+ProcessTaskStateObserver::~ProcessTaskStateObserver()
+{
+    m_delegate.get().taskStateChangedCallback = nil;
+}
+
+void ProcessTaskStateObserver::setTaskState(TaskState state)
+{
+    if (m_taskState == state)
+        return;
+
+    m_taskState = state;
+    if (m_client)
+        m_client->processTaskStateDidChange(state);
+}
+
+}
+
+#endif // PLATFORM(IOS_FAMILY)
+
index ca6955b..7babdeb 100644 (file)
@@ -41,6 +41,8 @@
 
 namespace WebKit {
 
+static const Seconds unexpectedActivityDuration = 10_s;
+
 const HashSet<String>& WebProcessProxy::platformPathsWithAssumedReadAccess()
 {
     static NeverDestroyed<HashSet<String>> platformPathsWithAssumedReadAccess(std::initializer_list<String> {
@@ -187,4 +189,25 @@ void WebProcessProxy::releaseHighPerformanceGPU()
 }
 #endif
 
+#if PLATFORM(IOS_FAMILY)
+void WebProcessProxy::processWasUnexpectedlyUnsuspended(CompletionHandler<void()>&& completion)
+{
+    if (m_throttler.shouldBeRunnable()) {
+        // The process becoming unsuspended was not unexpected; it likely was notified of its running state
+        // before receiving a procsessDidResume() message from the UIProcess.
+        completion();
+        return;
+    }
+
+    // The WebProcess was awakened by something other than the UIProcess. Take out an assertion for a
+    // limited duration to allow whatever task needs to be accomplished time to complete.
+    RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::processWasUnexpectedlyUnsuspended()", this);
+    auto backgroundActivityTimeoutHandler = [activityToken = m_throttler.backgroundActivityToken(), weakThis = makeWeakPtr(this)] {
+        RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::processWasUnexpectedlyUnsuspended() - lambda, background activity timed out", weakThis.get());
+    };
+    m_unexpectedActivityTimer = std::make_unique<WebCore::DeferrableOneShotTimer>(WTFMove(backgroundActivityTimeoutHandler), unexpectedActivityDuration);
+    completion();
+}
+#endif
+
 }
index e325d10..edb06d3 100644 (file)
@@ -35,6 +35,11 @@ ProcessAssertion::ProcessAssertion(ProcessID, const String&, AssertionState asse
 {
 }
 
+ProcessAssertion::ProcessAssertion(pid_t pid, const String& name, AssertionState assertionState, AssertionReason)
+    : m_assertionState(assertionState)
+{
+}
+
 ProcessAssertion::~ProcessAssertion() = default;
 
 void ProcessAssertion::setState(AssertionState assertionState)
index 47cc80f..a01c76d 100644 (file)
@@ -45,7 +45,13 @@ enum class AssertionState {
     Suspended,
     Background,
     UnboundedNetworking,
-    Foreground
+    Foreground,
+};
+
+enum class AssertionReason {
+    Extension,
+    FinishTask,
+    FinishTaskUnbounded,
 };
 
 class ProcessAssertion : public CanMakeWeakPtr<ProcessAssertion> {
@@ -58,6 +64,7 @@ public:
     };
 
     ProcessAssertion(ProcessID, const String& reason, AssertionState);
+    ProcessAssertion(ProcessID, const String& reason, AssertionState, AssertionReason);
     virtual ~ProcessAssertion();
 
     void setClient(Client& client) { m_client = &client; }
index b612da0..00a9918 100644 (file)
@@ -68,7 +68,7 @@ void ProcessThrottler::updateAssertionNow()
     
 void ProcessThrottler::updateAssertion()
 {
-    bool shouldBeRunnable = m_foregroundCounter.value() || m_backgroundCounter.value();
+    bool shouldBeRunnable = this->shouldBeRunnable();
 
     // If the process is currently runnable but will be suspended then first give it a chance to complete what it was doing
     // and clean up - move it to the background and send it a message to notify. Schedule a timeout so it can't stay running
index 0576e38..fd1529c 100644 (file)
@@ -60,6 +60,7 @@ public:
     void didConnectToProcess(ProcessID);
     void processReadyToSuspend();
     void didCancelProcessSuspension();
+    bool shouldBeRunnable() const { return m_foregroundCounter.value() || m_backgroundCounter.value(); }
 
 private:
     AssertionState assertionState();
index a9a993d..1284aea 100644 (file)
@@ -57,6 +57,7 @@ class PageConfiguration;
 }
 
 namespace WebCore {
+class DeferrableOneShotTimer;
 class ResourceRequest;
 struct PluginInfo;
 struct SecurityOriginData;
@@ -301,6 +302,10 @@ public:
     LayerHostingContextID contextIDForVisibilityPropagation() { return m_contextIDForVisibilityPropagation; }
 #endif
 
+#if PLATFORM(IOS_FAMILY)
+    void processWasUnexpectedlyUnsuspended(CompletionHandler<void()>&&);
+#endif
+
 protected:
     static uint64_t generatePageID();
     WebProcessProxy(WebProcessPool&, WebsiteDataStore*, IsPrewarmed);
@@ -448,6 +453,7 @@ private:
     ForegroundWebProcessToken m_foregroundToken;
     BackgroundWebProcessToken m_backgroundToken;
     bool m_hasSentMessageToUnblockAccessibilityServer { false };
+    std::unique_ptr<WebCore::DeferrableOneShotTimer> m_unexpectedActivityTimer;
 #endif
 
 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
index 86cf19a..1e0a67c 100644 (file)
@@ -83,4 +83,8 @@ messages -> WebProcessProxy LegacyReceiver {
 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
     DidCreateContextForVisibilityPropagation(WebKit::LayerHostingContextID contextID);
 #endif
+
+#if PLATFORM(IOS_FAMILY)
+    ProcessWasUnexpectedlyUnsuspended() -> () Async
+#endif
 }
index 8c4de3a..b8e6cfd 100644 (file)
@@ -146,19 +146,36 @@ static BKSProcessAssertionFlags flagsForState(AssertionState assertionState)
     }
 }
 
-static BKSProcessAssertionReason reasonForState(AssertionState assertionState)
+static AssertionReason reasonForState(AssertionState assertionState)
 {
     switch (assertionState) {
     case AssertionState::UnboundedNetworking:
-        return BKSProcessAssertionReasonFinishTaskUnbounded;
+        return AssertionReason::FinishTaskUnbounded;
     case AssertionState::Suspended:
     case AssertionState::Background:
     case AssertionState::Foreground:
+        return AssertionReason::Extension;
+    }
+}
+
+static BKSProcessAssertionReason toBKSProcessAssertionReason(AssertionReason reason)
+{
+    switch (reason) {
+    case AssertionReason::Extension:
         return BKSProcessAssertionReasonExtension;
+    case AssertionReason::FinishTask:
+        return BKSProcessAssertionReasonFinishTask;
+    case AssertionReason::FinishTaskUnbounded:
+        return BKSProcessAssertionReasonFinishTaskUnbounded;
     }
 }
 
 ProcessAssertion::ProcessAssertion(pid_t pid, const String& name, AssertionState assertionState)
+    : ProcessAssertion(pid, name, assertionState, reasonForState(assertionState))
+{
+}
+
+ProcessAssertion::ProcessAssertion(pid_t pid, const String& name, AssertionState assertionState, AssertionReason assertionReason)
     : m_assertionState(assertionState)
 {
     auto weakThis = makeWeakPtr(*this);
@@ -173,7 +190,7 @@ ProcessAssertion::ProcessAssertion(pid_t pid, const String& name, AssertionState
     };
     RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion() PID %d acquiring assertion for process with PID %d, name '%s'", this, getpid(), pid, name.utf8().data());
     
-    m_assertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:pid flags:flagsForState(assertionState) reason:reasonForState(assertionState) name:(NSString *)name withHandler:handler]);
+    m_assertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:pid flags:flagsForState(assertionState) reason:toBKSProcessAssertionReason(assertionReason) name:(NSString *)name withHandler:handler]);
     m_assertion.get().invalidationHandler = ^() {
         dispatch_async(dispatch_get_main_queue(), ^{
             RELEASE_LOG(ProcessSuspension, "%p - ProcessAssertion() Process assertion for process with PID %d was invalidated", this, pid);
index 248c6fb..e530b38 100644 (file)
                CD0C6831201FD10100A59409 /* WKFullScreenViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = CD0C682F201FD10100A59409 /* WKFullScreenViewController.h */; };
                CD19A26E1A13E834008D650E /* WebDiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = CD19A26A1A13E821008D650E /* WebDiagnosticLoggingClient.h */; };
                CD19D2EA2046406F0017074A /* FullscreenTouchSecheuristic.h in Headers */ = {isa = PBXBuildFile; fileRef = CD19D2E82046406F0017074A /* FullscreenTouchSecheuristic.h */; };
+               CD2865EE2255562000606AC7 /* ProcessTaskStateObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = CD2865EC2255562000606AC7 /* ProcessTaskStateObserver.h */; };
+               CD2865EF2255562000606AC7 /* ProcessTaskStateObserver.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD2865ED2255562000606AC7 /* ProcessTaskStateObserver.mm */; };
                CD491B081E70D05F00009066 /* UserMediaCaptureManager.h in Headers */ = {isa = PBXBuildFile; fileRef = CD491B061E70D05F00009066 /* UserMediaCaptureManager.h */; };
                CD491B0D1E732E4D00009066 /* UserMediaCaptureManagerMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD491B0B1E732E4D00009066 /* UserMediaCaptureManagerMessageReceiver.cpp */; };
                CD491B0E1E732E4D00009066 /* UserMediaCaptureManagerMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = CD491B0C1E732E4D00009066 /* UserMediaCaptureManagerMessages.h */; };
                CD19A26A1A13E821008D650E /* WebDiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebDiagnosticLoggingClient.h; sourceTree = "<group>"; };
                CD19D2E82046406F0017074A /* FullscreenTouchSecheuristic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FullscreenTouchSecheuristic.h; path = ios/fullscreen/FullscreenTouchSecheuristic.h; sourceTree = "<group>"; };
                CD19D2E92046406F0017074A /* FullscreenTouchSecheuristic.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FullscreenTouchSecheuristic.cpp; path = ios/fullscreen/FullscreenTouchSecheuristic.cpp; sourceTree = "<group>"; };
+               CD2865EC2255562000606AC7 /* ProcessTaskStateObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessTaskStateObserver.h; sourceTree = "<group>"; };
+               CD2865ED2255562000606AC7 /* ProcessTaskStateObserver.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ProcessTaskStateObserver.mm; sourceTree = "<group>"; };
                CD491B051E70D05F00009066 /* UserMediaCaptureManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserMediaCaptureManager.cpp; sourceTree = "<group>"; };
                CD491B061E70D05F00009066 /* UserMediaCaptureManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserMediaCaptureManager.h; sourceTree = "<group>"; };
                CD491B0A1E732D1200009066 /* UserMediaCaptureManager.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = UserMediaCaptureManager.messages.in; sourceTree = "<group>"; };
                                C55F916C1C595E440029E92D /* DataDetectionResult.h */,
                                C55F916D1C595E440029E92D /* DataDetectionResult.mm */,
                                2D1087621D2C641B00B85F82 /* LoadParametersCocoa.mm */,
+                               CD2865EC2255562000606AC7 /* ProcessTaskStateObserver.h */,
+                               CD2865ED2255562000606AC7 /* ProcessTaskStateObserver.mm */,
                                CD4B4D9A1E765E0000D27092 /* SharedRingBufferStorage.cpp */,
                                CD4B4D9B1E765E0000D27092 /* SharedRingBufferStorage.h */,
                                1AB1F78E1D1B34A6007C9BD1 /* WebCoreArgumentCodersCocoa.mm */,
                                E1CC1B9012D7EADF00625838 /* PrintInfo.h in Headers */,
                                86F9536518FF58F5001DB2EF /* ProcessAssertion.h in Headers */,
                                BC1A7C581136E19C00FB7167 /* ProcessLauncher.h in Headers */,
+                               CD2865EE2255562000606AC7 /* ProcessTaskStateObserver.h in Headers */,
                                463FD4821EB94EC000A2982C /* ProcessTerminationReason.h in Headers */,
                                86E67A251910B9D100004AB7 /* ProcessThrottler.h in Headers */,
                                83048AE61ACA45DC0082C832 /* ProcessThrottlerClient.h in Headers */,
                                BC82844D16B5081C00A278FE /* PluginServiceEntryPoint.mm in Sources */,
                                2D91344D212CF9F000128AFD /* PluginView.cpp in Sources */,
                                2D54C31B212F4DA60049C174 /* ProcessLauncher.cpp in Sources */,
+                               CD2865EF2255562000606AC7 /* ProcessTaskStateObserver.mm in Sources */,
                                2D72A1FA212BF46E00517A20 /* RemoteLayerTreeDrawingArea.mm in Sources */,
                                0FF24A2D1879E4BC003ABF0C /* RemoteLayerTreeDrawingAreaProxyMessageReceiver.cpp in Sources */,
                                2DC18FB4218A6E9E0025A88D /* RemoteLayerTreeViews.mm in Sources */,
index 540beb6..7159766 100644 (file)
@@ -1450,6 +1450,7 @@ void WebProcess::resetAllGeolocationPermissions()
 void WebProcess::actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend shouldAcknowledgeWhenReadyToSuspend)
 {
     SetForScope<bool> suspensionScope(m_isSuspending, true);
+    m_processIsSuspended = true;
 
 #if ENABLE(VIDEO)
     suspendAllMediaBuffering();
@@ -1511,6 +1512,9 @@ void WebProcess::prepareToSuspend()
 void WebProcess::cancelPrepareToSuspend()
 {
     RELEASE_LOG(ProcessSuspension, "%p - WebProcess::cancelPrepareToSuspend()", this);
+
+    m_processIsSuspended = false;
+
     unfreezeAllLayerTrees();
 
 #if PLATFORM(IOS_FAMILY)
@@ -1584,6 +1588,8 @@ void WebProcess::processDidResume()
 {
     RELEASE_LOG(ProcessSuspension, "%p - WebProcess::processDidResume()", this);
 
+    m_processIsSuspended = false;
+
     cancelMarkAllLayersVolatile();
     unfreezeAllLayerTrees();
     
index 4d986a8..09d6c50 100644 (file)
 #include <wtf/MachSendRight.h>
 #endif
 
+#if PLATFORM(IOS_FAMILY)
+#include "ProcessTaskStateObserver.h"
+#endif
+
 namespace API {
 class Object;
 }
@@ -116,7 +120,12 @@ struct WebsiteDataStoreParameters;
 class LayerHostingContext;
 #endif
 
-class WebProcess : public AuxiliaryProcess {
+class WebProcess
+    : public AuxiliaryProcess
+#if PLATFORM(IOS_FAMILY)
+    , ProcessTaskStateObserver::Client
+#endif
+{
 public:
     static WebProcess& singleton();
     static constexpr ProcessType processType = ProcessType::WebContent;
@@ -429,6 +438,7 @@ private:
 #endif
 
 #if PLATFORM(IOS_FAMILY)
+    void processTaskStateDidChange(ProcessTaskStateObserver::TaskState) final;
     bool shouldFreezeOnSuspension() const;
     void updateFreezerStatus();
 #endif
@@ -494,6 +504,8 @@ private:
     bool m_hasRichContentServices { false };
 #endif
 
+    bool m_processIsSuspended { false };
+
     HashSet<uint64_t> m_pagesInWindows;
     WebCore::Timer m_nonVisibleProcessCleanupTimer;
 
@@ -501,6 +513,7 @@ private:
 
 #if PLATFORM(IOS_FAMILY)
     std::unique_ptr<WebSQLiteDatabaseTracker> m_webSQLiteDatabaseTracker;
+    ProcessTaskStateObserver m_taskStateObserver { *this };
 #endif
 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
     std::unique_ptr<LayerHostingContext> m_contextForVisibilityPropagation;
index a174811..f00d780 100644 (file)
@@ -31,6 +31,7 @@
 #import "LogInitialization.h"
 #import "Logging.h"
 #import "ObjCObjectGraph.h"
+#import "ProcessAssertion.h"
 #import "SandboxExtension.h"
 #import "SandboxInitializationParameters.h"
 #import "WKAPICast.h"
@@ -287,6 +288,31 @@ void WebProcess::updateProcessName()
 #endif // PLATFORM(MAC)
 }
 
+#if PLATFORM(IOS_FAMILY)
+void WebProcess::processTaskStateDidChange(ProcessTaskStateObserver::TaskState taskState)
+{
+    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::processTaskStateDidChange() - taskState(%d)", this, taskState);
+    if (taskState == ProcessTaskStateObserver::None)
+        return;
+
+    if (taskState == ProcessTaskStateObserver::Suspended) {
+        if (m_processIsSuspended)
+            return;
+
+        RELEASE_LOG(ProcessSuspension, "%p - WebProcess::processTaskStateChanged() - unexpectedly entered Suspended state", this);
+        return;
+    }
+
+    if (!m_processIsSuspended)
+        return;
+
+    // We were awakened from suspension unexpectedly. Notify the WebProcessProxy, but take a process assertion on our parent PID
+    // to ensure that it too is awakened.
+    auto uiProcessAssertion = std::make_unique<ProcessAssertion>(parentProcessConnection()->remoteProcessID(), "Unexpectedly resumed", AssertionState::Background, AssertionReason::FinishTask);
+    parentProcessConnection()->sendWithAsyncReply(Messages::WebProcessProxy::ProcessWasUnexpectedlyUnsuspended(), [uiProcessAssertion = WTFMove(uiProcessAssertion)] { });
+}
+#endif
+
 static void registerWithAccessibility()
 {
 #if USE(APPKIT)