Once we can send Mach messages again, make sure to send any pending outgoing messages
[WebKit-https.git] / Source / WebKit2 / Platform / IPC / mac / ConnectionMac.mm
index c0845ec..66bde17 100644 (file)
 
 #include "DataReference.h"
 #include "ImportanceAssertion.h"
+#include "MachMessage.h"
 #include "MachPort.h"
 #include "MachUtilities.h"
 #include <WebCore/AXObjectCache.h>
 #include <mach/mach_error.h>
 #include <mach/vm_map.h>
+#include <sys/mman.h>
 #include <wtf/RunLoop.h>
 #include <wtf/spi/darwin/XPCSPI.h>
 
 #if PLATFORM(IOS)
 #include "ProcessAssertion.h"
+#include <UIKit/UIAccessibility.h>
+
+#if USE(APPLE_INTERNAL_SDK)
+#include <AXRuntime/AXDefines.h>
+#include <AXRuntime/AXNotificationConstants.h>
+#else
+#define kAXPidStatusChangedNotification 0
 #endif
 
-#if __has_include(<HIServices/AccessibilityPriv.h>)
+#endif
+
+#if PLATFORM(MAC)
+
+#if USE(APPLE_INTERNAL_SDK)
 #include <HIServices/AccessibilityPriv.h>
 #else
 typedef enum {
@@ -49,9 +62,9 @@ typedef enum {
 } AXSuspendStatus;
 #endif
 
-#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
 extern "C" AXError _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatus);
-#endif
+
+#endif // PLATFORM(MAC)
 
 namespace IPC {
 
@@ -86,9 +99,7 @@ private:
     
     void watchdogTimerFired()
     {
-#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
         xpc_connection_kill(m_xpcConnection.get(), SIGKILL);
-#endif
         delete this;
     }
 
@@ -101,26 +112,38 @@ private:
     
 void Connection::platformInvalidate()
 {
-    if (!m_isConnected)
+    if (!m_isConnected) {
+        if (m_sendPort) {
+            mach_port_deallocate(mach_task_self(), m_sendPort);
+            m_sendPort = MACH_PORT_NULL;
+        }
+
+        if (m_receivePort) {
+            mach_port_mod_refs(mach_task_self(), m_receivePort, MACH_PORT_RIGHT_RECEIVE, -1);
+            m_receivePort = MACH_PORT_NULL;
+        }
+
         return;
+    }
 
+    m_pendingOutgoingMachMessage = nullptr;
     m_isConnected = false;
 
     ASSERT(m_sendPort);
     ASSERT(m_receivePort);
 
     // Unregister our ports.
-    dispatch_source_cancel(m_deadNameSource);
-    dispatch_release(m_deadNameSource);
-    m_deadNameSource = 0;
+    dispatch_source_cancel(m_sendSource);
+    dispatch_release(m_sendSource);
+    m_sendSource = nullptr;
     m_sendPort = MACH_PORT_NULL;
 
-    dispatch_source_cancel(m_receivePortDataAvailableSource);
-    dispatch_release(m_receivePortDataAvailableSource);
-    m_receivePortDataAvailableSource = 0;
+    dispatch_source_cancel(m_receiveSource);
+    dispatch_release(m_receiveSource);
+    m_receiveSource = nullptr;
     m_receivePort = MACH_PORT_NULL;
 
-#if !PLATFORM(IOS)
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
     if (m_exceptionPort) {
         dispatch_source_cancel(m_exceptionPortDataAvailableSource);
         dispatch_release(m_exceptionPortDataAvailableSource);
@@ -128,8 +151,6 @@ void Connection::platformInvalidate()
         m_exceptionPort = MACH_PORT_NULL;
     }
 #endif
-
-    m_xpcConnection = nullptr;
 }
     
 void Connection::terminateSoon(double intervalInSeconds)
@@ -140,7 +161,7 @@ void Connection::terminateSoon(double intervalInSeconds)
     
 void Connection::platformInitialize(Identifier identifier)
 {
-#if !PLATFORM(IOS)
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
     m_exceptionPort = MACH_PORT_NULL;
     m_exceptionPortDataAvailableSource = nullptr;
 #endif
@@ -153,14 +174,14 @@ void Connection::platformInitialize(Identifier identifier)
         m_sendPort = identifier.port;
     }
 
-    m_deadNameSource = nullptr;
-    m_receivePortDataAvailableSource = nullptr;
+    m_sendSource = nullptr;
+    m_receiveSource = nullptr;
 
     m_xpcConnection = identifier.xpcConnection;
 }
 
 template<typename Function>
