Move SuspendedPage logic from WebProcessPool to new WebBackForwardCache class
https://bugs.webkit.org/show_bug.cgi?id=202660

Reviewed by Antti Koivisto.

Source/WebKit:

Move SuspendedPage logic from WebProcessPool to new WebBackForwardCache class. This is
a first step towards implementing back / forward cache handling in the UIProcess.

The following changes were made:
1. SuspendedPageProxy objects are now normally owned by their associated WebBackForwardListItem
   (Similarly to CachedPage objects being owned by their HistoryItem in WebCore) instead of the
   WebProcessPool.
2. A new WebBackForwardCache class is introduced to cap the number of cached pages and clear
   them (similary to the PageCache in WebCore).

* Shared/WebBackForwardListItem.cpp:
(WebKit::WebBackForwardListItem::~WebBackForwardListItem):
(WebKit::WebBackForwardListItem::wasRemovedFromBackForwardList):
(WebKit::WebBackForwardListItem::setSuspendedPage):
(WebKit::WebBackForwardListItem::takeSuspendedPage):
(WebKit::WebBackForwardListItem::suspendedPage const):
* Shared/WebBackForwardListItem.h:
* Sources.txt:
* UIProcess/API/Cocoa/WKProcessPool.mm:
(-[WKProcessPool _maximumSuspendedPageCount]):
* UIProcess/RemoteLayerTree/mac/ScrollingTreeOverflowScrollingNodeRemoteMac.cpp:
* UIProcess/SuspendedPageProxy.cpp:
(WebKit::SuspendedPageProxy::findReusableSuspendedPageProcess):
(WebKit::SuspendedPageProxy::SuspendedPageProxy):
(WebKit::SuspendedPageProxy::~SuspendedPageProxy):
(WebKit::SuspendedPageProxy::setBackForwardListItem):
(WebKit::SuspendedPageProxy::clearBackForwardListItem):
(WebKit::SuspendedPageProxy::backForwardCache const):
(WebKit::SuspendedPageProxy::suspensionTimedOut):
* UIProcess/SuspendedPageProxy.h:
* UIProcess/WebAuthentication/Mock/MockHidConnection.h:
* UIProcess/WebAuthentication/Mock/MockHidService.h:
* UIProcess/WebBackForwardCache.cpp: Added.
(WebKit::WebBackForwardCache::removeOldestEntry):
(WebKit::WebBackForwardCache::setCapacity):
(WebKit::WebBackForwardCache::addEntry):
(WebKit::WebBackForwardCache::removeEntry):
(WebKit::WebBackForwardCache::takeEntry):
(WebKit::WebBackForwardCache::removeEntriesForProcess):
(WebKit::WebBackForwardCache::removeEntriesForPage):
(WebKit::WebBackForwardCache::removeEntriesMatching):
(WebKit::WebBackForwardCache::clear):
* UIProcess/WebBackForwardCache.h: Copied from Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidService.h.
(WebKit::WebBackForwardCache::capacity const):
(WebKit::WebBackForwardCache::size const):
* UIProcess/WebBackForwardList.cpp:
(WebKit::WebBackForwardList::didRemoveItem):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::suspendCurrentPageIfPossible):
(WebKit::WebPageProxy::backForwardCache const):
(WebKit::WebPageProxy::shouldUseBackForwardCache const):
(WebKit::WebPageProxy::close):
(WebKit::WebPageProxy::receivedNavigationPolicyDecision):
(WebKit::WebPageProxy::resetState):
(WebKit::WebPageProxy::enterAcceleratedCompositingMode):
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::process const):
* UIProcess/WebProcessPool.cpp:
(WebKit::m_webProcessCache):
(WebKit::WebProcessPool::disconnectProcess):
(WebKit::WebProcessPool::processForRegistrableDomain):
(WebKit::WebProcessPool::handleMemoryPressureWarning):
(WebKit::WebProcessPool::updateBackForwardCacheCapacity):
(WebKit::WebProcessPool::setCacheModel):
* UIProcess/WebProcessPool.h:
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::removeData):
(WebKit::WebsiteDataStore::setMockWebAuthenticationConfiguration):
* WebKit.xcodeproj/project.pbxproj:

Tools:

Drop test that relied on reusing a SuspendedPage's process from a loadHTMLStringLoad (optimization).
This no longer works now that SuspendedPages are owned by their associated back/forward item because
we currently do not create a back/forward item for loadHTMLString requests. Suspending a page for a
loadHTMLString was also wasteful since there was no way to restore that page (given that there was
no WebBackForwardListItem to go back to).

* TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251022 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index ac99b93..c4c5b1a 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,80 @@
+2019-10-11  Chris Dumez  <cdumez@apple.com>
+
+        Move SuspendedPage logic from WebProcessPool to new WebBackForwardCache class
+        https://bugs.webkit.org/show_bug.cgi?id=202660
+
+        Reviewed by Antti Koivisto.
+
+        Move SuspendedPage logic from WebProcessPool to new WebBackForwardCache class. This is
+        a first step towards implementing back / forward cache handling in the UIProcess.
+
+        The following changes were made:
+        1. SuspendedPageProxy objects are now normally owned by their associated WebBackForwardListItem
+           (Similarly to CachedPage objects being owned by their HistoryItem in WebCore) instead of the
+           WebProcessPool.
+        2. A new WebBackForwardCache class is introduced to cap the number of cached pages and clear
+           them (similary to the PageCache in WebCore).
+
+        * Shared/WebBackForwardListItem.cpp:
+        (WebKit::WebBackForwardListItem::~WebBackForwardListItem):
+        (WebKit::WebBackForwardListItem::wasRemovedFromBackForwardList):
+        (WebKit::WebBackForwardListItem::setSuspendedPage):
+        (WebKit::WebBackForwardListItem::takeSuspendedPage):
+        (WebKit::WebBackForwardListItem::suspendedPage const):
+        * Shared/WebBackForwardListItem.h:
+        * Sources.txt:
+        * UIProcess/API/Cocoa/WKProcessPool.mm:
+        (-[WKProcessPool _maximumSuspendedPageCount]):
+        * UIProcess/RemoteLayerTree/mac/ScrollingTreeOverflowScrollingNodeRemoteMac.cpp:
+        * UIProcess/SuspendedPageProxy.cpp:
+        (WebKit::SuspendedPageProxy::findReusableSuspendedPageProcess):
+        (WebKit::SuspendedPageProxy::SuspendedPageProxy):
+        (WebKit::SuspendedPageProxy::~SuspendedPageProxy):
+        (WebKit::SuspendedPageProxy::setBackForwardListItem):
+        (WebKit::SuspendedPageProxy::clearBackForwardListItem):
+        (WebKit::SuspendedPageProxy::backForwardCache const):
+        (WebKit::SuspendedPageProxy::suspensionTimedOut):
+        * UIProcess/SuspendedPageProxy.h:
+        * UIProcess/WebAuthentication/Mock/MockHidConnection.h:
+        * UIProcess/WebAuthentication/Mock/MockHidService.h:
+        * UIProcess/WebBackForwardCache.cpp: Added.
+        (WebKit::WebBackForwardCache::removeOldestEntry):
+        (WebKit::WebBackForwardCache::setCapacity):
+        (WebKit::WebBackForwardCache::addEntry):
+        (WebKit::WebBackForwardCache::removeEntry):
+        (WebKit::WebBackForwardCache::takeEntry):
+        (WebKit::WebBackForwardCache::removeEntriesForProcess):
+        (WebKit::WebBackForwardCache::removeEntriesForPage):
+        (WebKit::WebBackForwardCache::removeEntriesMatching):
+        (WebKit::WebBackForwardCache::clear):
+        * UIProcess/WebBackForwardCache.h: Copied from Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidService.h.
+        (WebKit::WebBackForwardCache::capacity const):
+        (WebKit::WebBackForwardCache::size const):
+        * UIProcess/WebBackForwardList.cpp:
+        (WebKit::WebBackForwardList::didRemoveItem):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::suspendCurrentPageIfPossible):
+        (WebKit::WebPageProxy::backForwardCache const):
+        (WebKit::WebPageProxy::shouldUseBackForwardCache const):
+        (WebKit::WebPageProxy::close):
+        (WebKit::WebPageProxy::receivedNavigationPolicyDecision):
+        (WebKit::WebPageProxy::resetState):
+        (WebKit::WebPageProxy::enterAcceleratedCompositingMode):
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::process const):
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::m_webProcessCache):
+        (WebKit::WebProcessPool::disconnectProcess):
+        (WebKit::WebProcessPool::processForRegistrableDomain):
+        (WebKit::WebProcessPool::handleMemoryPressureWarning):
+        (WebKit::WebProcessPool::updateBackForwardCacheCapacity):
+        (WebKit::WebProcessPool::setCacheModel):
+        * UIProcess/WebProcessPool.h:
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::removeData):
+        (WebKit::WebsiteDataStore::setMockWebAuthenticationConfiguration):
+        * WebKit.xcodeproj/project.pbxproj:
+
 2019-10-11  Alex Christensen  <achristensen@webkit.org>
 
         Only use CFNetwork SPI for metrics where needed
diff --git a/Source/WebKit/Shared/WebBackForwardListItem.cpp b/Source/WebKit/Shared/WebBackForwardListItem.cpp
index daf4da2..b0825af 100644
--- a/Source/WebKit/Shared/WebBackForwardListItem.cpp
+++ b/Source/WebKit/Shared/WebBackForwardListItem.cpp
@@ -27,6 +27,7 @@
 #include "WebBackForwardListItem.h"
 
 #include "SuspendedPageProxy.h"
+#include "WebBackForwardCache.h"
 #include "WebProcessPool.h"
 #include "WebProcessProxy.h"
 #include <wtf/DebugUtilities.h>
@@ -53,8 +54,6 @@
 {
     ASSERT(allItems().get(m_itemState.identifier) == this);
     allItems().remove(m_itemState.identifier);
-
-    removeSuspendedPageFromProcessPool();
 }
 
 HashMap<BackForwardItemIdentifier, WebBackForwardListItem*>& WebBackForwardListItem::allItems()
@@ -159,13 +158,29 @@
     return hasSameFrames(mainFrameState, otherMainFrameState);
 }
 
-void WebBackForwardListItem::setSuspendedPage(SuspendedPageProxy* page)
+void WebBackForwardListItem::wasRemovedFromBackForwardList()
 {
-    if (m_suspendedPage == page)
-        return;
+    if (m_suspendedPage)
+        m_suspendedPage->backForwardCache().removeEntry(*this);
+    ASSERT(!m_suspendedPage);
+}
 
-    removeSuspendedPageFromProcessPool();
-    m_suspendedPage = makeWeakPtr(page);
+void WebBackForwardListItem::setSuspendedPage(std::unique_ptr<SuspendedPageProxy>&& suspendedPage)
+{
+    if (m_suspendedPage)
+        m_suspendedPage->clearBackForwardListItem();
+
+    m_suspendedPage = WTFMove(suspendedPage);
+
+    if (m_suspendedPage)
+        m_suspendedPage->setBackForwardListItem(*this);
+}
+
+std::unique_ptr<SuspendedPageProxy> WebBackForwardListItem::takeSuspendedPage()
+{
+    if (m_suspendedPage)
+        m_suspendedPage->clearBackForwardListItem();
+    return std::exchange(m_suspendedPage, nullptr);
 }
 
 SuspendedPageProxy* WebBackForwardListItem::suspendedPage() const
@@ -173,15 +188,6 @@
     return m_suspendedPage.get();
 }
 
