Title: [282368] trunk
Revision
282368
Author
achristen...@apple.com
Date
2021-09-13 15:36:21 -0700 (Mon, 13 Sep 2021)

Log Message

Add unit test that uses PCM daemon
https://bugs.webkit.org/show_bug.cgi?id=230191

Reviewed by Chris Dumez.

Source/WebKit:

I make a way to specify what mach service to connect to,
then I temporarily register an executable with launchd to provide that mach service,
then I run the test communicating with that executable.

* NetworkProcess/NetworkSession.cpp:
(WebKit::managerOrProxy):
* NetworkProcess/NetworkSessionCreationParameters.cpp:
(WebKit::NetworkSessionCreationParameters::encode const):
(WebKit::NetworkSessionCreationParameters::decode):
* NetworkProcess/NetworkSessionCreationParameters.h:
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp:
(WebKit::PCM::Connection::Connection):
(WebKit::PCM::Connection::send const):
(WebKit::PCM::Connection::sendWithReply const):
(WebKit::PCM::Connection::connectionToDaemon): Deleted.
(WebKit::PCM::Connection::send): Deleted.
(WebKit::PCM::Connection::sendWithReply): Deleted.
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h:
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp:
(WebKit::PCM::managerPointer):
(WebKit::PCM::initializePCMStorageInDirectory):
(WebKit::PCM::daemonManager):
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h:
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp:
(WebKit::PCM::ManagerProxy::sendMessage const):
(WebKit::PCM::ManagerProxy::sendMessageWithReply const):
(WebKit::PCM::ManagerProxy::ManagerProxy):
(WebKit::PCM::sendMessage): Deleted.
(WebKit::PCM::sendMessageWithReply): Deleted.
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h:
* NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm:
(WebKit::PCM::Connection::Connection):
(WebKit::PCM::Connection::initializeConnectionIfNeeded const):
(WebKit::PCM::Connection::send const):
(WebKit::PCM::Connection::sendWithReply const):
(WebKit::PCM::Connection::connectionToDaemon): Deleted.
(WebKit::PCM::Connection::send): Deleted.
(WebKit::PCM::Connection::sendWithReply): Deleted.
* NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in:
* Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm:
(WebKit::startListeningForMachServiceConnections):
(WebKit::registerScheduledActivityHandler):
(WebKit::PCMDaemonMain):
* UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h:
* UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm:
(-[_WKWebsiteDataStoreConfiguration pcmMachServiceName]):
(-[_WKWebsiteDataStoreConfiguration setPCMMachServiceName:]):
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::parameters):
* UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp:
(WebKit::WebsiteDataStoreConfiguration::WebsiteDataStoreConfiguration):
(WebKit::WebsiteDataStoreConfiguration::copy const):
* UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h:
(WebKit::WebsiteDataStoreConfiguration::setPCMMachServiceName):
(WebKit::WebsiteDataStoreConfiguration::pcmMachServiceName const):

Source/WTF:

* wtf/PlatformHave.h:
* wtf/spi/darwin/XPCSPI.h:

Tools:

* TestWebKitAPI/Configurations/TestPCMDaemon.xcconfig: Added.
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm:
(TestWebKitAPI::currentExecutableLocation):
(TestWebKitAPI::testDaemonPList):
(TestWebKitAPI::TEST):
* TestWebKitAPI/cocoa/TestPCMDaemonMain.c: Copied from Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp.
(main):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (282367 => 282368)


--- trunk/Source/WTF/ChangeLog	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WTF/ChangeLog	2021-09-13 22:36:21 UTC (rev 282368)
@@ -1,3 +1,13 @@
+2021-09-13  Alex Christensen  <achristen...@webkit.org>
+
+        Add unit test that uses PCM daemon
+        https://bugs.webkit.org/show_bug.cgi?id=230191
+
+        Reviewed by Chris Dumez.
+
+        * wtf/PlatformHave.h:
+        * wtf/spi/darwin/XPCSPI.h:
+
 2021-09-13  Sihui Liu  <sihui_...@apple.com>
 
         Add stub for File System Access API

Modified: trunk/Source/WTF/wtf/PlatformHave.h (282367 => 282368)


--- trunk/Source/WTF/wtf/PlatformHave.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WTF/wtf/PlatformHave.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -813,6 +813,7 @@
     || (PLATFORM(APPLETV) && __TV_OS_VERSION_MIN_REQUIRED >= 150000)
 #define HAVE_NETWORK_LOADER 1
 #define HAVE_SEC_TRUST_COPY_CERTIFICATE_CHAIN 1
+#define HAVE_OS_LAUNCHD_JOB 1
 #endif
 
 #if PLATFORM(MACCATALYST) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000

Modified: trunk/Source/WTF/wtf/spi/darwin/XPCSPI.h (282367 => 282368)


--- trunk/Source/WTF/wtf/spi/darwin/XPCSPI.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WTF/wtf/spi/darwin/XPCSPI.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -114,8 +114,20 @@
 #if USE(APPLE_INTERNAL_SDK)
 #include <os/transaction_private.h>
 #include <xpc/private.h>
-#else
+#if HAVE(OS_LAUNCHD_JOB)
+#include <AppServerSupport/OSLaunchdJob.h>
+#endif // HAVE(OS_LAUNCHD_JOB)
+#else // USE(APPLE_INTERNAL_SDK)
 
+#ifdef __OBJC__
+#if HAVE(OS_LAUNCHD_JOB)
+@interface OSLaunchdJob : NSObject
+- (instancetype)initWithPlist:(xpc_object_t)plist;
+- (BOOL)submit:(NSError **)errorOut;
+@end
+#endif // HAVE(OS_LAUNCHD_JOB)
+#endif // __OBJC__
+
 extern "C" const char * const XPC_ACTIVITY_RANDOM_INITIAL_DELAY;
 extern "C" const char * const XPC_ACTIVITY_REQUIRE_NETWORK_CONNECTIVITY;
 

Modified: trunk/Source/WebKit/ChangeLog (282367 => 282368)