-static dispatch_source_t createDataAvailableSource(mach_port_t receivePort, WorkQueue& workQueue, Function&& function)
+static dispatch_source_t createReceiveSource(mach_port_t receivePort, WorkQueue& workQueue, Function&& function)
 {
     dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, receivePort, 0, workQueue.dispatchQueue());
     dispatch_source_set_event_handler(source, function);
@@ -185,21 +206,19 @@ bool Connection::open()
         // Create the receive port.
         mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &m_receivePort);
 
-#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+#if PLATFORM(MAC)
         mach_port_set_attributes(mach_task_self(), m_receivePort, MACH_PORT_DENAP_RECEIVER, (mach_port_info_t)0, 0);
-#elif PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
-        mach_port_set_attributes(mach_task_self(), m_receivePort, MACH_PORT_IMPORTANCE_RECEIVER, (mach_port_info_t)0, 0);
 #endif
 
         m_isConnected = true;
         
         // Send the initialize message, which contains a send right for the server to use.
-        auto encoder = std::make_unique<MessageEncoder>("IPC", "InitializeConnection", 0);
+        auto encoder = std::make_unique<Encoder>("IPC", "InitializeConnection", 0);
         encoder->encode(MachPort(m_receivePort, MACH_MSG_TYPE_MAKE_SEND));
 
-        sendMessage(WTF::move(encoder));
+        initializeSendSource();
 
-        initializeDeadNameSource();
+        sendMessage(WTFMove(encoder), { });
     }
 
     // Change the message queue length for the receive port.
@@ -207,31 +226,30 @@ bool Connection::open()
 
     // Register the data available handler.
     RefPtr<Connection> connection(this);
