Make sure history navigations reuse the existing process when necessary.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 May 2018 16:12:11 +0000 (16:12 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 May 2018 16:12:11 +0000 (16:12 +0000)
<rdar://problem/39746516> and https://bugs.webkit.org/show_bug.cgi?id=185532

Reviewed by Ryosuke Niwa.

Source/WebCore:

Covered by new API tests.

In WebCore-land, make sure *all* NavigationActions to a back/forward item are tagged with
the item identifier.

* history/HistoryItem.cpp:
(WebCore::HistoryItem::HistoryItem):
(WebCore::HistoryItem::logString const):
* history/HistoryItem.h:

* loader/FrameLoader.cpp:
(WebCore::FrameLoader::loadDifferentDocumentItem):

* loader/NavigationAction.cpp:
(WebCore::NavigationAction::setTargetBackForwardItem):

* loader/NavigationAction.h:
(WebCore::NavigationAction::targetBackForwardItemIdentifier const):

Source/WebKit:

If a view navigates to either a data: or blob: URL, it reuses the existing process.

In such cases we need to also ensure that history navigations back will also reuse the existing process.

* Shared/NavigationActionData.cpp:
(WebKit::NavigationActionData::encode const):
(WebKit::NavigationActionData::decode):
* Shared/NavigationActionData.h:

* UIProcess/API/APINavigation.h:
(API::Navigation::setTargetItem):

* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
(-[_WKProcessPoolConfiguration pageCacheEnabled]):
(-[_WKProcessPoolConfiguration setPageCacheEnabled:]):

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::receivedPolicyDecision):
(WebKit::WebPageProxy::decidePolicyForNavigationAction):

* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::processForNavigationInternal): If the current and target back/forward items both
  came from the same process, then reuse the existing process.

* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:

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

17 files changed:
Source/WebCore/ChangeLog
Source/WebCore/history/HistoryItem.cpp
Source/WebCore/history/HistoryItem.h
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/NavigationAction.cpp
Source/WebCore/loader/NavigationAction.h
Source/WebKit/ChangeLog
Source/WebKit/Shared/NavigationActionData.cpp
Source/WebKit/Shared/NavigationActionData.h
Source/WebKit/UIProcess/API/APINavigation.h
Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h
Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm

index 5f4ab2b..bb3e5a4 100644 (file)
@@ -1,3 +1,29 @@
+2018-05-11  Brady Eidson  <beidson@apple.com>
+
+        Make sure history navigations reuse the existing process when necessary.
+        <rdar://problem/39746516> and https://bugs.webkit.org/show_bug.cgi?id=185532
+
+        Reviewed by Ryosuke Niwa.
+
+        Covered by new API tests.
+
+        In WebCore-land, make sure *all* NavigationActions to a back/forward item are tagged with
+        the item identifier.
+
+        * history/HistoryItem.cpp:
+        (WebCore::HistoryItem::HistoryItem):
+        (WebCore::HistoryItem::logString const):
+        * history/HistoryItem.h:
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::loadDifferentDocumentItem):
+
+        * loader/NavigationAction.cpp:
+        (WebCore::NavigationAction::setTargetBackForwardItem):
+
+        * loader/NavigationAction.h:
+        (WebCore::NavigationAction::targetBackForwardItemIdentifier const):
+
 2018-05-11  Yacine Bandou  <yacine.bandou_ext@softathome.com>
 
         [EME][GStreamer] Handle the protection event in MediaPlayerPrivate
index a5ba95e..d308b90 100644 (file)
@@ -35,6 +35,7 @@
 #include "SharedBuffer.h"
 #include <stdio.h>
 #include <wtf/DateMath.h>
+#include <wtf/DebugUtilities.h>
 #include <wtf/WallTime.h>
 #include <wtf/text/CString.h>
 
@@ -105,6 +106,7 @@ inline HistoryItem::HistoryItem(const HistoryItem& item)
     , m_scale(item.m_scale)
     , m_scaleIsInitial(item.m_scaleIsInitial)
 #endif
+    , m_identifier(item.m_identifier)
 {
     if (item.m_formData)
         m_formData = item.m_formData->copy();
@@ -489,7 +491,14 @@ int HistoryItem::showTreeWithIndent(unsigned indentLevel) const
 }
 
 #endif