--- trunk/Source/WebKit/ChangeLog	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/ChangeLog	2021-09-13 22:36:21 UTC (rev 282368)
@@ -1,3 +1,66 @@
+2021-09-13  Alex Christensen  <achristen...@webkit.org>
+
+        Add unit test that uses PCM daemon
+        https://bugs.webkit.org/show_bug.cgi?id=230191
+
+        Reviewed by Chris Dumez.
+
+        I make a way to specify what mach service to connect to,
+        then I temporarily register an executable with launchd to provide that mach service,
+        then I run the test communicating with that executable.
+
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::managerOrProxy):
+        * NetworkProcess/NetworkSessionCreationParameters.cpp:
+        (WebKit::NetworkSessionCreationParameters::encode const):
+        (WebKit::NetworkSessionCreationParameters::decode):
+        * NetworkProcess/NetworkSessionCreationParameters.h:
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp:
+        (WebKit::PCM::Connection::Connection):
+        (WebKit::PCM::Connection::send const):
+        (WebKit::PCM::Connection::sendWithReply const):
+        (WebKit::PCM::Connection::connectionToDaemon): Deleted.
+        (WebKit::PCM::Connection::send): Deleted.
+        (WebKit::PCM::Connection::sendWithReply): Deleted.
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h:
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp:
+        (WebKit::PCM::managerPointer):
+        (WebKit::PCM::initializePCMStorageInDirectory):
+        (WebKit::PCM::daemonManager):
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h:
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp:
+        (WebKit::PCM::ManagerProxy::sendMessage const):
+        (WebKit::PCM::ManagerProxy::sendMessageWithReply const):
+        (WebKit::PCM::ManagerProxy::ManagerProxy):
+        (WebKit::PCM::sendMessage): Deleted.
+        (WebKit::PCM::sendMessageWithReply): Deleted.
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h:
+        * NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm:
+        (WebKit::PCM::Connection::Connection):
+        (WebKit::PCM::Connection::initializeConnectionIfNeeded const):
+        (WebKit::PCM::Connection::send const):
+        (WebKit::PCM::Connection::sendWithReply const):
+        (WebKit::PCM::Connection::connectionToDaemon): Deleted.
+        (WebKit::PCM::Connection::send): Deleted.
+        (WebKit::PCM::Connection::sendWithReply): Deleted.
+        * NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in:
+        * Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm:
+        (WebKit::startListeningForMachServiceConnections):
+        (WebKit::registerScheduledActivityHandler):
+        (WebKit::PCMDaemonMain):
+        * UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h:
+        * UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm:
+        (-[_WKWebsiteDataStoreConfiguration pcmMachServiceName]):
+        (-[_WKWebsiteDataStoreConfiguration setPCMMachServiceName:]):
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::parameters):
+        * UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp:
+        (WebKit::WebsiteDataStoreConfiguration::WebsiteDataStoreConfiguration):
+        (WebKit::WebsiteDataStoreConfiguration::copy const):
+        * UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h:
+        (WebKit::WebsiteDataStoreConfiguration::setPCMMachServiceName):
+        (WebKit::WebsiteDataStoreConfiguration::pcmMachServiceName const):
+
 2021-09-13  Tim Horton  <timothy_hor...@apple.com>
 
         Postprocess framework headers in parallel

Modified: trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -95,11 +95,8 @@
 
 static UniqueRef<PCM::ManagerInterface> managerOrProxy(NetworkSession& networkSession, NetworkProcess& networkProcess, const NetworkSessionCreationParameters& parameters)
 {
-    // FIXME: Turn this on and remove ManagerInterface once rdar://80701098 is closed.
-    constexpr bool usePCMDaemon = false;
-
-    if (usePCMDaemon)
-        return makeUniqueRef<PCM::ManagerProxy>();
+    if (!parameters.pcmMachServiceName.isEmpty())
+        return makeUniqueRef<PCM::ManagerProxy>(parameters.pcmMachServiceName);
     return makeUniqueRef<PrivateClickMeasurementManager>(makeUniqueRef<PCM::ClientImpl>(networkSession, networkProcess), pcmStoreDirectory(networkSession, parameters.resourceLoadStatisticsParameters.directory, parameters.resourceLoadStatisticsParameters.privateClickMeasurementStorageDirectory));
 }
 

Modified: trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -87,6 +87,7 @@
     encoder << appHasRequestedCrossWebsiteTrackingPermission;
     encoder << useNetworkLoader;
     encoder << allowsHSTSWithUntrustedRootCertificate;
+    encoder << pcmMachServiceName;
     encoder << resourceLoadStatisticsParameters;
 }
 
@@ -293,12 +294,17 @@
     decoder >> allowsHSTSWithUntrustedRootCertificate;
     if (!allowsHSTSWithUntrustedRootCertificate)
         return std::nullopt;
-    
+
+    std::optional<String> pcmMachServiceName;
+    decoder >> pcmMachServiceName;
+    if (!pcmMachServiceName)
+        return std::nullopt;
+
     std::optional<ResourceLoadStatisticsParameters> resourceLoadStatisticsParameters;
     decoder >> resourceLoadStatisticsParameters;
     if (!resourceLoadStatisticsParameters)
         return std::nullopt;
-    
+
     return {{
         *sessionID
         , WTFMove(*boundInterfaceIdentifier)
@@ -347,6 +353,7 @@
         , WTFMove(*appHasRequestedCrossWebsiteTrackingPermission)
         , WTFMove(*useNetworkLoader)
         , WTFMove(*allowsHSTSWithUntrustedRootCertificate)
+        , WTFMove(*pcmMachServiceName)
         , WTFMove(*resourceLoadStatisticsParameters)
     }};
 }

Modified: trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -102,6 +102,7 @@
     bool appHasRequestedCrossWebsiteTrackingPermission { false };
     bool useNetworkLoader { false };
     bool allowsHSTSWithUntrustedRootCertificate { false };
+    String pcmMachServiceName;
 
     ResourceLoadStatisticsParameters resourceLoadStatisticsParameters;
 };

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -34,18 +34,17 @@
 
 #if !PLATFORM(COCOA)
 
-Connection Connection::connectionToDaemon()
+Connection::Connection(CString&&)
 {
     notImplemented();
-    return { };
 }
 
-void Connection::send(MessageType, EncodedMessage&&)
+void Connection::send(MessageType, EncodedMessage&&) const
 {
     notImplemented();
 }
 
