Changeset 283559 in webkit


Ignore:
Timestamp:
Oct 5, 2021, 11:12:24 AM (4 years ago)
Author:
achristensen@apple.com
Message:

Implement missing functions in PrivateClickMeasurementDaemonClient
https://bugs.webkit.org/show_bug.cgi?id=231060

Reviewed by Chris Dumez.

Source/WebKit:

Enable debug mode in the daemon if any connected clients have debug mode enabled.
Broadcast debug messages to all clients, which will then broadcast them to all web processes.
Add an API test that turns it on then off and checks that the debug messages make it all the way to the inspector.

  • NetworkProcess/NetworkSession.cpp:

(WebKit::managerOrProxy):
(WebKit::NetworkSession::setPrivateClickMeasurementDebugMode):

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp:

(WebKit::PCM::ConnectionToMachService::ConnectionToMachService):
(WebKit::PCM::ConnectionToMachService::send const):
(WebKit::PCM::ConnectionToMachService::sendWithReply const):
(WebKit::PCM::Connection::Connection): Deleted.
(WebKit::PCM::Connection::send const): Deleted.
(WebKit::PCM::Connection::sendWithReply const): Deleted.

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h:

(WebKit::PCM::Connection::Connection):
(WebKit::PCM::Connection::get const):

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDaemonClient.cpp:

(WebKit::PCM::DaemonClient::broadcastConsoleMessage):
(WebKit::PCM::DaemonClient::debugModeEnabled const):

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp:

(WebKit::PrivateClickMeasurementManager::setDebugModeIsEnabled):

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:
  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp:

(WebKit::PCM::messageTypeSendsReply):
(WebKit::PCM::handlePCMMessageSetDebugModeIsEnabled):
(WebKit::PCM::decodeMessageAndSendToManager):

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h:
  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp:

(WebKit::PCM::ManagerProxy::ManagerProxy):
(WebKit::PCM::ManagerProxy::setDebugModeIsEnabled):

  • NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h:
  • NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementConnectionCocoa.mm: Renamed from Source/WebKit/NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm.

(WebKit::PCM::ConnectionToMachService::initializeConnectionIfNeeded const):
(WebKit::PCM::ConnectionToMachService::sendDebugModeIsEnabledMessageIfNecessary const):
(WebKit::PCM::ConnectionToMachService::checkForDebugMessageBroadcast const):
(WebKit::PCM::Connection::send const):
(WebKit::PCM::Connection::sendWithReply const):
(WebKit::PCM::ConnectionToMachService::send const):
(WebKit::PCM::ConnectionToMachService::sendWithReply const):

  • Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonConnectionSet.h: Copied from Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h.
  • Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonConnectionSet.mm: Added.

(WebKit::PCM::DaemonConnectionSet::singleton):
(WebKit::PCM::DaemonConnectionSet::add):
(WebKit::PCM::DaemonConnectionSet::remove):
(WebKit::PCM::DaemonConnectionSet::setConnectedNetworkProcessHasDebugModeEnabled):
(WebKit::PCM::DaemonConnectionSet::debugModeEnabled const):
(WebKit::PCM::DaemonConnectionSet::broadcastConsoleMessage):

  • Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm:

(WebKit::connectionEventHandler):
(WebKit::startListeningForMachServiceConnections):
(WebKit::peers): Deleted.

  • SourcesCocoa.txt:
  • UIProcess/API/Cocoa/WKWebsiteDataStore.mm:

(-[WKWebsiteDataStore _setPrivateClickMeasurementDebugModeEnabledForTesting:]):

  • UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
  • WebKit.xcodeproj/project.pbxproj:

Tools:

  • TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm:

(TestWebKitAPI::testDaemonPList):
(TestWebKitAPI::cleanUpDaemon):
(TestWebKitAPI::TEST):

  • TestWebKitAPI/cocoa/TestUIDelegate.h:
  • TestWebKitAPI/cocoa/TestUIDelegate.mm:

(-[TestUIDelegate _webView:didAttachLocalInspector:]):
(-[TestUIDelegate waitForInspectorToShow]):
(-[WKWebView _test_waitForInspectorToShow]):