-void WebBackForwardListItem::removeSuspendedPageFromProcessPool()
-{
-    if (!m_suspendedPage)
-        return;
-
-    m_suspendedPage->process().processPool().removeSuspendedPage(*m_suspendedPage);
-    ASSERT(!m_suspendedPage);
-}
-
 #if !LOG_DISABLED
 const char* WebBackForwardListItem::loggingString()
 {
diff --git a/Source/WebKit/Shared/WebBackForwardListItem.h b/Source/WebKit/Shared/WebBackForwardListItem.h
index 6d67ec0..b2cf771 100644
--- a/Source/WebKit/Shared/WebBackForwardListItem.h
+++ b/Source/WebKit/Shared/WebBackForwardListItem.h
@@ -44,6 +44,7 @@
 namespace WebKit {
 
 class SuspendedPageProxy;
+class WebBackForwardCache;
 
 class WebBackForwardListItem : public API::ObjectImpl<API::Object::Type::BackForwardListItem> {
 public:
@@ -77,7 +78,9 @@
     ViewSnapshot* snapshot() const { return m_itemState.snapshot.get(); }
     void setSnapshot(RefPtr<ViewSnapshot>&& snapshot) { m_itemState.snapshot = WTFMove(snapshot); }
 #endif
-    void setSuspendedPage(SuspendedPageProxy*);
+
+    void wasRemovedFromBackForwardList();
+
     SuspendedPageProxy* suspendedPage() const;
 
 #if !LOG_DISABLED
@@ -87,13 +90,16 @@
 private:
     WebBackForwardListItem(BackForwardListItemState&&, WebPageProxyIdentifier);
 
-    void removeSuspendedPageFromProcessPool();
+    // WebBackForwardCache.
+    friend class WebBackForwardCache;
+    void setSuspendedPage(std::unique_ptr<SuspendedPageProxy>&&);
+    std::unique_ptr<SuspendedPageProxy> takeSuspendedPage();
 
     BackForwardListItemState m_itemState;
     URL m_resourceDirectoryURL;
     WebPageProxyIdentifier m_pageID;
     WebCore::ProcessIdentifier m_lastProcessIdentifier;
-    WeakPtr<SuspendedPageProxy> m_suspendedPage;
+    std::unique_ptr<SuspendedPageProxy> m_suspendedPage;
 };
 
 typedef Vector<Ref<WebBackForwardListItem>> BackForwardListItemVector;
diff --git a/Source/WebKit/Sources.txt b/Source/WebKit/Sources.txt
index 936c0cf..d7d8991 100644
--- a/Source/WebKit/Sources.txt
+++ b/Source/WebKit/Sources.txt
@@ -260,6 +260,7 @@
 UIProcess/UserMediaPermissionRequestProxy.cpp
 UIProcess/UserMediaProcessManager.cpp
 UIProcess/VisitedLinkStore.cpp
+UIProcess/WebBackForwardCache.cpp
 UIProcess/WebBackForwardList.cpp
 UIProcess/WebColorPicker.cpp
 UIProcess/WebConnectionToWebProcess.cpp
diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm b/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm
index 238ced3..607f20f 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm
+++ b/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm
@@ -36,6 +36,7 @@
 #import "WKObject.h"
 #import "WKWebViewInternal.h"
 #import "WKWebsiteDataStoreInternal.h"
+#import "WebBackForwardCache.h"
 #import "WebCertificateInfo.h"
 #import "WebCookieManagerProxy.h"
 #import "WebProcessCache.h"
@@ -526,7 +527,7 @@
 
 - (NSUInteger)_maximumSuspendedPageCount
 {
-    return _processPool->maxSuspendedPageCount();
+    return _processPool->backForwardCache().capacity();
 }
 
 - (NSUInteger)_processCacheCapacity
diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/mac/ScrollingTreeOverflowScrollingNodeRemoteMac.cpp b/Source/WebKit/UIProcess/RemoteLayerTree/mac/ScrollingTreeOverflowScrollingNodeRemoteMac.cpp
index fa6c3da..3178390 100644
--- a/Source/WebKit/UIProcess/RemoteLayerTree/mac/ScrollingTreeOverflowScrollingNodeRemoteMac.cpp
+++ b/Source/WebKit/UIProcess/RemoteLayerTree/mac/ScrollingTreeOverflowScrollingNodeRemoteMac.cpp
@@ -30,6 +30,7 @@
 
 #include "RemoteScrollingTree.h"
 #include "ScrollerPairMac.h"
+#include <WebCore/ScrollingStateOverflowScrollingNode.h>
 
 namespace WebKit {
 using namespace WebCore;
diff --git a/Source/WebKit/UIProcess/SuspendedPageProxy.cpp b/Source/WebKit/UIProcess/SuspendedPageProxy.cpp
index 7924e4e..5c5b86e 100644
--- a/Source/WebKit/UIProcess/SuspendedPageProxy.cpp
+++ b/Source/WebKit/UIProcess/SuspendedPageProxy.cpp
@@ -28,6 +28,7 @@
 
 #include "DrawingAreaProxy.h"
 #include "Logging.h"
+#include "WebBackForwardCache.h"
 #include "WebPageMessages.h"
 #include "WebPageProxy.h"
 #include "WebPageProxyMessages.h"
@@ -43,6 +44,22 @@
 
 static const Seconds suspensionTimeout { 10_s };
 
+static HashSet<SuspendedPageProxy*>& allSuspendedPages()
+{
+    static NeverDestroyed<HashSet<SuspendedPageProxy*>> map;
+    return map;
+}
+
+RefPtr<WebProcessProxy> SuspendedPageProxy::findReusableSuspendedPageProcess(WebProcessPool& processPool, const RegistrableDomain& registrableDomain, WebsiteDataStore& dataStore)
+{
+    for (auto* suspendedPage : allSuspendedPages()) {
+        auto& process = suspendedPage->process();
+        if (&process.processPool() == &processPool && process.registrableDomain() == registrableDomain && &process.websiteDataStore() == &dataStore)
+            return &process;
+    }
+    return nullptr;
+}
+
 #if !LOG_DISABLED
 static const HashSet<IPC::StringReference>& messageNamesToIgnoreWhileSuspended()
 {
@@ -89,6 +106,7 @@
     , m_suspensionToken(m_process->throttler().backgroundActivityToken())
 #endif
 {
+    allSuspendedPages().add(this);
     m_process->incrementSuspendedPageCount();
     m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_webPageID, *this);
 
@@ -99,6 +117,7 @@
 SuspendedPageProxy::~SuspendedPageProxy()
 {
     m_process->decrementSuspendedPageCount();
+    allSuspendedPages().remove(this);
 
     if (m_readyToUnsuspendHandler) {
         RunLoop::main().dispatch([readyToUnsuspendHandler = WTFMove(m_readyToUnsuspendHandler)]() mutable {
@@ -123,6 +142,23 @@
     });
 }
 
+void SuspendedPageProxy::setBackForwardListItem(WebBackForwardListItem& item)
+{
+    ASSERT(!m_backForwardListItem);
+    m_backForwardListItem = &item;
+}
+
+void SuspendedPageProxy::clearBackForwardListItem()
+{
+    ASSERT(m_backForwardListItem);
+    m_backForwardListItem = nullptr;
+}
+
+WebBackForwardCache& SuspendedPageProxy::backForwardCache() const
+{
+    return process().processPool().backForwardCache();
+}
+
 void SuspendedPageProxy::waitUntilReadyToUnsuspend(CompletionHandler<void(SuspendedPageProxy*)>&& completionHandler)
 {
     if (m_readyToUnsuspendHandler)
@@ -215,8 +251,12 @@
 
 void SuspendedPageProxy::suspensionTimedOut()
 {
+    if (!m_backForwardListItem)
+        return;
+
     RELEASE_LOG_ERROR(ProcessSwapping, "%p - SuspendedPageProxy::suspensionTimedOut() destroying the suspended page because it failed to suspend in time", this);
-    m_process->processPool().removeSuspendedPage(*this); // Will destroy |this|.
+    ASSERT(m_backForwardListItem->suspendedPage() == this);
+    backForwardCache().removeEntry(*m_backForwardListItem); // Will destroy |this|.
 }
 
 void SuspendedPageProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder& decoder)
diff --git a/Source/WebKit/UIProcess/SuspendedPageProxy.h b/Source/WebKit/UIProcess/SuspendedPageProxy.h
index 1ac3d88..9f3ae33 100644
--- a/Source/WebKit/UIProcess/SuspendedPageProxy.h
+++ b/Source/WebKit/UIProcess/SuspendedPageProxy.h
@@ -35,8 +35,11 @@
 
 namespace WebKit {
 
+class WebBackForwardCache;
 class WebPageProxy;
+class WebProcessPool;
 class WebProcessProxy;
+class WebsiteDataStore;
 
 enum class ShouldDelayClosingUntilEnteringAcceleratedCompositingMode : bool { No, Yes };
 
@@ -46,11 +49,19 @@
     SuspendedPageProxy(WebPageProxy&, Ref<WebProcessProxy>&&, WebCore::FrameIdentifier mainFrameID, ShouldDelayClosingUntilEnteringAcceleratedCompositingMode);
     ~SuspendedPageProxy();
 
+    static RefPtr<WebProcessProxy> findReusableSuspendedPageProcess(WebProcessPool&, const WebCore::RegistrableDomain&, WebsiteDataStore&);
+
     WebPageProxy& page() const { return m_page; }
     WebCore::PageIdentifier webPageID() const { return m_webPageID; }
-    WebProcessProxy& process() { return m_process.get(); }
+    WebProcessProxy& process() const { return m_process.get(); }
     WebCore::FrameIdentifier mainFrameID() const { return m_mainFrameID; }
 
+    void setBackForwardListItem(WebBackForwardListItem&);
+    void clearBackForwardListItem();
+
+    WebBackForwardListItem* backForwardListItem() { return m_backForwardListItem; }
+    WebBackForwardCache& backForwardCache() const;
+
     bool pageIsClosedOrClosing() const;
 
     void waitUntilReadyToUnsuspend(CompletionHandler<void(SuspendedPageProxy*)>&&);
@@ -77,6 +88,7 @@
     WebPageProxy& m_page;
     WebCore::PageIdentifier m_webPageID;
     Ref<WebProcessProxy> m_process;
+    WebBackForwardListItem* m_backForwardListItem { nullptr };
     WebCore::FrameIdentifier m_mainFrameID;
     bool m_isClosed { false };
     ShouldDelayClosingUntilEnteringAcceleratedCompositingMode m_shouldDelayClosingUntilEnteringAcceleratedCompositingMode { ShouldDelayClosingUntilEnteringAcceleratedCompositingMode::No };
diff --git a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h
index 40836d2..0db00e9 100644
--- a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h
+++ b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.h
@@ -62,8 +62,8 @@
 
     WebCore::MockWebAuthenticationConfiguration m_configuration;
     Optional<fido::FidoHidMessage> m_requestMessage;
-    WebCore::MockWebAuthenticationConfiguration::HidStage m_stage { MockWebAuthenticationConfiguration::HidStage::Info };
-    WebCore::MockWebAuthenticationConfiguration::HidSubStage m_subStage { MockWebAuthenticationConfiguration::HidSubStage::Init };
+    WebCore::MockWebAuthenticationConfiguration::HidStage m_stage { WebCore::MockWebAuthenticationConfiguration::HidStage::Info };
+    WebCore::MockWebAuthenticationConfiguration::HidSubStage m_subStage { WebCore::MockWebAuthenticationConfiguration::HidSubStage::Init };
     uint32_t m_currentChannel { fido::kHidBroadcastChannel };
     bool m_requireResidentKey { false };
     bool m_requireUserVerification  { false };
diff --git a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidService.h b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidService.h
index be1ead6..45c4d28 100644
--- a/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidService.h
+++ b/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidService.h
@@ -34,7 +34,7 @@
 
 class MockHidService final : public HidService {
 public:
-    MockHidService(Observer&, const MockWebAuthenticationConfiguration&);
+    MockHidService(Observer&, const WebCore::MockWebAuthenticationConfiguration&);
 
 private:
     void platformStartDiscovery() final;
diff --git a/Source/WebKit/UIProcess/WebBackForwardCache.cpp b/Source/WebKit/UIProcess/WebBackForwardCache.cpp
new file mode 100644
index 0000000..5de186e
--- /dev/null
+++ b/Source/WebKit/UIProcess/WebBackForwardCache.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "WebBackForwardCache.h"
+
+#include "SuspendedPageProxy.h"
+#include "WebBackForwardListItem.h"
+#include "WebPageProxy.h"
+#include "WebProcessProxy.h"
+
+namespace WebKit {
+
+WebBackForwardCache::WebBackForwardCache() = default;
+
+WebBackForwardCache::~WebBackForwardCache() = default;
+
+inline void WebBackForwardCache::removeOldestEntry()
+{
+    ASSERT(!m_itemsWithCachedPage.isEmpty());
+    removeEntry(*m_itemsWithCachedPage.first());
+}
+
+void WebBackForwardCache::setCapacity(unsigned capacity)
+{
+    m_capacity = capacity;
+    while (size() > capacity)
+        removeOldestEntry();
+}
+
+void WebBackForwardCache::addEntry(WebBackForwardListItem& item, std::unique_ptr<SuspendedPageProxy>&& suspendedPage)
+{
+    ASSERT(capacity());
+
+    item.setSuspendedPage(WTFMove(suspendedPage));
+    m_itemsWithCachedPage.add(&item);
+
+    if (size() > capacity())
+        removeOldestEntry();
+    ASSERT(size() <= capacity());
+}
+
+void WebBackForwardCache::removeEntry(WebBackForwardListItem& item)
+{
+    ASSERT(m_itemsWithCachedPage.contains(&item));
+    m_itemsWithCachedPage.remove(&item);
+    item.setSuspendedPage(nullptr);
+}
+
+std::unique_ptr<SuspendedPageProxy> WebBackForwardCache::takeEntry(WebBackForwardListItem& item)
+{
+    ASSERT(m_itemsWithCachedPage.contains(&item));
+    m_itemsWithCachedPage.remove(&item);
+    return item.takeSuspendedPage();
+}
+
+void WebBackForwardCache::removeEntriesForProcess(WebProcessProxy& process)
+{
+    removeEntriesMatching([&process](auto& item) {
+        return &item.suspendedPage()->process() == &process;
+    });
+}
+
+void WebBackForwardCache::removeEntriesForPage(WebPageProxy& page)
+{
+    removeEntriesMatching([pageID = page.identifier()](auto& item) {
+        return item.pageID() == pageID;
+    });
+}
+
+void WebBackForwardCache::removeEntriesMatching(const Function<bool(WebBackForwardListItem&)>& matches)
+{
+    for (auto it = m_itemsWithCachedPage.begin(); it != m_itemsWithCachedPage.end();) {
+        auto current = it;
+        ++it;
+        if (matches(**current)) {
+            (*current)->setSuspendedPage(nullptr);
+            m_itemsWithCachedPage.remove(current);
+        }
+    }
+}
+
+void WebBackForwardCache::clear(AllowProcessCaching allowProcessCaching)
+{
+    auto itemsWithCachedPage = WTFMove(m_itemsWithCachedPage);
+    for (auto* item : itemsWithCachedPage) {
+        auto process = makeRef(item->suspendedPage()->process());
+        item->setSuspendedPage(nullptr);
+        process->maybeShutDown(allowProcessCaching);
+    }
+}
+
+} // namespace WebKit.
diff --git a/Source/WebKit/UIProcess/WebBackForwardCache.h b/Source/WebKit/UIProcess/WebBackForwardCache.h
new file mode 100644
index 0000000..6e02ab1
--- /dev/null
+++ b/Source/WebKit/UIProcess/WebBackForwardCache.h
@@ -0,0 +1,65 @@
+/*
+ * 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
+
+#include <wtf/Forward.h>
+#include <wtf/ListHashSet.h>
+
+namespace WebKit {
+
+class SuspendedPageProxy;
+class WebBackForwardListItem;
+class WebPageProxy;
+class WebProcessProxy;
+enum class AllowProcessCaching;
+
+class WebBackForwardCache {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    WebBackForwardCache();
+    ~WebBackForwardCache();
+
+    void setCapacity(unsigned);
+    unsigned capacity() const { return m_capacity; }
+    unsigned size() const { return m_itemsWithCachedPage.size(); }
+
+    void clear(AllowProcessCaching);
+    void removeEntriesForProcess(WebProcessProxy&);
+    void removeEntriesForPage(WebPageProxy&);
+
+    void addEntry(WebBackForwardListItem&, std::unique_ptr<SuspendedPageProxy>&&);
+    void removeEntry(WebBackForwardListItem&);
+    std::unique_ptr<SuspendedPageProxy> takeEntry(WebBackForwardListItem&);
+
+private:
+    void removeOldestEntry();
+    void removeEntriesMatching(const Function<bool(WebBackForwardListItem&)>&);
+
+    unsigned m_capacity { 0 };
+    ListHashSet<WebBackForwardListItem*> m_itemsWithCachedPage;
+};
+
+} // namespace WebKit
diff --git a/Source/WebKit/UIProcess/WebBackForwardList.cpp b/Source/WebKit/UIProcess/WebBackForwardList.cpp
index 5370371..6aecd82 100644
--- a/Source/WebKit/UIProcess/WebBackForwardList.cpp
+++ b/Source/WebKit/UIProcess/WebBackForwardList.cpp
@@ -29,6 +29,7 @@
 #include "APIArray.h"
 #include "Logging.h"
 #include "SessionState.h"
+#include "WebBackForwardCache.h"
 #include "WebPageProxy.h"
 #include <WebCore/DiagnosticLoggingClient.h>
 #include <WebCore/DiagnosticLoggingKeys.h>
@@ -467,9 +468,10 @@
 
 void WebBackForwardList::didRemoveItem(WebBackForwardListItem& backForwardListItem)
 {
+    backForwardListItem.wasRemovedFromBackForwardList();
+
     m_page->backForwardRemovedItem(backForwardListItem.itemID());
 
-    backForwardListItem.setSuspendedPage(nullptr);
 #if PLATFORM(COCOA) || PLATFORM(GTK)
     backForwardListItem.setSnapshot(nullptr);
 #endif
diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp
index 08e142c..e251b36 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.cpp
+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp
@@ -92,6 +92,7 @@
 #include "UserMediaProcessManager.h"
 #include "WKContextPrivate.h"
 #include "WebAutomationSession.h"
+#include "WebBackForwardCache.h"
 #include "WebBackForwardList.h"
 #include "WebBackForwardListItem.h"
 #include "WebCertificateInfo.h"
@@ -750,6 +751,7 @@
 
 bool WebPageProxy::suspendCurrentPageIfPossible(API::Navigation& navigation, Optional<FrameIdentifier> mainFrameID, ProcessSwapRequestedByClient processSwapRequestedByClient, ShouldDelayClosingUntilEnteringAcceleratedCompositingMode shouldDelayClosingUntilEnteringAcceleratedCompositingMode)
 {
+    m_suspendedPageKeptToPreventFlashing = nullptr;
     m_lastSuspendedPage = nullptr;
 
     if (!mainFrameID)
@@ -780,6 +782,15 @@
         return false;
     }
 
+    bool needsSuspendedPageToPreventFlashing = shouldDelayClosingUntilEnteringAcceleratedCompositingMode == ShouldDelayClosingUntilEnteringAcceleratedCompositingMode::Yes;
+    if (!needsSuspendedPageToPreventFlashing && (!fromItem || !shouldUseBackForwardCache())) {
+        if (!fromItem)
+            RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i there is no associated WebBackForwardListItem", m_process->processIdentifier());
+        else
+            RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i the back / forward cache is disabled", m_process->processIdentifier());
+        return false;
+    }
+
     RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Suspending current page for process pid %i", m_process->processIdentifier());
     auto suspendedPage = makeUnique<SuspendedPageProxy>(*this, m_process.copyRef(), *mainFrameID, shouldDelayClosingUntilEnteringAcceleratedCompositingMode);
 
@@ -790,14 +801,28 @@
     if (processSwapRequestedByClient == ProcessSwapRequestedByClient::Yes)
         suspendedPage->closeWithoutFlashing();
 
-    if (fromItem && m_preferences->usesPageCache())
-        fromItem->setSuspendedPage(suspendedPage.get());
-
     m_lastSuspendedPage = makeWeakPtr(*suspendedPage);
-    m_process->processPool().addSuspendedPage(WTFMove(suspendedPage));
+
+    if (fromItem && shouldUseBackForwardCache())
+        backForwardCache().addEntry(*fromItem, WTFMove(suspendedPage));
+    else {
+        ASSERT(needsSuspendedPageToPreventFlashing);
+        m_suspendedPageKeptToPreventFlashing = WTFMove(suspendedPage);
+    }
+
     return true;
 }
 
+WebBackForwardCache& WebPageProxy::backForwardCache() const
+{
+    return process().processPool().backForwardCache();
+}
+
+bool WebPageProxy::shouldUseBackForwardCache() const
+{
+    return m_preferences->usesPageCache() && backForwardCache().capacity() > 0;
+}
+
 void WebPageProxy::swapToWebProcess(Ref<WebProcessProxy>&& process, PageIdentifier webPageID, std::unique_ptr<DrawingAreaProxy>&& drawingArea, RefPtr<WebFrameProxy>&& mainFrame)
 {
     ASSERT(!m_isClosed);
@@ -1027,7 +1052,7 @@
     m_fullscreenClient = makeUnique<API::FullscreenClient>();
 #endif
 
-    m_process->processPool().removeAllSuspendedPagesForPage(*this);
+    m_process->processPool().backForwardCache().removeEntriesForPage(*this);
 
     m_process->send(Messages::WebPage::Close(), m_webPageID);
     m_process->removeWebPage(*this, WebProcessProxy::EndsUsingDataStore::Yes);
@@ -2929,7 +2954,7 @@
             RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "decidePolicyForNavigationAction: keep using process %i for navigation, reason: %{public}s", processIdentifier(), reason.utf8().data());
 
         if (shouldProcessSwap) {
-            auto suspendedPage = destinationSuspendedPage ? process().processPool().takeSuspendedPage(*destinationSuspendedPage) : nullptr;
+            auto suspendedPage = destinationSuspendedPage ? backForwardCache().takeEntry(*destinationSuspendedPage->backForwardListItem()) : nullptr;
             if (suspendedPage && suspendedPage->pageIsClosedOrClosing())
                 suspendedPage = nullptr;
 
@@ -7026,6 +7051,7 @@
     m_mainFrame = nullptr;
     m_focusedFrame = nullptr;
     m_frameSetLargestFrame = nullptr;
+    m_suspendedPageKeptToPreventFlashing = nullptr;
     m_lastSuspendedPage = nullptr;
 
 #if PLATFORM(COCOA)
@@ -7355,6 +7381,7 @@
 
     if (m_lastSuspendedPage)
         m_lastSuspendedPage->pageEnteredAcceleratedCompositingMode();
+    m_suspendedPageKeptToPreventFlashing = nullptr;
 }
 
 void WebPageProxy::exitAcceleratedCompositingMode()
diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h
index a004e90..a69488d 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.h
+++ b/Source/WebKit/UIProcess/WebPageProxy.h
@@ -270,6 +270,7 @@
 class WebNavigationState;
 class VideoFullscreenManagerProxy;
 class WebAuthenticatorCoordinatorProxy;
+class WebBackForwardCache;
 class WebKeyboardEvent;
 class WebURLSchemeHandler;
 class WebMouseEvent;
@@ -1114,9 +1115,11 @@
 #endif
 
     WebProcessProxy& ensureRunningProcess();
-    WebProcessProxy& process() { return m_process; }
+    WebProcessProxy& process() const { return m_process; }
     ProcessID processIdentifier() const;
 
+    WebBackForwardCache& backForwardCache() const;
+
     WebPreferences& preferences() { return m_preferences; }
     void setPreferences(WebPreferences&);
 
@@ -1597,6 +1600,7 @@
     void platformInitialize();
 
     void notifyProcessPoolToPrewarm();
+    bool shouldUseBackForwardCache() const;
 
     RefPtr<API::Navigation> goToBackForwardItem(WebBackForwardListItem&, WebCore::FrameLoadType);
 
@@ -2551,6 +2555,7 @@
     bool m_mayHaveUniversalFileReadSandboxExtension { false };
 
     std::unique_ptr<ProvisionalPageProxy> m_provisionalPage;
+    std::unique_ptr<SuspendedPageProxy> m_suspendedPageKeptToPreventFlashing;
     WeakPtr<SuspendedPageProxy> m_lastSuspendedPage;
 
 #if HAVE(PENCILKIT)
diff --git a/Source/WebKit/UIProcess/WebProcessPool.cpp b/Source/WebKit/UIProcess/WebProcessPool.cpp
index 4b5edfc..30557a6 100644
--- a/Source/WebKit/UIProcess/WebProcessPool.cpp
+++ b/Source/WebKit/UIProcess/WebProcessPool.cpp
@@ -56,6 +56,7 @@
 #include "UIGamepadProvider.h"
 #include "WKContextPrivate.h"
 #include "WebAutomationSession.h"
+#include "WebBackForwardCache.h"
 #include "WebBackForwardList.h"
 #include "WebBackForwardListItem.h"
 #include "WebCertificateInfo.h"
@@ -236,6 +237,7 @@
     , m_foregroundWebProcessCounter([this](RefCounterEvent) { updateProcessAssertions(); })
     , m_backgroundWebProcessCounter([this](RefCounterEvent) { updateProcessAssertions(); })
 #endif
+    , m_backForwardCache(makeUniqueRef<WebBackForwardCache>())
     , m_webProcessCache(makeUniqueRef<WebProcessCache>(*this))
 {
     static std::once_flag onceFlag;
@@ -287,7 +289,7 @@
 
     notifyThisWebProcessPoolWasCreated();
 
-    updateMaxSuspendedPageCount();
+    updateBackForwardCacheCapacity();
 }
 
 WebProcessPool::~WebProcessPool()
@@ -1091,9 +1093,7 @@
     if (m_processWithPageCache == process)
         m_processWithPageCache = nullptr;
 
-    m_suspendedPages.removeAllMatching([process](auto& suspendedPage) {
-        return &suspendedPage->process() == process;
-    });
+    m_backForwardCache->removeEntriesForProcess(*process);
 
 #if ENABLE(SERVICE_WORKER)
     if (process->isRunningServiceWorkers()) {
@@ -1131,7 +1131,7 @@
             return *process;
 
         // Check if we have a suspended page for the given registrable domain and use its process if we do, for performance reasons.
-        if (auto process = page ? findReusableSuspendedPageProcess(registrableDomain, *page, websiteDataStore) : nullptr) {
+        if (auto process = SuspendedPageProxy::findReusableSuspendedPageProcess(*this, registrableDomain, websiteDataStore)) {
             RELEASE_LOG(ProcessSwapping, "Using WebProcess %i from a SuspendedPage", process->processIdentifier());
             return *process;
         }
@@ -1388,7 +1388,7 @@
     RELEASE_LOG(PerformanceLogging, "%p - WebProcessPool::handleMemoryPressureWarning", this);
 
 
-    clearSuspendedPages(AllowProcessCaching::No);
+    m_backForwardCache->clear(AllowProcessCaching::No);
     m_webProcessCache->clear();
 
     if (m_prewarmedProcess)
@@ -1549,25 +1549,22 @@
     sendToNetworkingProcess(Messages::NetworkProcess::RegisterURLSchemeAsCanDisplayOnlyIfCanRequest(urlScheme));
 }
 
-void WebProcessPool::updateMaxSuspendedPageCount()
+void WebProcessPool::updateBackForwardCacheCapacity()
 {
     if (!m_configuration->usesPageCache())
         return;
 
     unsigned dummy = 0;
     Seconds dummyInterval;
-    unsigned pageCacheSize = 0;
-    calculateMemoryCacheSizes(LegacyGlobalSettings::singleton().cacheModel(), dummy, dummy, dummy, dummyInterval, pageCacheSize);
+    unsigned backForwardCacheCapacity = 0;
+    calculateMemoryCacheSizes(LegacyGlobalSettings::singleton().cacheModel(), dummy, dummy, dummy, dummyInterval, backForwardCacheCapacity);
 
-    m_maxSuspendedPageCount = pageCacheSize;
-
-    while (m_suspendedPages.size() > m_maxSuspendedPageCount)
-        m_suspendedPages.removeFirst();
+    m_backForwardCache->setCapacity(backForwardCacheCapacity);
 }
 
 void WebProcessPool::setCacheModel(CacheModel cacheModel)
 {
-    updateMaxSuspendedPageCount();
+    updateBackForwardCacheCapacity();
 
     sendToAllProcesses(Messages::WebProcess::SetCacheModel(cacheModel));
 
@@ -2258,67 +2255,6 @@
     return completionHandler(createNewProcess(), nullptr, reason);
 }
 
-RefPtr<WebProcessProxy> WebProcessPool::findReusableSuspendedPageProcess(const WebCore::RegistrableDomain& registrableDomain, WebPageProxy& page, WebsiteDataStore& dataStore)
-{
-    auto it = m_suspendedPages.findIf([&](auto& suspendedPage) {
-        return suspendedPage->process().registrableDomain() == registrableDomain && &suspendedPage->process().websiteDataStore() == &dataStore;
-    });
-    return it == m_suspendedPages.end() ? nullptr : &(*it)->process();
-}
-
-void WebProcessPool::addSuspendedPage(std::unique_ptr<SuspendedPageProxy>&& suspendedPage)
-{
-    if (!m_maxSuspendedPageCount)
-        return;
-
-    if (m_suspendedPages.size() >= m_maxSuspendedPageCount)
-        m_suspendedPages.removeFirst();
-
-    m_suspendedPages.append(WTFMove(suspendedPage));
-}
-
-void WebProcessPool::removeAllSuspendedPagesForPage(WebPageProxy& page, WebProcessProxy* process)
-{
-    m_suspendedPages.removeAllMatching([&page, process](auto& suspendedPage) {
-        return &suspendedPage->page() == &page && (!process || &suspendedPage->process() == process);
-    });
-}
-
-std::unique_ptr<SuspendedPageProxy> WebProcessPool::takeSuspendedPage(SuspendedPageProxy& suspendedPage)
-{
-    return m_suspendedPages.takeFirst([&suspendedPage](auto& item) {
-        return item.get() == &suspendedPage;
-    });
-}
-
-void WebProcessPool::removeSuspendedPage(SuspendedPageProxy& suspendedPage)
-{
-    auto it = m_suspendedPages.findIf([&suspendedPage](auto& item) {
-        return item.get() == &suspendedPage;
-    });
-    if (it != m_suspendedPages.end())
-        m_suspendedPages.remove(it);
-}
-
-bool WebProcessPool::hasSuspendedPageFor(WebProcessProxy& process, WebPageProxy& page) const
-{
-    return m_suspendedPages.findIf([&process, &page](auto& suspendedPage) {
-        return &suspendedPage->process() == &process && &suspendedPage->page() == &page;
-    }) != m_suspendedPages.end();
-}
-
-void WebProcessPool::clearSuspendedPages(AllowProcessCaching allowProcessCaching)
-{
-    HashSet<RefPtr<WebProcessProxy>> processes;
-    for (auto& suspendedPage : m_suspendedPages)
-        processes.add(&suspendedPage->process());
-
-    m_suspendedPages.clear();
-
-    for (auto& process : processes)
-        process->maybeShutDown(allowProcessCaching);
-}
-
 void WebProcessPool::addMockMediaDevice(const MockMediaDevice& device)
 {
 #if ENABLE(MEDIA_STREAM)
diff --git a/Source/WebKit/UIProcess/WebProcessPool.h b/Source/WebKit/UIProcess/WebProcessPool.h
index 05b8721..60ef493 100644
--- a/Source/WebKit/UIProcess/WebProcessPool.h
+++ b/Source/WebKit/UIProcess/WebProcessPool.h
@@ -95,6 +95,7 @@
 
 namespace WebKit {
 
+class WebBackForwardCache;
 class HighPerformanceGraphicsUsageSampler;
 class UIGamepad;
 class PerActivityStateCPUUsageSampler;
@@ -149,6 +150,8 @@
     void addMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID, IPC::MessageReceiver&);
     void removeMessageReceiver(IPC::StringReference messageReceiverName);
     void removeMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID);
+
+    WebBackForwardCache& backForwardCache() { return m_backForwardCache.get(); }
     
     template <typename T>
     void addMessageReceiver(IPC::StringReference messageReceiverName, ObjectIdentifier<T> destinationID, IPC::MessageReceiver& receiver)
@@ -476,17 +479,6 @@
 
     void processForNavigation(WebPageProxy&, const API::Navigation&, Ref<WebProcessProxy>&& sourceProcess, const URL& sourceURL, ProcessSwapRequestedByClient, Ref<WebsiteDataStore>&&, CompletionHandler<void(Ref<WebProcessProxy>&&, SuspendedPageProxy*, const String&)>&&);
 
-    // SuspendedPageProxy management.
-    void addSuspendedPage(std::unique_ptr<SuspendedPageProxy>&&);
-    void removeAllSuspendedPagesForPage(WebPageProxy&, WebProcessProxy* = nullptr);
-    std::unique_ptr<SuspendedPageProxy> takeSuspendedPage(SuspendedPageProxy&);
-    void removeSuspendedPage(SuspendedPageProxy&);
-    bool hasSuspendedPageFor(WebProcessProxy&, WebPageProxy&) const;
-    unsigned maxSuspendedPageCount() const { return m_maxSuspendedPageCount; }
-    RefPtr<WebProcessProxy> findReusableSuspendedPageProcess(const WebCore::RegistrableDomain&, WebPageProxy&, WebsiteDataStore&);
-
-    void clearSuspendedPages(AllowProcessCaching);
-
     void didReachGoodTimeToPrewarm();
 
     void didCollectPrewarmInformation(const WebCore::RegistrableDomain&, const WebCore::PrewarmInformation&);
@@ -592,7 +584,7 @@
 
     void tryPrewarmWithDomainInformation(WebProcessProxy&, const WebCore::RegistrableDomain&);
 
-    void updateMaxSuspendedPageCount();
+    void updateBackForwardCacheCapacity();
 
 #if PLATFORM(IOS)
     static float displayBrightness();
@@ -773,8 +765,7 @@
 #endif
 #endif
 
-    Deque<std::unique_ptr<SuspendedPageProxy>> m_suspendedPages;
-    unsigned m_maxSuspendedPageCount { 0 };
+    UniqueRef<WebBackForwardCache> m_backForwardCache;
 
     UniqueRef<WebProcessCache> m_webProcessCache;
     HashMap<WebCore::RegistrableDomain, RefPtr<WebProcessProxy>> m_swappedProcessesPerRegistrableDomain;
diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
index 2619016..2a23b2a 100644
--- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
+++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
@@ -35,6 +35,7 @@
 #include "NetworkProcessMessages.h"
 #include "ShouldGrandfatherStatistics.h"
 #include "StorageAccessStatus.h"
+#include "WebBackForwardCache.h"
 #include "WebKit2Initialize.h"
 #include "WebPageProxy.h"
 #include "WebProcessCache.h"
@@ -725,7 +726,7 @@
     auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
     if (webProcessAccessType != ProcessAccessType::None) {
         for (auto& processPool : processPools()) {
-            processPool->clearSuspendedPages(AllowProcessCaching::No);
+            processPool->backForwardCache().clear(AllowProcessCaching::No);
             processPool->webProcessCache().clear();
         }
 
@@ -2077,7 +2078,7 @@
 #endif
 
 #if ENABLE(WEB_AUTHN)
-void WebsiteDataStore::setMockWebAuthenticationConfiguration(MockWebAuthenticationConfiguration&& configuration)
+void WebsiteDataStore::setMockWebAuthenticationConfiguration(WebCore::MockWebAuthenticationConfiguration&& configuration)
 {
     if (!m_authenticatorManager->isMock()) {
         m_authenticatorManager = makeUniqueRef<MockAuthenticatorManager>(WTFMove(configuration));
diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj
index 638ebed..da21741 100644
--- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj
+++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj
@@ -3197,6 +3197,8 @@
 		4459984122833E6000E61373 /* SyntheticEditingCommandType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SyntheticEditingCommandType.h; sourceTree = "<group>"; };
 		449D90D821FD63FE00F677C0 /* LocalAuthenticationSoftLink.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalAuthenticationSoftLink.mm; sourceTree = "<group>"; };
 		44A481C621F2D27B00F2F919 /* ClientCertificateAuthenticationXPCConstants.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ClientCertificateAuthenticationXPCConstants.cpp; sourceTree = "<group>"; };
+		4603011A234BE31D009C8217 /* WebBackForwardCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebBackForwardCache.cpp; sourceTree = "<group>"; };
+		4603011B234BE31E009C8217 /* WebBackForwardCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebBackForwardCache.h; sourceTree = "<group>"; };
 		460F488D1F996F6C00CF4B87 /* WebSWContextManagerConnectionMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebSWContextManagerConnectionMessageReceiver.cpp; path = DerivedSources/WebKit2/WebSWContextManagerConnectionMessageReceiver.cpp; sourceTree = BUILT_PRODUCTS_DIR; };
 		460F488E1F996F6C00CF4B87 /* WebSWContextManagerConnectionMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebSWContextManagerConnectionMessages.h; path = DerivedSources/WebKit2/WebSWContextManagerConnectionMessages.h; sourceTree = BUILT_PRODUCTS_DIR; };
 		462107D71F38DBD300DD7810 /* PingLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PingLoad.cpp; sourceTree = "<group>"; };
@@ -7822,6 +7824,8 @@
 				1A0F29E1120B44420053D1B9 /* VisitedLinkStore.cpp */,
 				1A0F29E2120B44420053D1B9 /* VisitedLinkStore.h */,
 				1A60224918C16B0800C3E8C9 /* VisitedLinkStore.messages.in */,
+				4603011A234BE31D009C8217 /* WebBackForwardCache.cpp */,
+				4603011B234BE31E009C8217 /* WebBackForwardCache.h */,
 				BC72BA1B11E64907001EB4EA /* WebBackForwardList.cpp */,
 				BC72BA1C11E64907001EB4EA /* WebBackForwardList.h */,
 				F036978715F4BF0500C3A80E /* WebColorPicker.cpp */,
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index f06050d..3f042b1 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,18 @@
+2019-10-11  Chris Dumez  <cdumez@apple.com>
+
+        Move SuspendedPage logic from WebProcessPool to new WebBackForwardCache class
+        https://bugs.webkit.org/show_bug.cgi?id=202660
+
+        Reviewed by Antti Koivisto.
+
+        Drop test that relied on reusing a SuspendedPage's process from a loadHTMLStringLoad (optimization).
+        This no longer works now that SuspendedPages are owned by their associated back/forward item because
+        we currently do not create a back/forward item for loadHTMLString requests. Suspending a page for a
+        loadHTMLString was also wasteful since there was no way to restore that page (given that there was
+        no WebBackForwardListItem to go back to).
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
+
 2019-10-11  Brian Burg  <bburg@apple.com>
 
         Remove some dead code in webkitpy's mac.py
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm
index 5e69f05..5d68b8d 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm
@@ -2481,55 +2481,6 @@
     EXPECT_EQ(applePID, [webView _webProcessIdentifier]);
 }
 
-TEST(ProcessSwap, ReuseSuspendedProcessLoadHTMLString)
-{
-    auto processPoolConfiguration = psonProcessPoolConfiguration();
-    processPoolConfiguration.get().usesWebProcessCache = NO;
-    auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
-
-    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
-    [webViewConfiguration setProcessPool:processPool.get()];
-    auto handler = adoptNS([[PSONScheme alloc] init]);
-    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
-
-    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
-    auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
-    [webView setNavigationDelegate:delegate.get()];
-
-    NSString *htmlString = @"<html><body>TEST</body></html>";
-    [webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]];
-
-    TestWebKitAPI::Util::run(&done);
-    done = false;
-
-    auto webkitPID = [webView _webProcessIdentifier];
-
-    [webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"pson://www.apple.com/main1.html"]];
-
-    TestWebKitAPI::Util::run(&done);
-    done = false;
-
-    auto applePID = [webView _webProcessIdentifier];
-
-    EXPECT_NE(webkitPID, applePID);
-
-    [webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]];
-
-    TestWebKitAPI::Util::run(&done);
-    done = false;
-
-    // We should have gone back to the webkit.org process for this load since we reuse SuspendedPages' process when possible.
-    EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]);
-
-    [webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"pson://www.apple.com/main2.html"]];
-
-    TestWebKitAPI::Util::run(&done);
-    done = false;
-
-    // We should have gone back to the apple.com process for this load since we reuse SuspendedPages' process when possible.
-    EXPECT_EQ(applePID, [webView _webProcessIdentifier]);
-}
-
 static const char* failsToEnterPageCacheTestBytes = R"PSONRESOURCE(
 <body>
 <script>