-void Connection::sendWithReply(MessageType, EncodedMessage&&, CompletionHandler<void(EncodedMessage&&)>&& completionHandler)
+void Connection::sendWithReply(MessageType, EncodedMessage&&, CompletionHandler<void(EncodedMessage&&)>&& completionHandler) const
 {
     notImplemented();
     completionHandler({ });

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
 
 #if PLATFORM(COCOA)
 #include <wtf/OSObjectPtr.h>
@@ -39,18 +40,19 @@
 enum class MessageType : uint8_t;
 using EncodedMessage = Vector<uint8_t>;
 
-class Connection {
+class Connection : public CanMakeWeakPtr<Connection> {
 public:
-    static Connection connectionToDaemon();
+    explicit Connection(CString&& machServiceName);
 
-    void send(MessageType, EncodedMessage&&);
-    void sendWithReply(MessageType, EncodedMessage&&, CompletionHandler<void(EncodedMessage&&)>&&);
+    void send(MessageType, EncodedMessage&&) const;
+    void sendWithReply(MessageType, EncodedMessage&&, CompletionHandler<void(EncodedMessage&&)>&&) const;
 
 private:
 #if PLATFORM(COCOA)
-    Connection(OSObjectPtr<xpc_connection_t>&&);
+    void initializeConnectionIfNeeded() const;
 
-    OSObjectPtr<xpc_connection_t> m_connection;
+    const CString m_machServiceName;
+    mutable OSObjectPtr<xpc_connection_t> m_connection;
 #endif
 };
 

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -167,11 +167,22 @@
     return false;
 }
 
+static std::unique_ptr<PrivateClickMeasurementManager>& managerPointer()
+{
+    static NeverDestroyed<std::unique_ptr<PrivateClickMeasurementManager>> manager;
+    return manager.get();
+}
+
+void initializePCMStorageInDirectory(const String& storageDirectory)
+{
+    ASSERT(!managerPointer());
+    managerPointer() = makeUnique<PrivateClickMeasurementManager>(makeUniqueRef<PCM::DaemonClient>(), storageDirectory);
+}
+
 static PrivateClickMeasurementManager& daemonManager()
 {
-    // FIXME: Give the manager a valid storage directory.
-    static NeverDestroyed<PrivateClickMeasurementManager> instance(makeUniqueRef<PCM::DaemonClient>(), String());
-    return instance.get();
+    ASSERT(managerPointer());
+    return *managerPointer();
 }
 
 template<typename Info>

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -101,6 +101,8 @@
 void decodeMessageAndSendToManager(MessageType, Vector<uint8_t>&& message, CompletionHandler<void(Vector<uint8_t>&&)>&&);
 bool messageTypeSendsReply(MessageType);
 
+void initializePCMStorageInDirectory(const String&);
+
 } // namespace PCM
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -35,11 +35,11 @@
 namespace PCM {
 
 template<MessageType messageType, typename... Args>
-void sendMessage(Args&&... args)
+void ManagerProxy::sendMessage(Args&&... args) const
 {
     Encoder encoder;
     encoder.encode(std::forward<Args>(args)...);
-    Connection::connectionToDaemon().send(messageType, encoder.takeBuffer());
+    m_connection.send(messageType, encoder.takeBuffer());
 }
 
 template<typename... Args> struct ReplyCaller;
@@ -61,16 +61,19 @@
 };
 
 template<MessageType messageType, typename... Args, typename... ReplyArgs>