-                
+
+#if !LOG_DISABLED
+const char* HistoryItem::logString() const
+{
+    return debugString("HistoryItem current URL ", urlString(), ", identifier ", m_identifier.logString());
+}
+#endif
+
 } // namespace WebCore
 
 #ifndef NDEBUG
index ee466c7..e72b589 100644 (file)
@@ -215,6 +215,10 @@ public:
     void setWasRestoredFromSession(bool wasRestoredFromSession) { m_wasRestoredFromSession = wasRestoredFromSession; }
     bool wasRestoredFromSession() const { return m_wasRestoredFromSession; }
 
+#if !LOG_DISABLED
+    const char* logString() const;
+#endif
+
 private:
     WEBCORE_EXPORT HistoryItem();
     WEBCORE_EXPORT HistoryItem(const String& urlString, const String& title);
index 1b7a099..0a5aff2 100644 (file)
@@ -3528,7 +3528,11 @@ void FrameLoader::loadDifferentDocumentItem(HistoryItem& item, FrameLoadType loa
     if (CachedPage* cachedPage = PageCache::singleton().get(item, m_frame.page())) {
         auto documentLoader = cachedPage->documentLoader();
         m_client.updateCachedDocumentLoader(*documentLoader);
-        documentLoader->setTriggeringAction({ *m_frame.document(), documentLoader->request(), initiatedByMainFrame, loadType, false });
+
+        auto action = NavigationAction { *m_frame.document(), documentLoader->request(), initiatedByMainFrame, loadType, false };
+        action.setTargetBackForwardItem(item);
+        documentLoader->setTriggeringAction(WTFMove(action));
+
         documentLoader->setLastCheckedRequest(ResourceRequest());
         loadWithDocumentLoader(documentLoader, loadType, 0, AllowNavigationToInvalidURL::Yes, navigationPolicyCheck, [] { });
         return;
@@ -3616,6 +3620,8 @@ void FrameLoader::loadDifferentDocumentItem(HistoryItem& item, FrameLoadType loa
         action = { *m_frame.document(), requestForOriginalURL, initiatedByMainFrame, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy };
     }
 
+    action.setTargetBackForwardItem(item);
+
     loadWithNavigationAction(request, action, LockHistory::No, loadType, 0, AllowNavigationToInvalidURL::Yes, [] { });
 }
 
index 71f13fc..8fcdb48 100644 (file)
@@ -32,6 +32,7 @@
 #include "Document.h"
 #include "Event.h"
 #include "FrameLoader.h"
+#include "HistoryItem.h"
 
 namespace WebCore {
 
@@ -95,4 +96,9 @@ NavigationAction NavigationAction::copyWithShouldOpenExternalURLsPolicy(ShouldOp
     return result;
 }
 
+void NavigationAction::setTargetBackForwardItem(HistoryItem& item)
+{
+    m_targetBackForwardItemIdentifier = item.identifier();
+}
+
 }
index a5af334..234108b 100644 (file)
@@ -28,6 +28,7 @@
 
 #pragma once
 
+#include "BackForwardItemIdentifier.h"
 #include "FrameLoaderTypes.h"
 #include "ResourceRequest.h"
 #include "UserGestureIndicator.h"
@@ -37,6 +38,7 @@ namespace WebCore {
 
 class Document;
 class Event;
+class HistoryItem;
 
 class NavigationAction {
 public:
@@ -80,6 +82,9 @@ public:
     void setOpener(std::optional<std::pair<uint64_t, uint64_t>>&& opener) { m_opener = WTFMove(opener); }
     const std::optional<std::pair<uint64_t, uint64_t>>& opener() const { return m_opener; }
 
+    void setTargetBackForwardItem(HistoryItem&);
+    const std::optional<BackForwardItemIdentifier>& targetBackForwardItemIdentifier() const { return m_targetBackForwardItemIdentifier; }
+
 private:
     RefPtr<Document> m_sourceDocument;
     ResourceRequest m_resourceRequest;
@@ -92,6 +97,7 @@ private:
     bool m_treatAsSameOriginNavigation;
     bool m_isCrossOriginWindowOpenNavigation { false };
     std::optional<std::pair<uint64_t /* pageID */, uint64_t /* frameID */>> m_opener;
+    std::optional<BackForwardItemIdentifier> m_targetBackForwardItemIdentifier;
 };
 
 } // namespace WebCore
index 2568dd3..dfff45a 100644 (file)
@@ -1,3 +1,38 @@
+2018-05-11  Brady Eidson  <beidson@apple.com>
+
+        Make sure history navigations reuse the existing process when necessary.
+        <rdar://problem/39746516> and https://bugs.webkit.org/show_bug.cgi?id=185532
+
+        Reviewed by Ryosuke Niwa.
+
+        If a view navigates to either a data: or blob: URL, it reuses the existing process.
+        In such cases we need to also ensure that history navigations back will also reuse the existing process.
+
+        * Shared/NavigationActionData.cpp:
+        (WebKit::NavigationActionData::encode const):
+        (WebKit::NavigationActionData::decode):
+        * Shared/NavigationActionData.h:
+
+        * UIProcess/API/APINavigation.h:
+        (API::Navigation::setTargetItem):
+
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
+        (-[_WKProcessPoolConfiguration pageCacheEnabled]):
+        (-[_WKProcessPoolConfiguration setPageCacheEnabled:]):
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::receivedPolicyDecision):
+        (WebKit::WebPageProxy::decidePolicyForNavigationAction):
+
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::processForNavigationInternal): If the current and target back/forward items both
+          came from the same process, then reuse the existing process.
+
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
+
 2018-05-10  Brent Fulgham  <bfulgham@apple.com>
 
         REGRESSION(r231057): Encrypted media content playback failures