Location:
trunk
Files:
3 added
1 deleted
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r283522 r283559  
     12021-10-05  Alex Christensen  <achristensen@webkit.org>
     2
     3        Implement missing functions in PrivateClickMeasurementDaemonClient
     4        https://bugs.webkit.org/show_bug.cgi?id=231060
     5
     6        Reviewed by Chris Dumez.
     7
     8        Enable debug mode in the daemon if any connected clients have debug mode enabled.
     9        Broadcast debug messages to all clients, which will then broadcast them to all web processes.
     10        Add an API test that turns it on then off and checks that the debug messages make it all the way to the inspector.
     11
     12        * NetworkProcess/NetworkSession.cpp:
     13        (WebKit::managerOrProxy):
     14        (WebKit::NetworkSession::setPrivateClickMeasurementDebugMode):
     15        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp:
     16        (WebKit::PCM::ConnectionToMachService::ConnectionToMachService):
     17        (WebKit::PCM::ConnectionToMachService::send const):
     18        (WebKit::PCM::ConnectionToMachService::sendWithReply const):
     19        (WebKit::PCM::Connection::Connection): Deleted.
     20        (WebKit::PCM::Connection::send const): Deleted.
     21        (WebKit::PCM::Connection::sendWithReply const): Deleted.
     22        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h:
     23        (WebKit::PCM::Connection::Connection):
     24        (WebKit::PCM::Connection::get const):
     25        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDaemonClient.cpp:
     26        (WebKit::PCM::DaemonClient::broadcastConsoleMessage):
     27        (WebKit::PCM::DaemonClient::debugModeEnabled const):
     28        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp:
     29        (WebKit::PrivateClickMeasurementManager::setDebugModeIsEnabled):
     30        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:
     31        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp:
     32        (WebKit::PCM::messageTypeSendsReply):
     33        (WebKit::PCM::handlePCMMessageSetDebugModeIsEnabled):
     34        (WebKit::PCM::decodeMessageAndSendToManager):
     35        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h:
     36        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp:
     37        (WebKit::PCM::ManagerProxy::ManagerProxy):
     38        (WebKit::PCM::ManagerProxy::setDebugModeIsEnabled):
     39        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h:
     40        * NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementConnectionCocoa.mm: Renamed from Source/WebKit/NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm.
     41        (WebKit::PCM::ConnectionToMachService::initializeConnectionIfNeeded const):
     42        (WebKit::PCM::ConnectionToMachService::sendDebugModeIsEnabledMessageIfNecessary const):
     43        (WebKit::PCM::ConnectionToMachService::checkForDebugMessageBroadcast const):
     44        (WebKit::PCM::Connection::send const):
     45        (WebKit::PCM::Connection::sendWithReply const):
     46        (WebKit::PCM::ConnectionToMachService::send const):
     47        (WebKit::PCM::ConnectionToMachService::sendWithReply const):
     48        * Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonConnectionSet.h: Copied from Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h.
     49        * Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonConnectionSet.mm: Added.
     50        (WebKit::PCM::DaemonConnectionSet::singleton):
     51        (WebKit::PCM::DaemonConnectionSet::add):
     52        (WebKit::PCM::DaemonConnectionSet::remove):
     53        (WebKit::PCM::DaemonConnectionSet::setConnectedNetworkProcessHasDebugModeEnabled):
     54        (WebKit::PCM::DaemonConnectionSet::debugModeEnabled const):
     55        (WebKit::PCM::DaemonConnectionSet::broadcastConsoleMessage):
     56        * Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm:
     57        (WebKit::connectionEventHandler):
     58        (WebKit::startListeningForMachServiceConnections):
     59        (WebKit::peers): Deleted.
     60        * SourcesCocoa.txt:
     61        * UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
     62        (-[WKWebsiteDataStore _setPrivateClickMeasurementDebugModeEnabledForTesting:]):
     63        * UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
     64        * WebKit.xcodeproj/project.pbxproj:
     65
    1662021-10-04  Chris Dumez  <cdumez@apple.com>
    267
  • trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp

    r283504 r283559  
    9999{
    100100    if (!parameters.pcmMachServiceName.isEmpty())
    101         return makeUniqueRef<PCM::ManagerProxy>(parameters.pcmMachServiceName);
     101        return makeUniqueRef<PCM::ManagerProxy>(parameters.pcmMachServiceName, networkSession);
    102102    return makeUniqueRef<PrivateClickMeasurementManager>(makeUniqueRef<PCM::ClientImpl>(networkSession, networkProcess), pcmStoreDirectory(networkSession, parameters.resourceLoadStatisticsParameters.directory, parameters.resourceLoadStatisticsParameters.privateClickMeasurementStorageDirectory));
    103103}
     
    428428
    429429    m_privateClickMeasurementDebugModeEnabled = enabled;
    430 
    431     auto message = enabled ? "[Private Click Measurement] Turned Debug Mode on."_s : "[Private Click Measurement] Turned Debug Mode off."_s;
    432     m_networkProcess->broadcastConsoleMessage(sessionID(), MessageSource::PrivateClickMeasurement, MessageLevel::Info, message);
     430    m_privateClickMeasurement->setDebugModeIsEnabled(enabled);
    433431}
    434432
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.cpp

    r282368 r283559  
    3333namespace PCM {
    3434
     35ConnectionToMachService::ConnectionToMachService(CString&& machServiceName, NetworkSession& networkSession)
     36    : m_machServiceName(WTFMove(machServiceName))
     37    , m_networkSession(makeWeakPtr(networkSession)) { }
     38
    3539#if !PLATFORM(COCOA)
    3640
    37 Connection::Connection(CString&&)
     41void ConnectionToMachService::send(MessageType, EncodedMessage&&) const
    3842{
    3943    notImplemented();
    4044}
    4145
    42 void Connection::send(MessageType, EncodedMessage&&) const
    43 {
    44     notImplemented();
    45 }
    46 
    47 void Connection::sendWithReply(MessageType, EncodedMessage&&, CompletionHandler<void(EncodedMessage&&)>&& completionHandler) const
     46void ConnectionToMachService::sendWithReply(MessageType, EncodedMessage&&, CompletionHandler<void(EncodedMessage&&)>&& completionHandler) const
    4847{
    4948    notImplemented();
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementConnection.h

    r282430 r283559  
    2929#include <wtf/Vector.h>
    3030#include <wtf/WeakPtr.h>
     31#include <wtf/text/CString.h>
    3132
    3233#if PLATFORM(COCOA)
    33 #include <wtf/OSObjectPtr.h>
     34#include <wtf/RetainPtr.h>
    3435#include <wtf/spi/darwin/XPCSPI.h>
    3536#endif
    3637
    3738namespace WebKit {
     39
     40class NetworkSession;
    3841
    3942namespace PCM {
     
    4447class Connection : public CanMakeWeakPtr<Connection> {
    4548public:
    46     explicit Connection(CString&& machServiceName);
     49    Connection() = default;
     50#if PLATFORM(COCOA)
     51    explicit Connection(RetainPtr<xpc_connection_t>&& connection)
     52        : m_connection(WTFMove(connection)) { }
     53    xpc_connection_t get() const { return m_connection.get(); }
     54    void send(xpc_object_t) const;
     55    void sendWithReply(xpc_object_t, CompletionHandler<void(xpc_object_t)>&&) const;
     56protected:
     57    mutable RetainPtr<xpc_connection_t> m_connection;
     58#endif
     59};
     60
     61class ConnectionToMachService : public Connection {
     62public:
     63    ConnectionToMachService(CString&& machServiceName, NetworkSession&);
    4764
    4865    void send(MessageType, EncodedMessage&&) const;
     
    5067
    5168private:
     69    void initializeConnectionIfNeeded() const;
    5270#if PLATFORM(COCOA)
    53     void initializeConnectionIfNeeded() const;
     71    void checkForDebugMessageBroadcast(xpc_object_t) const;
     72#endif
     73    void sendDebugModeIsEnabledMessageIfNecessary() const;
    5474
    5575    const CString m_machServiceName;
    56     mutable OSObjectPtr<xpc_connection_t> m_connection;
    57 #endif
     76    WeakPtr<NetworkSession> m_networkSession;
    5877};
    5978
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDaemonClient.cpp

    r282230 r283559  
    2727#include "PrivateClickMeasurementDaemonClient.h"
    2828
    29 #include <WebCore/NotImplemented.h>
     29#if PLATFORM(COCOA)
     30#include "PCMDaemonConnectionSet.h"
     31#endif
    3032
    3133namespace WebKit {
     
    3335namespace PCM {
    3436
    35 void DaemonClient::broadcastConsoleMessage(JSC::MessageLevel, const String&)
     37void DaemonClient::broadcastConsoleMessage(JSC::MessageLevel level, const String& message)
    3638{
    37     notImplemented();
     39#if PLATFORM(COCOA)
     40    DaemonConnectionSet::singleton().broadcastConsoleMessage(level, message);
     41#else
     42    UNUSED_PARAM(level);
     43    UNUSED_PARAM(message);
     44#endif
    3845}
    3946
     
    4552bool DaemonClient::debugModeEnabled() const
    4653{
    47     notImplemented();
     54#if PLATFORM(COCOA)
     55    return DaemonConnectionSet::singleton().debugModeEnabled();
     56#else
    4857    return false;
     58#endif
    4959}
    5060
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp

    r283383 r283559  
    228228}
    229229
     230void PrivateClickMeasurementManager::setDebugModeIsEnabled(bool enabled)
     231{
     232    // This doesn't maintain global state, it just broadcasts a message when debug mode enabled changes.
     233    // The state is either stored in NetworkSession when not using the daemon
     234    // or in DaemonConnectionSet per-connection when using the daemon.
     235
     236    auto message = enabled ? "[Private Click Measurement] Turned Debug Mode on."_s : "[Private Click Measurement] Turned Debug Mode off."_s;
     237    m_client->broadcastConsoleMessage(MessageLevel::Info, message);
     238}
     239
    230240void PrivateClickMeasurementManager::handleAttribution(AttributionTriggerData&& attributionTriggerData, const URL& requestURL, WebCore::RegistrableDomain&& redirectDomain, const URL& firstPartyURL, const ApplicationBundleIdentifier& applicationBundleIdentifier)
    231241{
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h

    r283383 r283559  
    5353    void clearForRegistrableDomain(const RegistrableDomain&, CompletionHandler<void()>&&) final;
    5454    void migratePrivateClickMeasurementFromLegacyStorage(PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType) final;
     55    void setDebugModeIsEnabled(bool) final;
    5556
    5657    void toStringForTesting(CompletionHandler<void(String)>&&) const final;
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.cpp

    r283383 r283559  
    3535#include "WebCoreArgumentCoders.h"
    3636
     37#if PLATFORM(COCOA)
     38#include "PCMDaemonConnectionSet.h"
     39#endif
     40
    3741namespace WebKit {
    3842
     
    6872FUNCTION(migratePrivateClickMeasurementFromLegacyStorage)
    6973ARGUMENTS(WebCore::PrivateClickMeasurement, PrivateClickMeasurementAttributionType)
     74END
     75
     76FUNCTION(setDebugModeIsEnabled)
     77ARGUMENTS(bool)
    7078END
    7179
     
    148156    case MessageType::HandleAttribution:
    149157    case MessageType::MigratePrivateClickMeasurementFromLegacyStorage:
     158    case MessageType::SetDebugModeIsEnabled:
    150159    case MessageType::SetOverrideTimerForTesting:
    151160    case MessageType::SetTokenPublicKeyURLForTesting:
     
    201210}
    202211
     212static void handlePCMMessageSetDebugModeIsEnabled(const Connection& connection, PCM::EncodedMessage&& encodedMessage)
     213{
     214#if PLATFORM(COCOA)
     215    PCM::Decoder decoder(WTFMove(encodedMessage));
     216    std::optional<bool> enabled;
     217    decoder >> enabled;
     218    if (UNLIKELY(!enabled))
     219        return;
     220
     221    auto& connectionSet = DaemonConnectionSet::singleton();
     222    bool debugModeWasEnabled = connectionSet.debugModeEnabled();
     223    connectionSet.setConnectedNetworkProcessHasDebugModeEnabled(connection, *enabled);
     224    if (debugModeWasEnabled != connectionSet.debugModeEnabled())
     225        daemonManager().setDebugModeIsEnabled(*enabled);
     226#else
     227    UNUSED_PARAM(connection);
     228    UNUSED_PARAM(encodedMessage);
     229#endif
     230}
     231
    203232template<typename Info>
    204233void handlePCMMessageWithReply(PCM::EncodedMessage&& encodedMessage, CompletionHandler<void(PCM::EncodedMessage&&)>&& replySender)
     
    218247}
    219248
    220 void decodeMessageAndSendToManager(MessageType messageType, Vector<uint8_t>&& encodedMessage, CompletionHandler<void(Vector<uint8_t>&&)>&& replySender)
     249void decodeMessageAndSendToManager(const Connection& connection, MessageType messageType, Vector<uint8_t>&& encodedMessage, CompletionHandler<void(Vector<uint8_t>&&)>&& replySender)
    221250{
    222251    ASSERT(messageTypeSendsReply(messageType) == !!replySender);
     
    234263        handlePCMMessageWithReply<MessageInfo::clearForRegistrableDomain>(WTFMove(encodedMessage), WTFMove(replySender));
    235264        break;
     265    case PCM::MessageType::SetDebugModeIsEnabled:
     266        handlePCMMessageSetDebugModeIsEnabled(connection, WTFMove(encodedMessage));
     267        break;
    236268    case PCM::MessageType::MigratePrivateClickMeasurementFromLegacyStorage:
    237269        handlePCMMessage<MessageInfo::migratePrivateClickMeasurementFromLegacyStorage>(WTFMove(encodedMessage));
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerInterface.h

    r283383 r283559  
    4242namespace PCM {
    4343
     44class Connection;
     45
    4446class ManagerInterface {
    4547public:
     
    5860    virtual void clearForRegistrableDomain(const RegistrableDomain&, CompletionHandler<void()>&&) = 0;
    5961    virtual void migratePrivateClickMeasurementFromLegacyStorage(PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType) = 0;
     62    virtual void setDebugModeIsEnabled(bool) = 0;
    6063
    6164    virtual void toStringForTesting(CompletionHandler<void(String)>&&) const = 0;
     
    7679constexpr uint64_t protocolVersionValue { 1 };
    7780
     81constexpr const char* protocolDebugMessageLevelKey { "debug message level" };
     82constexpr const char* protocolDebugMessageKey { "debug message" };
     83
    7884constexpr const char* protocolMessageTypeKey { "message type" };
    7985enum class MessageType : uint8_t {
     
    8389    ClearForRegistrableDomain,
    8490    MigratePrivateClickMeasurementFromLegacyStorage,
     91    SetDebugModeIsEnabled,
    8592    ToStringForTesting,
    8693    SetOverrideTimerForTesting,
     
    100107using EncodedMessage = Vector<uint8_t>;
    101108
    102 void decodeMessageAndSendToManager(MessageType, Vector<uint8_t>&& message, CompletionHandler<void(Vector<uint8_t>&&)>&&);
     109void decodeMessageAndSendToManager(const Connection&, MessageType, Vector<uint8_t>&& message, CompletionHandler<void(Vector<uint8_t>&&)>&&);
    103110bool messageTypeSendsReply(MessageType);
    104111
     
    119126        WebKit::PCM::MessageType::ClearForRegistrableDomain,
    120127        WebKit::PCM::MessageType::MigratePrivateClickMeasurementFromLegacyStorage,
     128        WebKit::PCM::MessageType::SetDebugModeIsEnabled,
    121129        WebKit::PCM::MessageType::ToStringForTesting,
    122130        WebKit::PCM::MessageType::SetOverrideTimerForTesting,
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.cpp

    r283383 r283559  
    7373}
    7474
    75 ManagerProxy::ManagerProxy(const String& machServiceName)
    76     : m_connection(machServiceName.utf8()) { }
     75ManagerProxy::ManagerProxy(const String& machServiceName, NetworkSession& networkSession)
     76    : m_connection(machServiceName.utf8(), networkSession) { }
    7777
    7878void ManagerProxy::storeUnattributed(WebCore::PrivateClickMeasurement&& pcm, CompletionHandler<void()>&& completionHandler)
     
    9494{
    9595    sendMessageWithReply<MessageType::ClearForRegistrableDomain>(WTFMove(completionHandler), domain);
     96}
     97
     98void ManagerProxy::setDebugModeIsEnabled(bool enabled)
     99{
     100    sendMessage<MessageType::SetDebugModeIsEnabled>(enabled);
    96101}
    97102
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManagerProxy.h

    r283383 r283559  
    3333namespace WebKit {
    3434
     35class NetworkSession;
     36
    3537namespace PCM {
    3638
     
    3840    WTF_MAKE_FAST_ALLOCATED;
    3941public:
    40     ManagerProxy(const String& machServiceName);
     42    ManagerProxy(const String& machServiceName, NetworkSession&);
    4143
    4244    using ApplicationBundleIdentifier = String;
     
    4749    void clearForRegistrableDomain(const WebCore::RegistrableDomain&, CompletionHandler<void()>&&) final;
    4850    void migratePrivateClickMeasurementFromLegacyStorage(WebCore::PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType) final;
     51    void setDebugModeIsEnabled(bool) final;
    4952
    5053    void toStringForTesting(CompletionHandler<void(String)>&&) const final;
     
    6770    void sendMessageWithReply(CompletionHandler<void(ReplyArgs...)>&&, Args&&...) const;
    6871
    69     Connection m_connection;
     72    ConnectionToMachService m_connection;
    7073};
    7174
  • trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm

    r282368 r283559  
    2727#import "PCMDaemonEntryPoint.h"
    2828
     29#import "PCMDaemonConnectionSet.h"
    2930#import "PrivateClickMeasurementConnection.h"
     31#import "PrivateClickMeasurementDecoder.h"
    3032#import "PrivateClickMeasurementManagerInterface.h"
    3133#import "PrivateClickMeasurementXPCUtilities.h"
     
    4345
    4446namespace WebKit {
    45 
    46 static HashSet<RetainPtr<xpc_connection_t>>& peers()
    47 {
    48     ASSERT(RunLoop::isMain());
    49     static NeverDestroyed<HashSet<RetainPtr<xpc_connection_t>>> set;
    50     return set.get();
    51 }
    5247
    5348static CompletionHandler<void(PCM::EncodedMessage&&)> replySender(PCM::MessageType messageType, OSObjectPtr<xpc_object_t>&& request)
     
    7570    const void* data = xpc_dictionary_get_data(request, PCM::protocolEncodedMessageKey, &dataSize);
    7671    PCM::EncodedMessage encodedMessage { static_cast<const uint8_t*>(data), dataSize };
    77     decodeMessageAndSendToManager(messageType, WTFMove(encodedMessage), replySender(messageType, request));
     72    decodeMessageAndSendToManager(PCM::Connection(xpc_dictionary_get_remote_connection(request)), messageType, WTFMove(encodedMessage), replySender(messageType, request));
    7873}
    7974
     
    9287            if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
    9388                NSLog(@"removing peer connection %p", peer);
    94                 peers().remove(peer);
     89                PCM::DaemonConnectionSet::singleton().remove(peer);
    9590                return;
    9691            }
     
    10196
    10297        NSLog(@"adding peer connection %p", peer);
    103         peers().add(peer);
     98        PCM::DaemonConnectionSet::singleton().add(peer);
    10499    });
    105100    xpc_connection_activate(listener.get().get());
  • trunk/Source/WebKit/SourcesCocoa.txt

    r283276 r283559  
    4242NetworkProcess/EntryPoint/Cocoa/XPCService/NetworkServiceEntryPoint.mm
    4343
    44 NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementDaemonConnectionCocoa.mm
     44NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementConnectionCocoa.mm
    4545NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementNetworkLoaderCocoa.mm
    4646NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementXPCUtilities.mm
     
    194194Shared/Cocoa/WebPreferencesDefaultValuesCocoa.mm
    195195
     196Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonConnectionSet.mm
    196197Shared/EntryPointUtilities/Cocoa/Daemon/PCMDaemonEntryPoint.mm
    197198
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm

    r282429 r283559  
    659659}
    660660
     661- (void)_setPrivateClickMeasurementDebugModeEnabledForTesting:(BOOL)enabled
     662{
     663    _websiteDataStore->setPrivateClickMeasurementDebugMode(enabled);
     664}
     665
    661666- (void)_appBoundDomains:(void (^)(NSArray<NSString *> *))completionHandler
    662667{
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h

    r282269 r283559  
    8686- (void)_allowTLSCertificateChain:(NSArray *)certificateChain forHost:(NSString *)host WK_API_AVAILABLE(macos(12.0), ios(15.0));
    8787- (void)_trustServerForLocalPCMTesting:(SecTrustRef)serverTrust WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
     88- (void)_setPrivateClickMeasurementDebugModeEnabledForTesting:(BOOL)enabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
    8889
    8990- (void)_renameOrigin:(NSURL *)oldName to:(NSURL *)newName forDataOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(void))completionHandler;
  • trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj

    r283276 r283559  
    45384538                5C5D2389227A1892000B9BDA /* _WKCustomHeaderFields.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKCustomHeaderFields.h; sourceTree = "<group>"; };
    45394539                5C5D238A227A1D9B000B9BDA /* APICustomHeaderFields.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APICustomHeaderFields.h; sourceTree = "<group>"; };
     4540                5C6289A827068EC000CF5EC6 /* PCMDaemonConnectionSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PCMDaemonConnectionSet.h; sourceTree = "<group>"; };
     4541                5C6289A927068EC000CF5EC6 /* PCMDaemonConnectionSet.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMDaemonConnectionSet.mm; sourceTree = "<group>"; };
    45404542                5C62FDF81EFC263C00CE072E /* WKURLSchemeTaskPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKURLSchemeTaskPrivate.h; sourceTree = "<group>"; };
    45414543                5C66A4B32320961300EA4D44 /* WKHTTPCookieStoreRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKHTTPCookieStoreRef.cpp; sourceTree = "<group>"; };
     
    46014603                5CB930C126E054B80032B1C0 /* PrivateClickMeasurementClientImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivateClickMeasurementClientImpl.h; sourceTree = "<group>"; };
    46024604                5CB930F226E7EEE00032B1C0 /* PrivateClickMeasurementNetworkLoaderCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PrivateClickMeasurementNetworkLoaderCocoa.mm; sourceTree = "<group>"; };
    4603                 5CB930F426E801E80032B1C0 /* PrivateClickMeasurementDaemonConnectionCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PrivateClickMeasurementDaemonConnectionCocoa.mm; sourceTree = "<group>"; };
     4605                5CB930F426E801E80032B1C0 /* PrivateClickMeasurementConnectionCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PrivateClickMeasurementConnectionCocoa.mm; sourceTree = "<group>"; };
    46044606                5CB930F526E802150032B1C0 /* PrivateClickMeasurementManagerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivateClickMeasurementManagerProxy.h; sourceTree = "<group>"; };
    46054607                5CB930F626E802150032B1C0 /* PrivateClickMeasurementDaemonClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivateClickMeasurementDaemonClient.h; sourceTree = "<group>"; };
     
    94699471                        isa = PBXGroup;
    94709472                        children = (
    9471                                 5CB930F426E801E80032B1C0 /* PrivateClickMeasurementDaemonConnectionCocoa.mm */,
     9473                                5CB930F426E801E80032B1C0 /* PrivateClickMeasurementConnectionCocoa.mm */,
    94729474                                5CB930F226E7EEE00032B1C0 /* PrivateClickMeasurementNetworkLoaderCocoa.mm */,
    94739475                                5CB9310726E841CB0032B1C0 /* PrivateClickMeasurementXPCUtilities.h */,
     
    94819483                        children = (
    94829484                                5CAF7AA526F93A950003F19E /* AdAttributionDaemon.c */,
     9485                                5C6289A827068EC000CF5EC6 /* PCMDaemonConnectionSet.h */,
     9486                                5C6289A927068EC000CF5EC6 /* PCMDaemonConnectionSet.mm */,
    94839487                                5CB9310426E837FC0032B1C0 /* PCMDaemonEntryPoint.h */,
    94849488                                5CB9310526E837FD0032B1C0 /* PCMDaemonEntryPoint.mm */,
  • trunk/Tools/ChangeLog

    r283553 r283559  
     12021-10-05  Alex Christensen  <achristensen@webkit.org>
     2
     3        Implement missing functions in PrivateClickMeasurementDaemonClient
     4        https://bugs.webkit.org/show_bug.cgi?id=231060
     5
     6        Reviewed by Chris Dumez.
     7
     8        * TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm:
     9        (TestWebKitAPI::testDaemonPList):
     10        (TestWebKitAPI::cleanUpDaemon):
     11        (TestWebKitAPI::TEST):
     12        * TestWebKitAPI/cocoa/TestUIDelegate.h:
     13        * TestWebKitAPI/cocoa/TestUIDelegate.mm:
     14        (-[TestUIDelegate _webView:didAttachLocalInspector:]):
     15        (-[TestUIDelegate waitForInspectorToShow]):
     16        (-[WKWebView _test_waitForInspectorToShow]):
     17
    1182021-10-05  David Kilzer  <ddkilzer@apple.com>
    219
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EventAttribution.mm

    r283357 r283559  
    3030#import "Test.h"
    3131#import "TestNavigationDelegate.h"
     32#import "TestUIDelegate.h"
    3233#import "TestWKWebView.h"
    3334#import "Utilities.h"
     35#import "WKWebViewConfigurationExtras.h"
    3436#import <WebKit/WKMain.h>
     37#import <WebKit/WKPage.h>
     38#import <WebKit/WKPageInjectedBundleClient.h>
     39#import <WebKit/WKPreferencesPrivate.h>
     40#import <WebKit/WKString.h>
    3541#import <WebKit/WKWebViewPrivate.h>
    3642#import <WebKit/WKWebViewPrivateForTesting.h>
    3743#import <WebKit/WKWebsiteDataStorePrivate.h>
     44#import <WebKit/_WKInspector.h>
    3845#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
    3946#import <mach-o/dyld.h>
     
    4956
    5057#endif // HAVE(RSA_BSSA)
     58
     59@interface WKWebView ()
     60- (WKPageRef)_pageForTesting;
     61@end
    5162
    5263@interface MockEventAttribution : NSObject
     
    111122}
    112123
    113 void runBasicEventAttributionTest(WKWebViewConfiguration *configuration, Function<void(WKWebView *, const HTTPServer&)>&& addAttributionToWebView)
     124void runBasicPCMTest(WKWebViewConfiguration *configuration, Function<void(WKWebView *, const HTTPServer&)>&& addAttributionToWebView)
    114125{
    115126    clearState();
     
    164175
    165176#if HAVE(RSA_BSSA)
    166 TEST(EventAttribution, FraudPrevention)
     177TEST(PrivateClickMeasurement, FraudPrevention)
    167178{
    168179    [WKWebsiteDataStore _setNetworkProcessSuspensionAllowedForTesting:NO];
     
    318329#endif
    319330
    320 TEST(EventAttribution, Basic)
    321 {
    322     runBasicEventAttributionTest(nil, [](WKWebView *webView, const HTTPServer& server) {
     331TEST(PrivateClickMeasurement, Basic)
     332{
     333    runBasicPCMTest(nil, [](WKWebView *webView, const HTTPServer& server) {
    323334        [webView _addEventAttributionWithSourceID:42 destinationURL:exampleURL() sourceDescription:@"test source description" purchaser:@"test purchaser" reportEndpoint:server.request().URL optionalNonce:nil applicationBundleID:@"test.bundle.id"];
    324335    });
    325336}
    326337
    327 TEST(EventAttribution, DatabaseLocation)
     338TEST(PrivateClickMeasurement, DatabaseLocation)
    328339{
    329340    NSFileManager *fileManager = [NSFileManager defaultManager];
     
    342353        auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);
    343354        viewConfiguration.get().websiteDataStore = dataStore.get();
    344         runBasicEventAttributionTest(viewConfiguration.get(), [](WKWebView *webView, const HTTPServer& server) {
     355        runBasicPCMTest(viewConfiguration.get(), [](WKWebView *webView, const HTTPServer& server) {
    345356            [webView _addEventAttributionWithSourceID:42 destinationURL:exampleURL() sourceDescription:@"test source description" purchaser:@"test purchaser" reportEndpoint:server.request().URL optionalNonce:nil applicationBundleID:@"test.bundle.id"];
    346357        });
     
    401412    xpc_dictionary_set_string(plist.get(), "Label", "org.webkit.pcmtestdaemon");
    402413    xpc_dictionary_set_bool(plist.get(), "LaunchOnlyOnce", true);
     414    xpc_dictionary_set_string(plist.get(), "StandardErrorPath", [storageLocation URLByAppendingPathComponent:@"daemon_stderr"].path.fileSystemRepresentation);
    403415
    404416    {
     
    432444        @"Label" : @"org.webkit.pcmtestdaemon",
    433445        @"LaunchOnlyOnce" : @YES,
     446        @"StandardErrorPath" : [storageLocation URLByAppendingPathComponent:@"daemon_stderr"].path,
    434447        @"EnvironmentVariables" : @{ @"DYLD_FRAMEWORK_PATH" : currentExecutableDirectory().get().path },
    435448        @"MachServices" : @{ @"org.webkit.pcmtestdaemon.service" : @YES },
     
    446459#endif
    447460
    448 TEST(EventAttribution, Daemon)
     461static std::pair<NSURL *, WKWebViewConfiguration *> setUpDaemon(WKWebViewConfiguration *viewConfiguration)
    449462{
    450463    NSFileManager *fileManager = [NSFileManager defaultManager];
     
    468481    success = [plist writeToURL:plistLocation error:&error];
    469482    EXPECT_TRUE(success);
    470     system([NSString stringWithFormat:@"launchctl load %@", plistLocation.path].UTF8String);
     483    system([NSString stringWithFormat:@"launchctl load %@", plistLocation.path].fileSystemRepresentation);
    471484#endif
    472485    EXPECT_NULL(error);
     
    474487    auto dataStoreConfiguration = adoptNS([_WKWebsiteDataStoreConfiguration new]);
    475488    dataStoreConfiguration.get().pcmMachServiceName = @"org.webkit.pcmtestdaemon.service";
    476     auto viewConfiguration = adoptNS([WKWebViewConfiguration new]);
    477     viewConfiguration.get().websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]).get();
    478     runBasicEventAttributionTest(viewConfiguration.get(), [](WKWebView *webView, const HTTPServer& server) {
     489    viewConfiguration.websiteDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]).get();
     490
     491    return std::make_pair(tempDir, viewConfiguration);
     492}
     493
     494static void cleanUpDaemon(NSURL *tempDir)
     495{
     496    system("killall AdAttributionDaemon -9");
     497
     498    EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:tempDir.path]);
     499    NSError *error = nil;
     500    [[NSFileManager defaultManager] removeItemAtURL:tempDir error:&error];
     501    EXPECT_NULL(error);
     502}
     503
     504TEST(PrivateClickMeasurement, DaemonBasicFunctionality)
     505{
     506    auto [tempDir, configuration] = setUpDaemon(adoptNS([WKWebViewConfiguration new]).autorelease());
     507    runBasicPCMTest(configuration, [](WKWebView *webView, const HTTPServer& server) {
    479508        [webView _addEventAttributionWithSourceID:42 destinationURL:exampleURL() sourceDescription:@"test source description" purchaser:@"test purchaser" reportEndpoint:server.request().URL optionalNonce:nil applicationBundleID:@"test.bundle.id"];
    480509    });
    481 
    482     system("killall AdAttributionDaemon -9");
    483 
    484     EXPECT_TRUE([fileManager fileExistsAtPath:tempDir.path]);
    485     [fileManager removeItemAtURL:tempDir error:&error];
    486     EXPECT_NULL(error);
     510    cleanUpDaemon(tempDir);
     511}
     512
     513static void setInjectedBundleClient(WKWebView *webView, Vector<String>& consoleMessages)
     514{
     515    WKPageInjectedBundleClientV0 injectedBundleClient = {
     516        { 0, &consoleMessages },
     517        [] (WKPageRef, WKStringRef messageName, WKTypeRef message, const void* clientInfo) {
     518            auto& consoleMessages = *reinterpret_cast<Vector<String>*>(const_cast<void*>(clientInfo));
     519            if (WKStringIsEqualToUTF8CString(messageName, "ConsoleMessage"))
     520                consoleMessages.append(Util::toNS((WKStringRef)message));
     521        },
     522        nullptr,
     523    };
     524    WKPageSetPageInjectedBundleClient(webView._pageForTesting, &injectedBundleClient.base);
     525};
     526
     527static RetainPtr<TestWKWebView> webViewWithOpenInspector(WKWebViewConfiguration *configuration)
     528{
     529    configuration.preferences._developerExtrasEnabled = YES;
     530    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
     531    [webView synchronouslyLoadHTMLString:@"start processes"];
     532    [[webView _inspector] show];
     533    [webView _test_waitForInspectorToShow];
     534    return webView;
     535}
     536
     537TEST(PrivateClickMeasurement, DaemonDebugMode)
     538{
     539    auto [tempDir, configuration] = setUpDaemon([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundlePageConsoleMessage"]);
     540    Vector<String> consoleMessages;
     541    auto webView = webViewWithOpenInspector(configuration);
     542    setInjectedBundleClient(webView.get(), consoleMessages);
     543    [configuration.websiteDataStore _setPrivateClickMeasurementDebugModeEnabledForTesting:YES];
     544    while (consoleMessages.isEmpty())
     545        Util::spinRunLoop();
     546    EXPECT_WK_STREQ(consoleMessages[0], "[Private Click Measurement] Turned Debug Mode on.");
     547    [configuration.websiteDataStore _setPrivateClickMeasurementDebugModeEnabledForTesting:NO];
     548    while (consoleMessages.size() < 2)
     549        Util::spinRunLoop();
     550    EXPECT_WK_STREQ(consoleMessages[1], "[Private Click Measurement] Turned Debug Mode off.");
     551}
     552
     553TEST(PrivateClickMeasurement, NetworkProcessDebugMode)
     554{
     555    auto configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundlePageConsoleMessage"];
     556    Vector<String> consoleMessages;
     557    auto webView = webViewWithOpenInspector(configuration);
     558    setInjectedBundleClient(webView.get(), consoleMessages);
     559    [configuration.websiteDataStore _setPrivateClickMeasurementDebugModeEnabledForTesting:YES];
     560    while (consoleMessages.isEmpty())
     561        Util::spinRunLoop();
     562    EXPECT_WK_STREQ(consoleMessages[0], "[Private Click Measurement] Turned Debug Mode on.");
     563    [configuration.websiteDataStore _setPrivateClickMeasurementDebugModeEnabledForTesting:NO];
     564    while (consoleMessages.size() < 2)
     565        Util::spinRunLoop();
     566    EXPECT_WK_STREQ(consoleMessages[1], "[Private Click Measurement] Turned Debug Mode off.");
    487567}
    488568
     
    491571#if HAVE(UI_EVENT_ATTRIBUTION)
    492572
    493 TEST(EventAttribution, BasicWithIOSSPI)
    494 {
    495     runBasicEventAttributionTest(nil, [](WKWebView *webView, const HTTPServer& server) {
     573TEST(PrivateClickMeasurement, BasicWithIOSSPI)
     574{
     575    runBasicPCMTest(nil, [](WKWebView *webView, const HTTPServer& server) {
    496576        auto attribution = adoptNS([[MockEventAttribution alloc] initWithReportEndpoint:server.request().URL destinationURL:exampleURL()]);
    497577        webView._uiEventAttribution = (UIEventAttribution *)attribution.get();
     
    501581}
    502582
    503 TEST(EventAttribution, BasicWithEphemeralIOSSPI)
    504 {
    505     runBasicEventAttributionTest(nil, [](WKWebView *webView, const HTTPServer& server) {
     583TEST(PrivateClickMeasurement, BasicWithEphemeralIOSSPI)
     584{
     585    runBasicPCMTest(nil, [](WKWebView *webView, const HTTPServer& server) {
    506586        auto attribution = adoptNS([[MockEventAttribution alloc] initWithReportEndpoint:server.request().URL destinationURL:exampleURL()]);
    507587        webView._ephemeralUIEventAttribution = (UIEventAttribution *)attribution.get();
  • trunk/Tools/TestWebKitAPI/cocoa/TestUIDelegate.h

    r270273 r283559  
    4141@interface WKWebView (TestUIDelegateExtras)
    4242- (NSString *)_test_waitForAlert;
     43- (void)_test_waitForInspectorToShow;
    4344@end
  • trunk/Tools/TestWebKitAPI/cocoa/TestUIDelegate.mm

    r270273 r283559  
    3131#import <wtf/RetainPtr.h>
    3232
    33 @implementation TestUIDelegate
     33@implementation TestUIDelegate {
     34    BOOL _showedInspector;
     35}
    3436
    3537- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
     
    8284}
    8385
     86- (void)_webView:(WKWebView *)webView didAttachLocalInspector:(_WKInspector *)inspector
     87{
     88    _showedInspector = YES;
     89}
     90
     91- (void)waitForInspectorToShow
     92{
     93    while (!_showedInspector)
     94        TestWebKitAPI::Util::spinRunLoop();
     95}
     96
    8497@end
    8598
     
    96109}
    97110
     111- (void)_test_waitForInspectorToShow
     112{
     113    EXPECT_FALSE(self.UIDelegate);
     114    auto uiDelegate = adoptNS([TestUIDelegate new]);
     115    self.UIDelegate = uiDelegate.get();
     116    [uiDelegate waitForInspectorToShow];
     117    self.UIDelegate = nil;
     118}
     119
    98120@end
Note: See TracChangeset for help on using the changeset viewer.