-void sendMessageWithReply(CompletionHandler<void(ReplyArgs...)>&& completionHandler, Args&&... args)
+void ManagerProxy::sendMessageWithReply(CompletionHandler<void(ReplyArgs...)>&& completionHandler, Args&&... args) const
 {
     Encoder encoder;
     encoder.encode(std::forward<Args>(args)...);
-    Connection::connectionToDaemon().sendWithReply(messageType, encoder.takeBuffer(), [completionHandler = WTFMove(completionHandler)] (auto replyBuffer) mutable {
+    m_connection.sendWithReply(messageType, encoder.takeBuffer(), [completionHandler = WTFMove(completionHandler)] (auto replyBuffer) mutable {
         Decoder decoder(WTFMove(replyBuffer));
         ReplyCaller<ReplyArgs...>::callReply(WTFMove(decoder), WTFMove(completionHandler));
     });
 }
 
+ManagerProxy::ManagerProxy(const String& machServiceName)
+    : m_connection(machServiceName.utf8()) { }
+
 void ManagerProxy::storeUnattributed(WebCore::PrivateClickMeasurement&& pcm)
 {
     sendMessage<MessageType::StoreUnattributed>(pcm);

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -25,8 +25,10 @@
 
 #pragma once
 
+#include "PrivateClickMeasurementConnection.h"
 #include "PrivateClickMeasurementManagerInterface.h"
 #include <wtf/FastMalloc.h>
+#include <wtf/text/CString.h>
 
 namespace WebKit {
 
@@ -35,6 +37,8 @@
 class ManagerProxy : public ManagerInterface {
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    ManagerProxy(const String& machServiceName);
+
     void storeUnattributed(WebCore::PrivateClickMeasurement&&) final;
     void handleAttribution(WebCore::PrivateClickMeasurement::AttributionTriggerData&&, const URL& requestURL, WebCore::RegistrableDomain&& redirectDomain, const URL& firstPartyURL) final;
     void clear(CompletionHandler<void()>&&) final;
@@ -53,6 +57,14 @@
     void startTimerImmediatelyForTesting() final;
     void destroyStoreForTesting(CompletionHandler<void()>&&) final;
     void allowTLSCertificateChainForLocalPCMTesting(const WebCore::CertificateInfo&) final;
+
+private:
+    template<MessageType messageType, typename... Args>
+    void sendMessage(Args&&...) const;
+    template<MessageType messageType, typename... Args, typename... ReplyArgs>
+    void sendMessageWithReply(CompletionHandler<void(ReplyArgs...)>&&, Args&&...) const;
+
+    Connection m_connection;
 };
 
 } // namespace PCM

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm	2021-09-13 22:36:21 UTC (rev 282368)
@@ -33,26 +33,25 @@
 
 namespace PCM {
 
-Connection::Connection(OSObjectPtr<xpc_connection_t>&& connection)
-    : m_connection(WTFMove(connection)) { }
+Connection::Connection(CString&& machServiceName)
+    : m_machServiceName(WTFMove(machServiceName)) { }
 
-Connection Connection::connectionToDaemon()
+void Connection::initializeConnectionIfNeeded() const
 {
-    ASSERT(RunLoop::isMain());
-    static NeverDestroyed<OSObjectPtr<xpc_connection_t>> connection;
-    if (!connection.get()) {
-        connection.get() = adoptOSObject(xpc_connection_create_mach_service("com.apple.webkit.adattributiond.service", dispatch_get_main_queue(), 0));
-        xpc_connection_set_event_handler(connection.get().get(), ^(xpc_object_t event) {
-            ASSERT(RunLoop::isMain());
-            if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
-                // Daemon crashed, we will need to make a new connection to a new instance of the daemon.
-                connection.get() = nullptr;
-            } else
-                ASSERT_NOT_REACHED();
-        });
-        xpc_connection_activate(connection.get().get());
-    }
-    return Connection(OSObjectPtr<xpc_connection_t>(connection.get()));
+    if (m_connection)
+        return;
+    m_connection = adoptOSObject(xpc_connection_create_mach_service(m_machServiceName.data(), dispatch_get_main_queue(), 0));
+    xpc_connection_set_event_handler(m_connection.get(), [weakThis = makeWeakPtr(*this)](xpc_object_t event) {
+        if (!weakThis)
+            return;
+        if (event == XPC_ERROR_CONNECTION_INVALID)
+            WTFLogAlways("Failed to connect to mach service %s, likely because it is not registered with launchd", weakThis->m_machServiceName.data());
+        if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
+            // Daemon crashed, we will need to make a new connection to a new instance of the daemon.
+            weakThis->m_connection = nullptr;
+        }
+    });
+    xpc_connection_activate(m_connection.get());
 }
 
 static OSObjectPtr<xpc_object_t> dictionaryFromMessage(MessageType messageType, EncodedMessage&& message)
@@ -63,25 +62,22 @@
     return dictionary;
 }
 
-void Connection::send(MessageType messageType, EncodedMessage&& message)
+void Connection::send(MessageType messageType, EncodedMessage&& message) const
 {
-    if (!m_connection) {
-        ASSERT_NOT_REACHED();
-        return;
-    }
+    ASSERT(RunLoop::isMain());
+    initializeConnectionIfNeeded();
 
     xpc_connection_send_message(m_connection.get(), dictionaryFromMessage(messageType, WTFMove(message)).get());
 }
 
-void Connection::sendWithReply(MessageType messageType, EncodedMessage&& message, CompletionHandler<void(EncodedMessage&&)>&& completionHandler)
+void Connection::sendWithReply(MessageType messageType, EncodedMessage&& message, CompletionHandler<void(EncodedMessage&&)>&& completionHandler) const
 {
-    if (!m_connection) {
-        ASSERT_NOT_REACHED();
-        return completionHandler({ });
-    }
+    ASSERT(RunLoop::isMain());
+    initializeConnectionIfNeeded();
 
     auto dictionary = dictionaryFromMessage(messageType, WTFMove(message));
     xpc_connection_send_message_with_reply(m_connection.get(), dictionary.get(), dispatch_get_main_queue(), makeBlockPtr([completionHandler = WTFMove(completionHandler)] (xpc_object_t reply) mutable {
+        ASSERT(RunLoop::isMain());
         if (xpc_get_type(reply) != XPC_TYPE_DICTIONARY) {
             ASSERT_NOT_REACHED();
             return completionHandler({ });

Modified: trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in (282367 => 282368)


--- trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in	2021-09-13 22:36:21 UTC (rev 282368)
@@ -358,6 +358,7 @@
 )
 
 (allow mach-lookup (global-name "com.apple.webkit.adattributiond.service"))
+(allow mach-lookup (global-name "org.webkit.pcmtestdaemon.service"))
 
 (with-filter (uid 0)
     (allow mach-lookup

Modified: trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm (282367 => 282368)


--- trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm	2021-09-13 22:36:21 UTC (rev 282368)
@@ -31,6 +31,7 @@
 #import "PrivateClickMeasurementXPCUtilities.h"
 #import <Foundation/Foundation.h>
 #import <wtf/CompletionHandler.h>
+#import <wtf/FileSystem.h>
 #import <wtf/HashSet.h>
 #import <wtf/NeverDestroyed.h>
 #import <wtf/RetainPtr.h>
@@ -76,9 +77,9 @@
     decodeMessageAndSendToManager(messageType, WTFMove(encodedMessage), replySender(messageType, request));
 }
 
-static void startListeningForMachServiceConnections()
+static void startListeningForMachServiceConnections(const char* serviceName)
 {
-    static NeverDestroyed<OSObjectPtr<xpc_connection_t>> listener = xpc_connection_create_mach_service("com.apple.webkit.adattributiond.service", dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_LISTENER);
+    static NeverDestroyed<OSObjectPtr<xpc_connection_t>> listener = xpc_connection_create_mach_service(serviceName, dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_LISTENER);
     xpc_connection_set_event_handler(listener.get().get(), ^(xpc_object_t peer) {
         if (xpc_get_type(peer) != XPC_TYPE_CONNECTION)
             return;
@@ -86,6 +87,8 @@
         // FIXME: Add an entitlement check here so that only the network process can successfully connect.
 
         xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
+            if (event == XPC_ERROR_CONNECTION_INVALID)
+                NSLog(@"Failed to start listening for connections to mach service %s, likely because it is not registered with launchd", serviceName);
             if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
                 NSLog(@"removing peer connection %p", peer);
                 peers().remove(peer);
@@ -104,6 +107,7 @@
 
 static void registerScheduledActivityHandler()
 {
+    NSLog(@"Registering XPC activity");
     xpc_activity_register("com.apple.webkit.adattributiond.activity", XPC_ACTIVITY_CHECK_IN, ^(xpc_activity_t activity) {
         if (xpc_activity_get_state(activity) == XPC_ACTIVITY_STATE_CHECK_IN) {
             xpc_object_t criteria = xpc_activity_copy_criteria(activity);
@@ -125,15 +129,21 @@
 
 int PCMDaemonMain(int argc, const char** argv)
 {
-    if (argc != 2 || strcmp(argv[1], "runAsDaemon")) {
-        NSLog(@"This executable is intended to be run as a daemon.");
+    if (argc < 5 || strcmp(argv[1], "--machServiceName") || strcmp(argv[3], "--storageLocation")) {
+        NSLog(@"Usage: %s --machServiceName <name> --storageLocation <location> [--startActivity]", argv[0]);
         return -1;
     }
+    const char* machServiceName = argv[2];
+    const char* storageLocation = argv[4];
+    bool startActivity = argc > 5 && !strcmp(argv[5], "--startActivity");
+
     @autoreleasepool {
         enterSandbox();
-        startListeningForMachServiceConnections();
-        registerScheduledActivityHandler();
+        startListeningForMachServiceConnections(machServiceName);
+        if (startActivity)
+            registerScheduledActivityHandler();
         WTF::initializeMainThread();
+        PCM::initializePCMStorageInDirectory(FileSystem::stringFromFileSystemRepresentation(storageLocation));
     }
     CFRunLoopRun();
     return 0;

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h (282367 => 282368)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -76,6 +76,7 @@
 @property (nonatomic, nullable, copy, setter=setHSTSStorageDirectory:) NSURL *hstsStorageDirectory WK_API_AVAILABLE(macos(12.0), ios(15.0));
 @property (nonatomic) BOOL enableInAppBrowserPrivacyForTesting WK_API_AVAILABLE(macos(12.0), ios(15.0));
 @property (nonatomic) BOOL allowsHSTSWithUntrustedRootCertificate WK_API_AVAILABLE(macos(12.0), ios(15.0));
+@property (nonatomic, nullable, copy, setter=setPCMMachServiceName:) NSString *pcmMachServiceName WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @property (nonatomic, nullable, copy) NSURL *alternativeServicesStorageDirectory WK_API_AVAILABLE(macos(11.0), ios(14.0));
 @property (nonatomic, nullable, copy) NSURL *standaloneApplicationURL WK_API_AVAILABLE(macos(11.0), ios(14.0));

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm (282367 => 282368)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm	2021-09-13 22:36:21 UTC (rev 282368)
@@ -521,6 +521,16 @@
     _configuration->setAllowsHSTSWithUntrustedRootCertificate(allows);
 }
 
+- (NSString *)pcmMachServiceName
+{
+    return _configuration->pcmMachServiceName();
+}
+
+- (void)setPCMMachServiceName:(NSString *)name
+{
+    _configuration->setPCMMachServiceName(name);
+}
+
 - (BOOL)allLoadsBlockedByDeviceManagementRestrictionsForTesting
 {
     return _configuration->allLoadsBlockedByDeviceManagementRestrictionsForTesting();

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp (282367 => 282368)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -2060,6 +2060,7 @@
     networkSessionParameters.requiresSecureHTTPSProxyConnection = m_configuration->requiresSecureHTTPSProxyConnection();
     networkSessionParameters.preventsSystemHTTPProxyAuthentication = m_configuration->preventsSystemHTTPProxyAuthentication();
     networkSessionParameters.allowsHSTSWithUntrustedRootCertificate = m_configuration->allowsHSTSWithUntrustedRootCertificate();
+    networkSessionParameters.pcmMachServiceName = m_configuration->pcmMachServiceName();
 
     parameters.networkSessionParameters = WTFMove(networkSessionParameters);
 

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp (282367 => 282368)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp	2021-09-13 22:36:21 UTC (rev 282368)
@@ -51,6 +51,7 @@
 #if HAVE(ARKIT_INLINE_PREVIEW)
         setModelElementCacheDirectory(WebsiteDataStore::defaultModelElementCacheDirectory());
 #endif
+        // FIXME: Give m_pcmMachServiceName a default value and remove PCM::ManagerInterface once rdar://80701098 is closed.
     }
 }
 
@@ -100,6 +101,7 @@
     copy->m_standaloneApplicationURL = this->m_standaloneApplicationURL;
     copy->m_enableInAppBrowserPrivacyForTesting = this->m_enableInAppBrowserPrivacyForTesting;
     copy->m_allowsHSTSWithUntrustedRootCertificate = this->m_allowsHSTSWithUntrustedRootCertificate;
+    copy->m_pcmMachServiceName = this->m_pcmMachServiceName;
 #if PLATFORM(COCOA)
     if (m_proxyConfiguration)
         copy->m_proxyConfiguration = adoptCF(CFDictionaryCreateCopy(nullptr, this->m_proxyConfiguration.get()));

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h (282367 => 282368)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h	2021-09-13 22:36:21 UTC (rev 282368)
@@ -180,6 +180,9 @@
     bool allowsHSTSWithUntrustedRootCertificate() const { return m_allowsHSTSWithUntrustedRootCertificate; }
     void setAllowsHSTSWithUntrustedRootCertificate(bool allows) { m_allowsHSTSWithUntrustedRootCertificate = allows; }
     
+    void setPCMMachServiceName(String&& name) { m_pcmMachServiceName = WTFMove(name); }
+    const String& pcmMachServiceName() const { return m_pcmMachServiceName; }
+
 private:
     IsPersistent m_isPersistent { IsPersistent::No };
 
@@ -232,6 +235,7 @@
     URL m_standaloneApplicationURL;
     bool m_enableInAppBrowserPrivacyForTesting { false };
     bool m_allowsHSTSWithUntrustedRootCertificate { false };
+    String m_pcmMachServiceName;
 #if PLATFORM(COCOA)
     RetainPtr<CFDictionaryRef> m_proxyConfiguration;
 #endif

Modified: trunk/Tools/ChangeLog (282367 => 282368)


--- trunk/Tools/ChangeLog	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Tools/ChangeLog	2021-09-13 22:36:21 UTC (rev 282368)
@@ -1,3 +1,19 @@
+2021-09-13  Alex Christensen  <achristen...@webkit.org>
+
+        Add unit test that uses PCM daemon
+        https://bugs.webkit.org/show_bug.cgi?id=230191
+
+        Reviewed by Chris Dumez.
+
+        * TestWebKitAPI/Configurations/TestPCMDaemon.xcconfig: Added.
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm:
+        (TestWebKitAPI::currentExecutableLocation):
+        (TestWebKitAPI::testDaemonPList):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/cocoa/TestPCMDaemonMain.c: Copied from Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp.
+        (main):
+
 2021-09-13  Chris Dumez  <cdu...@apple.com>
 
         Crash under WebPage::runJavaScript()

Added: trunk/Tools/TestWebKitAPI/Configurations/TestPCMDaemon.xcconfig (0 => 282368)


--- trunk/Tools/TestWebKitAPI/Configurations/TestPCMDaemon.xcconfig	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Configurations/TestPCMDaemon.xcconfig	2021-09-13 22:36:21 UTC (rev 282368)
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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. ``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
+// 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. 
+
+PRODUCT_NAME = TestPCMDaemon;
+
+OTHER_LDFLAGS = -framework WebKit;
+FRAMEWORK_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR);

Modified: trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI-macOS-internal.entitlements (282367 => 282368)


--- trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI-macOS-internal.entitlements	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI-macOS-internal.entitlements	2021-09-13 22:36:21 UTC (rev 282368)
@@ -2,6 +2,8 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>com.apple.private.xpc.launchd.job-manager</key>
+	<string>TestWebKitAPI</string>
 	<key>com.apple.hid.manager.user-access-device</key>
 	<true/>
 	<key>com.apple.private.hid.client.event-filter</key>

Modified: trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI-macOS.entitlements (282367 => 282368)


--- trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI-macOS.entitlements	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI-macOS.entitlements	2021-09-13 22:36:21 UTC (rev 282368)
@@ -2,6 +2,8 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>com.apple.private.xpc.launchd.job-manager</key>
+	<string>TestWebKitAPI</string>
 	<key>keychain-access-groups</key>
 	<array>
 		<string>com.apple.TestWebKitAPI</string>

Modified: trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig (282367 => 282368)


--- trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig	2021-09-13 22:36:21 UTC (rev 282368)
@@ -43,6 +43,10 @@
 FRAMEWORK_SEARCH_PATHS_ = $(inherited) $(WK_PRIVATE_FRAMEWORKS_DIR) $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/WebKit.framework/Versions/A/Frameworks;
 FRAMEWORK_SEARCH_PATHS_cocoatouch = $(inherited) $(WK_PRIVATE_FRAMEWORKS_DIR);
 
+WK_APPSERVERSUPPORT_LDFLAGS = $(WK_APPSERVERSUPPORT_LDFLAGS_$(WK_PLATFORM_NAME));
+WK_APPSERVERSUPPORT_LDFLAGS_macosx = $(WK_APPSERVERSUPPORT_LDFLAGS$(WK_MACOS_1200));
+WK_APPSERVERSUPPORT_LDFLAGS_MACOS_SINCE_1200 = -framework AppServerSupport
+
 WK_AUTHKIT_LDFLAGS = $(WK_AUTHKIT_LDFLAGS_$(WK_PLATFORM_NAME));
 WK_AUTHKIT_LDFLAGS_iphoneos = $(WK_AUTHKIT_LDFLAGS$(WK_IOS_13));
 WK_AUTHKIT_LDFLAGS_iphonesimulator = $(WK_AUTHKIT_LDFLAGS$(WK_IOS_13));
@@ -86,7 +90,7 @@
 
 OTHER_CPLUSPLUSFLAGS = $(inherited) -isystem $(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders;
 
-OTHER_LDFLAGS = $(inherited) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework _javascript_Core -framework WebKit -lWebCoreTestSupport $(WK_AUTHKIT_LDFLAGS) -framework Network $(WK_HID_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SYSTEM_LDFLAGS) $(WK_UIKITMACHELPER_LDFLAGS) $(WK_VISIONKITCORE_LDFLAGS) $(OTHER_LDFLAGS_PLATFORM_$(WK_COCOA_TOUCH));
+OTHER_LDFLAGS = $(inherited) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework _javascript_Core -framework WebKit -lWebCoreTestSupport $(WK_APPSERVERSUPPORT_LDFLAGS) $(WK_AUTHKIT_LDFLAGS) -framework Network $(WK_HID_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SYSTEM_LDFLAGS) $(WK_UIKITMACHELPER_LDFLAGS) $(WK_VISIONKITCORE_LDFLAGS) $(OTHER_LDFLAGS_PLATFORM_$(WK_COCOA_TOUCH));
 OTHER_LDFLAGS_PLATFORM_ = -framework Cocoa -framework Carbon;
 
 // FIXME: This should not be built on iOS. Instead we should create and use a TestWebKitAPI application.

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (282367 => 282368)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-09-13 22:36:21 UTC (rev 282368)
@@ -36,6 +36,7 @@
 				537CF84822EFD72000C6EBB3 /* Check .xcfilelists */,
 			);
 			dependencies = (
+				5C1B1D1C26EC2EEC00882DA2 /* PBXTargetDependency */,
 				7C83E0301D0A5E1B00FEBCF3 /* PBXTargetDependency */,
 				7C83E0321D0A5E1D00FEBCF3 /* PBXTargetDependency */,
 			);
@@ -478,6 +479,7 @@
 		5C121E8D2410704900486F9B /* ContentWorldPlugIn.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C121E8C2410703200486F9B /* ContentWorldPlugIn.mm */; };
 		5C16F8FC230C94370074C4A8 /* TextSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C16F8FB230C942B0074C4A8 /* TextSize.mm */; };
 		5C19A5241FD0F60100EEA323 /* CookiePrivateBrowsing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C19A5231FD0F32600EEA323 /* CookiePrivateBrowsing.mm */; };
+		5C1B1D1726EC27DE00882DA2 /* TestPCMDaemonMain.c in Sources */ = {isa = PBXBuildFile; fileRef = 5C1B1D1626EC27C500882DA2 /* TestPCMDaemonMain.c */; };
 		5C23DF0B2246015800F454B6 /* Challenge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C23DF0A2245C9D700F454B6 /* Challenge.mm */; };
 		5C2936931D5BF70D00DEAB1E /* CookieAcceptPolicy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */; };
 		5C2936961D5C00ED00DEAB1E /* CookieMessage.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5C2936941D5BFD1900DEAB1E /* CookieMessage.html */; };
@@ -1297,6 +1299,13 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
+		5C1B1D1B26EC2EEC00882DA2 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 5C1B1D0826EC278500882DA2;
+			remoteInfo = TestPCMDaemon;
+		};
 		5C9D922122D7DC84008E9266 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
@@ -2331,6 +2340,9 @@
 		5C121E8C2410703200486F9B /* ContentWorldPlugIn.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ContentWorldPlugIn.mm; sourceTree = "<group>"; };
 		5C16F8FB230C942B0074C4A8 /* TextSize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextSize.mm; sourceTree = "<group>"; };
 		5C19A5231FD0F32600EEA323 /* CookiePrivateBrowsing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CookiePrivateBrowsing.mm; sourceTree = "<group>"; };
+		5C1B1D1426EC278500882DA2 /* TestPCMDaemon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TestPCMDaemon; sourceTree = BUILT_PRODUCTS_DIR; };
+		5C1B1D1626EC27C500882DA2 /* TestPCMDaemonMain.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = TestPCMDaemonMain.c; path = cocoa/TestPCMDaemonMain.c; sourceTree = "<group>"; };
+		5C1B1D1A26EC284E00882DA2 /* TestPCMDaemon.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestPCMDaemon.xcconfig; sourceTree = "<group>"; };
 		5C23DF0A2245C9D700F454B6 /* Challenge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Challenge.mm; sourceTree = "<group>"; };
 		5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CookieAcceptPolicy.mm; sourceTree = "<group>"; };
 		5C2936941D5BFD1900DEAB1E /* CookieMessage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = CookieMessage.html; sourceTree = "<group>"; };
@@ -3162,6 +3174,13 @@
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		5C1B1D0E26EC278500882DA2 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		7C83DF641D0A590C00FEBCF3 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -3306,6 +3325,7 @@
 				5C72E8CE244FFCE400381EB7 /* TestLegacyDownloadDelegate.mm */,
 				2D1C04A51D76298B000A6816 /* TestNavigationDelegate.h */,
 				2D1C04A61D76298B000A6816 /* TestNavigationDelegate.mm */,
+				5C1B1D1626EC27C500882DA2 /* TestPCMDaemonMain.c */,
 				516281232325C17A00BB7E42 /* TestPDFDocument.h */,
 				516281242325C17B00BB7E42 /* TestPDFDocument.mm */,
 				A14FC58D1B8AE36500D107EB /* TestProtocol.h */,
@@ -3364,6 +3384,7 @@
 		1AB674ADFE9D54B511CA2CBB /* Products */ = {
 			isa = PBXGroup;
 			children = (
+				5C1B1D1426EC278500882DA2 /* TestPCMDaemon */,
 				8DD76FA10486AA7600D96B5E /* TestWebKitAPI */,
 				7C83E0231D0A5AE400FEBCF3 /* TestWTF */,
 				BC575980126E74AF006F0F12 /* InjectedBundleTestWebKitAPI.bundle */,
@@ -4293,6 +4314,7 @@
 				BC90957F12554CF900083756 /* DebugRelease.xcconfig */,
 				BC575AE2126E88B1006F0F12 /* InjectedBundle.xcconfig */,
 				A1B89B99221E029F00EB4CEA /* SDKVariant.xcconfig */,
+				5C1B1D1A26EC284E00882DA2 /* TestPCMDaemon.xcconfig */,
 				5735F0251F3A4EA6000EE801 /* TestWebKitAPI-iOS.entitlements */,
 				51EB125324C66BCB000CB030 /* TestWebKitAPI-macOS-internal.entitlements */,
 				7AB0173923FB2BF0002F8366 /* TestWebKitAPI-macOS.entitlements */,
@@ -4979,6 +5001,23 @@
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
+		5C1B1D0826EC278500882DA2 /* TestPCMDaemon */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5C1B1D1126EC278500882DA2 /* Build configuration list for PBXNativeTarget "TestPCMDaemon" */;
+			buildPhases = (
+				5C1B1D0B26EC278500882DA2 /* Sources */,
+				5C1B1D0E26EC278500882DA2 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = TestPCMDaemon;
+			productInstallPath = "$(HOME)/bin";
+			productName = TestPCMDaemon;
+			productReference = 5C1B1D1426EC278500882DA2 /* TestPCMDaemon */;
+			productType = "com.apple.product-type.tool";
+		};
 		7C83DE951D0A590C00FEBCF3 /* TestWTFLibrary */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 7C83DF651D0A590C00FEBCF3 /* Build configuration list for PBXNativeTarget "TestWTFLibrary" */;
@@ -5138,6 +5177,7 @@
 				BC57597F126E74AF006F0F12 /* InjectedBundleTestWebKitAPI */,
 				A13EBB481B87339E00097110 /* WebProcessPlugIn */,
 				537CF84322EFD64100C6EBB3 /* Apply Configuration to XCFileLists */,
+				5C1B1D0826EC278500882DA2 /* TestPCMDaemon */,
 			);
 		};
 /* End PBXProject section */
@@ -5214,6 +5254,14 @@
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+		5C1B1D0B26EC278500882DA2 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5C1B1D1726EC27DE00882DA2 /* TestPCMDaemonMain.c in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		7C83DE961D0A590C00FEBCF3 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -6078,6 +6126,11 @@
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+		5C1B1D1C26EC2EEC00882DA2 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 5C1B1D0826EC278500882DA2 /* TestPCMDaemon */;
+			targetProxy = 5C1B1D1B26EC2EEC00882DA2 /* PBXContainerItemProxy */;
+		};
 		5C9D922222D7DC84008E9266 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = 5C9D921422D7DA02008E9266 /* Generate Unified Sources */;
@@ -6160,6 +6213,22 @@
 			};
 			name = Release;
 		};
+		5C1B1D1226EC278500882DA2 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 5C1B1D1A26EC284E00882DA2 /* TestPCMDaemon.xcconfig */;
+			buildSettings = {
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		5C1B1D1326EC278500882DA2 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 5C1B1D1A26EC284E00882DA2 /* TestPCMDaemon.xcconfig */;
+			buildSettings = {
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 		5C9D921A22D7DA02008E9266 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -6288,6 +6357,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		5C1B1D1126EC278500882DA2 /* Build configuration list for PBXNativeTarget "TestPCMDaemon" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5C1B1D1226EC278500882DA2 /* Debug */,
+				5C1B1D1326EC278500882DA2 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		5C9D921922D7DA02008E9266 /* Build configuration list for PBXAggregateTarget "Generate Unified Sources" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm (282367 => 282368)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm	2021-09-13 22:26:19 UTC (rev 282367)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm	2021-09-13 22:36:21 UTC (rev 282368)
@@ -27,13 +27,18 @@
 
 #import "HTTPServer.h"
 #import "PlatformUtilities.h"
+#import "Test.h"
 #import "TestNavigationDelegate.h"
 #import "TestWKWebView.h"
 #import "Utilities.h"
+#import <WebKit/WKMain.h>
 #import <WebKit/WKWebViewPrivate.h>
 #import <WebKit/WKWebViewPrivateForTesting.h>
 #import <WebKit/WKWebsiteDataStorePrivate.h>
 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
+#import <mach-o/dyld.h>
+#import <wtf/OSObjectPtr.h>
+#import <wtf/spi/darwin/XPCSPI.h>
 
 #if HAVE(RSA_BSSA)
 
@@ -360,6 +365,130 @@
     EXPECT_EQ(webViewToKeepNetworkProcessAlive.get().configuration.websiteDataStore._networkProcessIdentifier, originalNetworkProcessPid);
 }
 
+// FIXME: Get this working in the iOS simulator.
+#if PLATFORM(MAC)
+
+static RetainPtr<NSURL> currentExecutableLocation()
+{
+    uint32_t size { 0 };
+    _NSGetExecutablePath(nullptr, &size);
+    Vector<char> buffer;
+    buffer.resize(size + 1);
+    _NSGetExecutablePath(buffer.data(), &size);
+    buffer[size] = '\0';
+    auto pathString = adoptNS([[NSString alloc] initWithUTF8String:buffer.data()]);
+    return adoptNS([[NSURL alloc] initFileURLWithPath:pathString.get() isDirectory:NO]);
+}
+
+static RetainPtr<NSURL> currentExecutableDirectory()
+{
+    return [currentExecutableLocation() URLByDeletingLastPathComponent];
+}
+
+static RetainPtr<NSURL> testPCMDaemonLocation()
+{
+    return [currentExecutableDirectory() URLByAppendingPathComponent:@"TestPCMDaemon" isDirectory:NO];
+}
+
+#if HAVE(OS_LAUNCHD_JOB)
+
+static OSObjectPtr<xpc_object_t> testDaemonPList(NSURL *storageLocation)
+{
+    auto plist = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
+    xpc_dictionary_set_string(plist.get(), "_ManagedBy", "TestWebKitAPI");
+    xpc_dictionary_set_string(plist.get(), "Label", "org.webkit.pcmtestdaemon");
+    xpc_dictionary_set_bool(plist.get(), "LaunchOnlyOnce", true);
+
+    {
+        auto environmentVariables = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
+        xpc_dictionary_set_string(environmentVariables.get(), "DYLD_FRAMEWORK_PATH", currentExecutableDirectory().get().fileSystemRepresentation);
+        xpc_dictionary_set_value(plist.get(), "EnvironmentVariables", environmentVariables.get());
+    }
+    {
+        auto machServices = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
+        xpc_dictionary_set_bool(machServices.get(), "org.webkit.pcmtestdaemon.service", true);
+        xpc_dictionary_set_value(plist.get(), "MachServices", machServices.get());
+    }
+    {
+        auto programArguments = adoptOSObject(xpc_array_create(nullptr, 0));
+        auto executableLocation = testPCMDaemonLocation();
+        xpc_array_set_string(programArguments.get(), XPC_ARRAY_APPEND, executableLocation.get().fileSystemRepresentation);
+        xpc_array_set_string(programArguments.get(), XPC_ARRAY_APPEND, "--machServiceName");
+        xpc_array_set_string(programArguments.get(), XPC_ARRAY_APPEND, "org.webkit.pcmtestdaemon.service");
+        xpc_array_set_string(programArguments.get(), XPC_ARRAY_APPEND, "--storageLocation");
+        xpc_array_set_string(programArguments.get(), XPC_ARRAY_APPEND, storageLocation.fileSystemRepresentation);
+        xpc_dictionary_set_value(plist.get(), "ProgramArguments", programArguments.get());
+    }
+    return plist;
+}
+
+#else
+
+static RetainPtr<NSDictionary> testDaemonPList(NSURL *storageLocation)
+{
+    return @{
+        @"Label" : @"org.webkit.pcmtestdaemon",
+        @"LaunchOnlyOnce" : @YES,
+        @"EnvironmentVariables" : @{ @"DYLD_FRAMEWORK_PATH" : currentExecutableDirectory().get().path },
+        @"MachServices" : @{ @"org.webkit.pcmtestdaemon.service" : @YES },
+        @"ProgramArguments" : @[
+            testPCMDaemonLocation().get().path,
+            @"--machServiceName",
+            @"org.webkit.pcmtestdaemon.service",
+            @"--storageLocation",
+            storageLocation.path
+        ]
+    };
+}
+
+#endif
+
+TEST(EventAttribution, Daemon)
+{
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"EventAttributionDaemonTest"] isDirectory:YES];
+    NSError *error = nil;
+    if ([fileManager fileExistsAtPath:tempDir.path])
+        [fileManager removeItemAtURL:tempDir error:&error];
+    EXPECT_NULL(error);
+
+    auto plist = testDaemonPList(tempDir);
+#if HAVE(OS_LAUNCHD_JOB)
+    auto launchDJob = adoptNS([[OSLaunchdJob alloc] initWithPlist:plist.get()]);
+    [launchDJob submit:&error];
+#else
+    NSURL *plistLocation = [tempDir URLByAppendingPathComponent:@"DaemonInfo.plist"];
+    BOOL success = [fileManager createDirectoryAtURL:tempDir withIntermediateDirectories:YES attributes:nil error:&error];
+    EXPECT_TRUE(success);
+    EXPECT_NULL(error);
+    success = [plist writeToURL:plistLocation error:&error];
+    EXPECT_TRUE(success);
+    system([NSString stringWithFormat:@"launchctl load %@", plistLocation.path].UTF8String);
+#endif
+    EXPECT_NULL(error);
+
+    auto dataStoreConfiguration = adoptNS([_WKWebsiteDataStoreConfiguration new]);
+    dataStoreConfiguration.get().pcmMachServiceName = @"org.webkit.pcmtestdaemon.service";
+    auto viewConfiguration = adoptNS([WKWebViewConfiguration new]);
+    viewConfiguration.get().websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]).get();
+    runBasicEventAttributionTest(viewConfiguration.get(), [](WKWebView *webView, const HTTPServer& server) {
+        [webView _addEventAttributionWithSourceID:42 destinationURL:exampleURL() sourceDescription:@"test source description" purchaser:@"test purchaser" reportEndpoint:server.request().URL optionalNonce:nil];
+    });
+
+    system("killall TestPCMDaemon -9");
+#if HAVE(OS_LAUNCHD_JOB)
+    // LaunchOnlyOnce takes care of cleanup with launchd.
+#else
+    system([NSString stringWithFormat:@"launchctl unload %@", plistLocation.path].UTF8String);
+#endif
+
+    EXPECT_TRUE([fileManager fileExistsAtPath:tempDir.path]);
+    [fileManager removeItemAtURL:tempDir error:&error];
+    EXPECT_NULL(error);
+}
+
+#endif // PLATFORM(MAC)
+
 #if HAVE(UI_EVENT_ATTRIBUTION)
 
 TEST(EventAttribution, BasicWithIOSSPI)

Added: trunk/Tools/TestWebKitAPI/cocoa/TestPCMDaemonMain.c (0 => 282368)


--- trunk/Tools/TestWebKitAPI/cocoa/TestPCMDaemonMain.c	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestPCMDaemonMain.c	2021-09-13 22:36:21 UTC (rev 282368)
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 <os/availability.h>
+#include <WebKit/WKMain.h>
+
+int main(int argc, const char** argv)
+{
+    WKPCMDaemonMain(argc, argv);
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to