-    m_receivePortDataAvailableSource = createDataAvailableSource(m_receivePort, *m_connectionQueue, [connection] {
+    m_receiveSource = createReceiveSource(m_receivePort, m_connectionQueue, [connection] {
         connection->receiveSourceEventHandler();
     });
 
-#if !PLATFORM(IOS)
-    // If we have an exception port, register the data available handler and send over the port to the other end.
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
     if (m_exceptionPort) {
-        m_exceptionPortDataAvailableSource = createDataAvailableSource(m_exceptionPort, *m_connectionQueue, [connection] {
+        m_exceptionPortDataAvailableSource = createReceiveSource(m_exceptionPort, m_connectionQueue, [connection] {
             connection->exceptionSourceEventHandler();
         });
 
-        auto encoder = std::make_unique<MessageEncoder>("IPC", "SetExceptionPort", 0);
+        auto encoder = std::make_unique<Encoder>("IPC", "SetExceptionPort", 0);
         encoder->encode(MachPort(m_exceptionPort, MACH_MSG_TYPE_MAKE_SEND));
 
-        sendMessage(WTF::move(encoder));
+        sendMessage(WTFMove(encoder), { });
     }
 #endif
 
     ref();
     dispatch_async(m_connectionQueue->dispatchQueue(), ^{
-        dispatch_resume(m_receivePortDataAvailableSource);
+        dispatch_resume(m_receiveSource);
 
-        if (m_deadNameSource)
-            dispatch_resume(m_deadNameSource);
-#if !PLATFORM(IOS)
+        if (m_sendSource)
+            dispatch_resume(m_sendSource);
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
         if (m_exceptionPortDataAvailableSource)
             dispatch_resume(m_exceptionPortDataAvailableSource);
 #endif
@@ -242,26 +260,41 @@ bool Connection::open()
     return true;
 }
 
-static inline size_t machMessageSize(size_t bodySize, size_t numberOfPortDescriptors = 0, size_t numberOfOOLMemoryDescriptors = 0)
+bool Connection::sendMessage(std::unique_ptr<MachMessage> message)
 {
-    size_t size = sizeof(mach_msg_header_t) + bodySize;
-    if (numberOfPortDescriptors || numberOfOOLMemoryDescriptors) {
-        size += sizeof(mach_msg_body_t);
-        if (numberOfPortDescriptors)
-            size += (numberOfPortDescriptors * sizeof(mach_msg_port_descriptor_t));
-        if (numberOfOOLMemoryDescriptors)
-            size += (numberOfOOLMemoryDescriptors * sizeof(mach_msg_ool_descriptor_t));
+    ASSERT(!m_pendingOutgoingMachMessage);
+
+    // Send the message.
+    kern_return_t kr = mach_msg(message->header(), MACH_SEND_MSG | MACH_SEND_TIMEOUT | MACH_SEND_NOTIFY, message->size(), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+    switch (kr) {
+    case MACH_MSG_SUCCESS:
+        // The kernel has already adopted the descriptors.
+        message->leakDescriptors();
+        return true;
+
+    case MACH_SEND_TIMED_OUT:
+        // We timed out, stash away the message for later.
+        m_pendingOutgoingMachMessage = WTFMove(message);
+        return false;
+
+    case MACH_SEND_INVALID_DEST:
+        // The other end has disappeared, we'll get a dead name notification which will cause us to be invalidated.
+        return false;
+
+    default:
+        CRASH();
     }
-    return round_msg(size);
 }
 
 bool Connection::platformCanSendOutgoingMessages() const
 {
-    return true;
+    return !m_pendingOutgoingMachMessage;
 }
 
-bool Connection::sendOutgoingMessage(std::unique_ptr<MessageEncoder> encoder)
+bool Connection::sendOutgoingMessage(std::unique_ptr<Encoder> encoder)
 {
+    ASSERT(!m_pendingOutgoingMachMessage);
+
     Vector<Attachment> attachments = encoder->releaseAttachments();
     
     size_t numberOfPortDescriptors = 0;
@@ -272,24 +305,21 @@ bool Connection::sendOutgoingMessage(std::unique_ptr<MessageEncoder> encoder)
             numberOfPortDescriptors++;
     }
     
-    size_t messageSize = machMessageSize(encoder->bufferSize(), numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
+    size_t messageSize = MachMessage::messageSize(encoder->bufferSize(), numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
 
     bool messageBodyIsOOL = false;
     if (messageSize > inlineMessageMaxSize) {
         messageBodyIsOOL = true;
 
         numberOfOOLMemoryDescriptors++;
-        messageSize = machMessageSize(0, numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
+        messageSize = MachMessage::messageSize(0, numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
     }
 
-    char stackBuffer[inlineMessageMaxSize];
-    char* buffer = &stackBuffer[0];
-    if (messageSize > inlineMessageMaxSize)
-        buffer = (char*)mmap(0, messageSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+    auto message = MachMessage::create(messageSize);
 
-    bool isComplex = (numberOfPortDescriptors + numberOfOOLMemoryDescriptors > 0);
+    bool isComplex = (numberOfPortDescriptors + numberOfOOLMemoryDescriptors) > 0;
 
-    mach_msg_header_t* header = reinterpret_cast<mach_msg_header_t*>(buffer);
+    mach_msg_header_t* header = message->header();
     header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
     header->msgh_size = messageSize;
     header->msgh_remote_port = m_sendPort;
@@ -346,38 +376,47 @@ bool Connection::sendOutgoingMessage(std::unique_ptr<MessageEncoder> encoder)
 
     ASSERT(m_sendPort);
 
-    // Send the message.
-    kern_return_t kr = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
-    if (kr != KERN_SUCCESS) {
-        // FIXME: What should we do here?
-    }
-
-    if (buffer != &stackBuffer[0])
-        munmap(buffer, messageSize);
-
-    return true;
+    return sendMessage(WTFMove(message));
 }
 
-void Connection::initializeDeadNameSource()
+void Connection::initializeSendSource()
 {
-    m_deadNameSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, m_sendPort, 0, m_connectionQueue->dispatchQueue());
-    dispatch_source_set_event_handler(m_deadNameSource, bind(&Connection::connectionDidClose, this));
+    m_sendSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, m_sendPort, DISPATCH_MACH_SEND_DEAD | DISPATCH_MACH_SEND_POSSIBLE, m_connectionQueue->dispatchQueue());
+
+    RefPtr<Connection> connection(this);
+    dispatch_source_set_event_handler(m_sendSource, [connection] {
+        if (!connection->m_sendSource)
+            return;
+
+        unsigned long data = dispatch_source_get_data(connection->m_sendSource);
+
+        if (data & DISPATCH_MACH_SEND_DEAD) {
+            connection->connectionDidClose();
+            return;
+        }
+
+        if (data & DISPATCH_MACH_SEND_POSSIBLE) {
+            connection->sendMessage(WTFMove(connection->m_pendingOutgoingMachMessage));
+            connection->sendOutgoingMessages();
+            return;
+        }
+    });
 
     mach_port_t sendPort = m_sendPort;
-    dispatch_source_set_cancel_handler(m_deadNameSource, ^{
+    dispatch_source_set_cancel_handler(m_sendSource, ^{
         // Release our send right.
         mach_port_deallocate(mach_task_self(), sendPort);
     });
 }
 
-static std::unique_ptr<MessageDecoder> createMessageDecoder(mach_msg_header_t* header)
+static std::unique_ptr<Decoder> createMessageDecoder(mach_msg_header_t* header)
 {
     if (!(header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
         // We have a simple message.
         uint8_t* body = reinterpret_cast<uint8_t*>(header + 1);
         size_t bodySize = header->msgh_size - sizeof(mach_msg_header_t);
 
-        return std::make_unique<MessageDecoder>(DataReference(body, bodySize), Vector<Attachment>());
+        return std::make_unique<Decoder>(body, bodySize, nullptr, Vector<Attachment> { });
     }
 
     bool messageBodyIsOOL = header->msgh_id & MessageBodyIsOutOfLine;
@@ -416,9 +455,9 @@ static std::unique_ptr<MessageDecoder> createMessageDecoder(mach_msg_header_t* h
         uint8_t* messageBody = static_cast<uint8_t*>(descriptor->out_of_line.address);
         size_t messageBodySize = descriptor->out_of_line.size;
 
-        auto decoder = std::make_unique<MessageDecoder>(DataReference(messageBody, messageBodySize), WTF::move(attachments));
-
-        vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(descriptor->out_of_line.address), descriptor->out_of_line.size);
+        auto decoder = std::make_unique<Decoder>(messageBody, messageBodySize, [](const uint8_t* buffer, size_t length) {
+            vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(buffer), length);
+        }, WTFMove(attachments));
 
         return decoder;
     }
@@ -426,7 +465,7 @@ static std::unique_ptr<MessageDecoder> createMessageDecoder(mach_msg_header_t* h
     uint8_t* messageBody = descriptorData;
     size_t messageBodySize = header->msgh_size - (descriptorData - reinterpret_cast<uint8_t*>(header));
 
-    return std::make_unique<MessageDecoder>(DataReference(messageBody, messageBodySize), attachments);
+    return std::make_unique<Decoder>(messageBody, messageBodySize, nullptr, attachments);
 }
 
 // The receive buffer size should always include the maximum trailer size.
@@ -467,10 +506,24 @@ void Connection::receiveSourceEventHandler()
     if (!header)
         return;
 
-    std::unique_ptr<MessageDecoder> decoder = createMessageDecoder(header);
+    switch (header->msgh_id) {
+    case MACH_NOTIFY_NO_SENDERS:
+        ASSERT(m_isServer);
+        if (!m_sendPort)
+            connectionDidClose();
+        return;
+
+    case MACH_NOTIFY_SEND_ONCE:
+        return;
+
+    default:
+        break;
+    }
+
+    std::unique_ptr<Decoder> decoder = createMessageDecoder(header);
     ASSERT(decoder);
 
-#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
+#if PLATFORM(MAC)
     decoder->setImportanceAssertion(std::make_unique<ImportanceAssertion>(header));
 #endif
 
@@ -488,8 +541,14 @@ void Connection::receiveSourceEventHandler()
         m_sendPort = port.port();
         
         if (m_sendPort) {
-            initializeDeadNameSource();
-            dispatch_resume(m_deadNameSource);
+            mach_port_t previousNotificationPort;
+            mach_port_request_notification(mach_task_self(), m_receivePort, MACH_NOTIFY_NO_SENDERS, 0, MACH_PORT_NULL, MACH_MSG_TYPE_MOVE_SEND_ONCE, &previousNotificationPort);
+
+            if (previousNotificationPort != MACH_PORT_NULL)
+                mach_port_deallocate(mach_task_self(), previousNotificationPort);
+
+            initializeSendSource();
+            dispatch_resume(m_sendSource);
         }
 
         m_isConnected = true;
@@ -504,7 +563,14 @@ void Connection::receiveSourceEventHandler()
     if (decoder->messageReceiverName() == "IPC" && decoder->messageName() == "SetExceptionPort") {
         if (m_isServer) {
             // Server connections aren't supposed to have their exception ports overriden. Treat this as an invalid message.
-            m_clientRunLoop.dispatch(bind(&Connection::dispatchDidReceiveInvalidMessage, this, decoder->messageReceiverName().toString(), decoder->messageName().toString()));
+            StringReference messageReceiverNameReference = decoder->messageReceiverName();
+            String messageReceiverName(String(messageReceiverNameReference.data(), messageReceiverNameReference.size()));
+            StringReference messageNameReference = decoder->messageName();
+            String messageName(String(messageNameReference.data(), messageNameReference.size()));
+
+            RunLoop::main().dispatch([protectedThis = makeRef(*this), messageReceiverName = WTFMove(messageReceiverName), messageName = WTFMove(messageName)]() mutable {
+                protectedThis->dispatchDidReceiveInvalidMessage(messageReceiverName.utf8(), messageName.utf8());
+            });
             return;
         }
         MachPort exceptionPort;
@@ -516,10 +582,10 @@ void Connection::receiveSourceEventHandler()
     }
 #endif
 
-    processIncomingMessage(WTF::move(decoder));
+    processIncomingMessage(WTFMove(decoder));
 }    
 
-#if !PLATFORM(IOS)
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 101000
 void Connection::exceptionSourceEventHandler()
 {
     ReceiveBuffer buffer;
@@ -580,30 +646,43 @@ bool Connection::getAuditToken(audit_token_t& auditToken)
 
 bool Connection::kill()
 {
-#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
     if (m_xpcConnection) {
         xpc_connection_kill(m_xpcConnection.get(), SIGKILL);
         return true;
     }
-#endif
 
     return false;
 }
     
-void Connection::willSendSyncMessage(unsigned flags)
+static void AccessibilityProcessSuspendedNotification(bool suspended)
 {
-#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
-    if ((flags & InformPlatformProcessWillSuspend) && WebCore::AXObjectCache::accessibilityEnabled())
-        _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatusSuspended);
+#if PLATFORM(MAC)
+    _AXUIElementNotifyProcessSuspendStatus(suspended ? AXSuspendStatusSuspended : AXSuspendStatusRunning);
+#elif PLATFORM(IOS)
+    UIAccessibilityPostNotification(kAXPidStatusChangedNotification, @{ @"pid" : @(getpid()), @"suspended" : @(suspended) });
+#else
+    UNUSED_PARAM(suspended);
 #endif
 }
+    
+void Connection::willSendSyncMessage(OptionSet<SendSyncOption> sendSyncOptions)
+{
+    if (sendSyncOptions.contains(IPC::SendSyncOption::InformPlatformProcessWillSuspend) && WebCore::AXObjectCache::accessibilityEnabled())
+        AccessibilityProcessSuspendedNotification(true);
+}
 
-void Connection::didReceiveSyncReply(unsigned flags)
+void Connection::didReceiveSyncReply(OptionSet<SendSyncOption> sendSyncOptions)
 {
-#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
-    if ((flags & InformPlatformProcessWillSuspend) && WebCore::AXObjectCache::accessibilityEnabled())
-        _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatusRunning);
-#endif
-}    
+    if (sendSyncOptions.contains(IPC::SendSyncOption::InformPlatformProcessWillSuspend) && WebCore::AXObjectCache::accessibilityEnabled())
+        AccessibilityProcessSuspendedNotification(false);
+}
+
+pid_t Connection::remoteProcessID() const
+{
+    if (!m_xpcConnection)
+        return 0;
+
+    return xpc_connection_get_pid(m_xpcConnection.get());
+}
     
 } // namespace IPC