index 5133c42..e61b2ac 100644 (file)
@@ -50,6 +50,7 @@ void NavigationActionData::encode(IPC::Encoder& encoder) const
     encoder << treatAsSameOriginNavigation;
     encoder << isCrossOriginWindowOpenNavigation;
     encoder << opener;
+    encoder << targetBackForwardItemIdentifier;
 }
 
 std::optional<NavigationActionData> NavigationActionData::decode(IPC::Decoder& decoder)
@@ -113,9 +114,14 @@ std::optional<NavigationActionData> NavigationActionData::decode(IPC::Decoder& d
     if (!opener)
         return std::nullopt;
 
+    std::optional<std::optional<BackForwardItemIdentifier>> targetBackForwardItemIdentifier;
+    decoder >> targetBackForwardItemIdentifier;
+    if (!targetBackForwardItemIdentifier)
+        return std::nullopt;
+        
     return {{ WTFMove(navigationType), WTFMove(modifiers), WTFMove(mouseButton), WTFMove(syntheticClickType), WTFMove(*userGestureTokenIdentifier),
         WTFMove(*canHandleRequest), WTFMove(shouldOpenExternalURLsPolicy), WTFMove(*downloadAttribute), WTFMove(clickLocationInRootViewCoordinates),
-        WTFMove(*isRedirect), *treatAsSameOriginNavigation, *isCrossOriginWindowOpenNavigation, WTFMove(*opener) }};
+        WTFMove(*isRedirect), *treatAsSameOriginNavigation, *isCrossOriginWindowOpenNavigation, WTFMove(*opener), WTFMove(*targetBackForwardItemIdentifier) }};
 }
 
 } // namespace WebKit
index c231327..bf73982 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "WebEvent.h"
+#include <WebCore/BackForwardItemIdentifier.h>
 #include <WebCore/FloatPoint.h>
 #include <WebCore/FrameLoaderTypes.h>
 
@@ -53,6 +54,7 @@ struct NavigationActionData {
     bool treatAsSameOriginNavigation { false };
     bool isCrossOriginWindowOpenNavigation { false };
     std::optional<std::pair<uint64_t, uint64_t>> opener;
+    std::optional<WebCore::BackForwardItemIdentifier> targetBackForwardItemIdentifier;
 };
 
 }
index 1ea94d6..9e59177 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "APIObject.h"
+#include "WebBackForwardListItem.h"
 #include <WebCore/Process.h>
 #include <WebCore/ResourceRequest.h>
 #include <wtf/Ref.h>
@@ -35,7 +36,6 @@ enum class FrameLoadType;
 }
 
 namespace WebKit {
-class WebBackForwardListItem;
 class WebNavigationState;
 }
 
@@ -71,6 +71,7 @@ public:
     void setCurrentRequestIsRedirect(bool isRedirect) { m_isRedirect = isRedirect; }
     bool currentRequestIsRedirect() const { return m_isRedirect; }
 
+    void setTargetItem(WebKit::WebBackForwardListItem& item) { m_targetItem = &item; }
     WebKit::WebBackForwardListItem* targetItem() const { return m_targetItem.get(); }
     WebKit::WebBackForwardListItem* fromItem() const { return m_fromItem.get(); }
     std::optional<WebCore::FrameLoadType> backForwardFrameLoadType() const { return m_backForwardFrameLoadType; }
index 424e4f4..7ea6d70 100644 (file)
@@ -61,6 +61,7 @@ WK_CLASS_AVAILABLE(macosx(10.10), ios(8.0))
 @property (nonatomic) BOOL alwaysKeepAndReuseSwappedProcesses WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic) BOOL processSwapsOnWindowOpenWithOpener WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic) BOOL trackNetworkActivity WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic) BOOL pageCacheEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @end
 
index 5b69b61..5541664 100644 (file)
     _processPoolConfiguration->setTrackNetworkActivity(track);
 }
 
+- (BOOL)pageCacheEnabled
+{
+    return _processPoolConfiguration->cacheModel() != WebKit::CacheModelDocumentViewer;
+}
+
+- (void)setPageCacheEnabled:(BOOL)enabled
+{
+    if (!enabled)
+        _processPoolConfiguration->setCacheModel(WebKit::CacheModelDocumentViewer);
+    else if (![self pageCacheEnabled])
+        _processPoolConfiguration->setCacheModel(WebKit::CacheModelPrimaryWebBrowser);
+}
+
 #if PLATFORM(IOS)
 - (NSString *)CTDataConnectionServiceType
 {
index d7df4cf..a67ba08 100644 (file)
@@ -2423,7 +2423,7 @@ void WebPageProxy::receivedPolicyDecision(PolicyAction action, WebFrameProxy& fr
             auto proposedProcess = process().processPool().processForNavigation(*this, *navigation, action);
 
             if (proposedProcess.ptr() != &process()) {
-                LOG(ProcessSwapping, "Switching from process %i to new process for navigation %" PRIu64 " '%s'", processIdentifier(), navigation->navigationID(), navigation->loggingString());
+                LOG(ProcessSwapping, "(ProcessSwapping) Switching from process %i to new process (%i) for navigation %" PRIu64 " '%s'", processIdentifier(), proposedProcess->processIdentifier(), navigation->navigationID(), navigation->loggingString());
 
                 RunLoop::main().dispatch([this, protectedThis = makeRef(*this), navigation = makeRef(*navigation), proposedProcess = WTFMove(proposedProcess)]() mutable {
                     continueNavigationInNewProcess(navigation.get(), WTFMove(proposedProcess));
@@ -3964,8 +3964,22 @@ void WebPageProxy::decidePolicyForNavigationAction(uint64_t frameID, const Secur
     MESSAGE_CHECK(frame);
     MESSAGE_CHECK_URL(request.url());
     MESSAGE_CHECK_URL(originalRequest.url());
-    
-    Ref<API::Navigation> navigation = navigationID ? makeRef(m_navigationState->navigation(navigationID)) : m_navigationState->createLoadRequestNavigation(ResourceRequest(request), m_backForwardList->currentItem());
+
+    RefPtr<API::Navigation> navigation;
+    if (navigationID)
+        navigation = makeRef(m_navigationState->navigation(navigationID));
+
+    if (auto targetBackForwardItemIdentifier = navigationActionData.targetBackForwardItemIdentifier) {
+        if (auto* item = m_backForwardList->itemForID(*navigationActionData.targetBackForwardItemIdentifier)) {
+            if (!navigation)
+                navigation = m_navigationState->createBackForwardNavigation(*item, m_backForwardList->currentItem(), FrameLoadType::IndexedBackForward);
+            else
+                navigation->setTargetItem(*item);
+        }
+    }
+
+    if (!navigation)
+        navigation = m_navigationState->createLoadRequestNavigation(ResourceRequest(request), m_backForwardList->currentItem());
 
     uint64_t newNavigationID = navigation->navigationID();
     navigation->setWasUserInitiated(!!navigationActionData.userGestureTokenIdentifier);
@@ -3977,7 +3991,7 @@ void WebPageProxy::decidePolicyForNavigationAction(uint64_t frameID, const Secur
     navigation->setOpener(navigationActionData.opener);
 
     auto listener = makeRef(frame->setUpPolicyListenerProxy(listenerID, PolicyListenerType::NavigationAction));
-    listener->setNavigation(WTFMove(navigation));
+    listener->setNavigation(navigation.releaseNonNull());
 
 #if ENABLE(CONTENT_FILTERING)
     if (frame->didHandleContentFilterUnblockNavigation(request))
index 11cc8bd..ebdd045 100644 (file)
@@ -59,6 +59,8 @@
 #include "UIGamepadProvider.h"
 #include "WKContextPrivate.h"
 #include "WebAutomationSession.h"
+#include "WebBackForwardList.h"
+#include "WebBackForwardListItem.h"
 #include "WebCertificateInfo.h"
 #include "WebContextSupplement.h"
 #include "WebCookieManagerProxy.h"
@@ -2106,6 +2108,15 @@ Ref<WebProcessProxy> WebProcessPool::processForNavigationInternal(WebPageProxy&
             action = PolicyAction::Suspend;
             return *suspendedPage->process();
         }
+
+        // If the target back/forward item and the current back/forward item originated
+        // in the same WebProcess then we should reuse the current WebProcess.
+        if (auto* currentItem = page.backForwardList().currentItem()) {
+            if (currentItem->itemID().processIdentifier == backForwardListItem->itemID().processIdentifier) {
+                action = PolicyAction::Suspend;
+                return page.process();
+            }
+        }
     }
 
     if (navigation.treatAsSameOriginNavigation())
index 338b699..d0fe918 100644 (file)
@@ -867,6 +867,7 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const Navigat
     navigationActionData.treatAsSameOriginNavigation = navigationAction.treatAsSameOriginNavigation();
     navigationActionData.isCrossOriginWindowOpenNavigation = navigationAction.isCrossOriginWindowOpenNavigation();
     navigationActionData.opener = navigationAction.opener();
+    navigationActionData.targetBackForwardItemIdentifier = navigationAction.targetBackForwardItemIdentifier();
 
     WebCore::Frame* coreFrame = m_frame->coreFrame();
     if (!coreFrame)
index 9b08264..c2ddfb5 100644 (file)
@@ -1,3 +1,12 @@
+2018-05-11  Brady Eidson  <beidson@apple.com>
+
+        Make sure history navigations reuse the existing process when necessary.
+        <rdar://problem/39746516> and https://bugs.webkit.org/show_bug.cgi?id=185532
+
+        Reviewed by Ryosuke Niwa.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
+
 2018-05-10  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [JSC][GLIB] Add introspectable alternatives to functions using vargars
index 5db6b85..dd59052 100644 (file)
 
 #if WK_API_ENABLED
 
+@interface WKProcessPool ()
+- (WKContextRef)_contextForTesting;
+@end
+
 static bool done;
 static bool failed;
 static bool didCreateWebView;
@@ -1382,4 +1386,52 @@ TEST(ProcessSwap, NavigateToInvalidURL)
     EXPECT_EQ(pid1, pid2);
 }
 
+static const char* navigateToDataURLThenBackBytes = R"PSONRESOURCE(
+<script>
+onpageshow = function(event) {
+    // Location changes need to happen outside the onload handler to generate history entries.
+    setTimeout(function() {
+      window.location.href = "data:text/html,<body onload='history.back()'></body>";
+    }, 0);
+}
+
+</script>
+)PSONRESOURCE";
+
+TEST(ProcessSwap, NavigateToDataURLThenBack)
+{
+    auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+    processPoolConfiguration.get().processSwapsOnNavigation = YES;
+    processPoolConfiguration.get().pageCacheEnabled = NO;
+    auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
+
+    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [webViewConfiguration setProcessPool:processPool.get()];
+    RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:navigateToDataURLThenBackBytes]);
+    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
+
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+
+    numberOfDecidePolicyCalls = 0;
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]]];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+    auto pid1 = [webView _webProcessIdentifier];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+    auto pid2 = [webView _webProcessIdentifier];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+    auto pid3 = [webView _webProcessIdentifier];
+
+    EXPECT_EQ(3, numberOfDecidePolicyCalls);
+    EXPECT_EQ(1u, seenPIDs.size());
+    EXPECT_EQ(pid1, pid2);
+    EXPECT_EQ(pid2, pid3);
+}
+
 #endif // WK_API_ENABLED