| /* |
| * Copyright (C) 2010-2018 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. |
| */ |
| |
| #import "config.h" |
| #import "Connection.h" |
| |
| #import "DataReference.h" |
| #import "ImportanceAssertion.h" |
| #import "MachMessage.h" |
| #import "MachPort.h" |
| #import "MachUtilities.h" |
| #import "WKCrashReporter.h" |
| #import <WebCore/AXObjectCache.h> |
| #import <mach/mach_error.h> |
| #import <mach/vm_map.h> |
| #import <sys/mman.h> |
| #import <wtf/MachSendRight.h> |
| #import <wtf/RunLoop.h> |
| #import <wtf/spi/darwin/XPCSPI.h> |
| |
| #if PLATFORM(IOS_FAMILY) |
| #import "ProcessAssertion.h" |
| #import <UIKit/UIAccessibility.h> |
| |
| #if USE(APPLE_INTERNAL_SDK) |
| #import <AXRuntime/AXDefines.h> |
| #import <AXRuntime/AXNotificationConstants.h> |
| #else |
| #define kAXPidStatusChangedNotification 0 |
| #endif |
| |
| #endif |
| |
| #if PLATFORM(MAC) |
| |
| #import "ApplicationServicesSPI.h" |
| |
| extern "C" AXError _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatus); |
| |
| #endif // PLATFORM(MAC) |
| |
| namespace IPC { |
| |
| static const size_t inlineMessageMaxSize = 4096; |
| |
| // Arbitrary message IDs that do not collide with Mach notification messages (used my initials). |
| constexpr mach_msg_id_t inlineBodyMessageID = 0xdba0dba; |
| constexpr mach_msg_id_t outOfLineBodyMessageID = 0xdba1dba; |
| |
| // ConnectionTerminationWatchdog does two things: |
| // 1) It sets a watchdog timer to kill the peered process. |
| // 2) On iOS, make the process runnable for the duration of the watchdog |
| // to ensure it has a chance to terminate cleanly. |
| class ConnectionTerminationWatchdog { |
| public: |
| static void createConnectionTerminationWatchdog(OSObjectPtr<xpc_connection_t>& xpcConnection, Seconds interval) |
| { |
| new ConnectionTerminationWatchdog(xpcConnection, interval); |
| } |
| |
| private: |
| ConnectionTerminationWatchdog(OSObjectPtr<xpc_connection_t>& xpcConnection, Seconds interval) |
| : m_xpcConnection(xpcConnection) |
| , m_watchdogTimer(RunLoop::main(), this, &ConnectionTerminationWatchdog::watchdogTimerFired) |
| #if PLATFORM(IOS_FAMILY) |
| , m_assertion(makeUnique<WebKit::ProcessAndUIAssertion>(xpc_connection_get_pid(m_xpcConnection.get()), "ConnectionTerminationWatchdog"_s, WebKit::AssertionState::Background)) |
| #endif |
| { |
| m_watchdogTimer.startOneShot(interval); |
| } |
| |
| void watchdogTimerFired() |
| { |
| xpc_connection_kill(m_xpcConnection.get(), SIGKILL); |
| delete this; |
| } |
| |
| OSObjectPtr<xpc_connection_t> m_xpcConnection; |
| RunLoop::Timer<ConnectionTerminationWatchdog> m_watchdogTimer; |
| #if PLATFORM(IOS_FAMILY) |
| std::unique_ptr<WebKit::ProcessAndUIAssertion> m_assertion; |
| #endif |
| }; |
| |
| void Connection::platformInvalidate() |
| { |
| if (!m_isConnected) { |
| if (m_sendPort) { |
| ASSERT(!m_isServer); |
| deallocateSendRightSafely(m_sendPort); |
| m_sendPort = MACH_PORT_NULL; |
| } |
| |
| if (m_receiveSource) { |
| // For a short period of time, when m_isServer is true and open() has been called, m_receiveSource has been initialized |
| // but m_isConnected has not been set to true yet. In this case, we need to cancel m_receiveSource instead of destroying |
| // m_receivePort ourselves. |
| ASSERT(m_isServer); |
| cancelReceiveSource(); |
| } |
| |
| if (m_receivePort) { |
| ASSERT(m_isServer); |
| #if !PLATFORM(WATCHOS) |
| mach_port_unguard(mach_task_self(), m_receivePort, reinterpret_cast<mach_port_context_t>(this)); |
| #endif |
| mach_port_mod_refs(mach_task_self(), m_receivePort, MACH_PORT_RIGHT_RECEIVE, -1); |
| m_receivePort = MACH_PORT_NULL; |
| } |
| |
| return; |
| } |
| |
| m_pendingOutgoingMachMessage = nullptr; |
| m_isInitializingSendSource = false; |
| m_isConnected = false; |
| |
| ASSERT(m_sendPort); |
| ASSERT(m_receivePort); |
| |
| // Unregister our ports. |
| dispatch_source_cancel(m_sendSource); |
| dispatch_release(m_sendSource); |
| m_sendSource = nullptr; |
| m_sendPort = MACH_PORT_NULL; |
| |
| cancelReceiveSource(); |
| } |
| |
| void Connection::cancelReceiveSource() |
| { |
| dispatch_source_cancel(m_receiveSource); |
| dispatch_release(m_receiveSource); |
| m_receiveSource = nullptr; |
| m_receivePort = MACH_PORT_NULL; |
| } |
| |
| void Connection::terminateSoon(Seconds interval) |
| { |
| if (m_xpcConnection) |
| ConnectionTerminationWatchdog::createConnectionTerminationWatchdog(m_xpcConnection, interval); |
| } |
| |
| void Connection::platformInitialize(Identifier identifier) |
| { |
| if (!MACH_PORT_VALID(identifier.port)) |
| return; |
| |
| if (m_isServer) { |
| m_receivePort = identifier.port; |
| m_sendPort = MACH_PORT_NULL; |
| |
| #if !PLATFORM(WATCHOS) |
| mach_port_guard(mach_task_self(), m_receivePort, reinterpret_cast<mach_port_context_t>(this), true); |
| #endif |
| } else { |
| m_receivePort = MACH_PORT_NULL; |
| m_sendPort = identifier.port; |
| } |
| |
| m_sendSource = nullptr; |
| m_receiveSource = nullptr; |
| |
| m_xpcConnection = identifier.xpcConnection; |
| } |
| |
| bool Connection::open() |
| { |
| if (m_isServer) { |
| ASSERT(m_receivePort); |
| ASSERT(!m_sendPort); |
| ASSERT(MACH_PORT_VALID(m_receivePort)); |
| } else { |
| ASSERT(!m_receivePort); |
| ASSERT(m_sendPort); |
| ASSERT(MACH_PORT_VALID(m_sendPort)); |
| |
| auto kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &m_receivePort); |
| if (kr != KERN_SUCCESS) { |
| LOG_ERROR("Could not allocate mach port, error %x: %s", kr, mach_error_string(kr)); |
| CRASH(); |
| } |
| #if !PLATFORM(WATCHOS) |
| mach_port_guard(mach_task_self(), m_receivePort, reinterpret_cast<mach_port_context_t>(this), true); |
| #endif |
| |
| #if PLATFORM(MAC) |
| mach_port_set_attributes(mach_task_self(), m_receivePort, MACH_PORT_DENAP_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 = makeUnique<Encoder>("IPC", "InitializeConnection", 0); |
| encoder->encode(MachPort(m_receivePort, MACH_MSG_TYPE_MAKE_SEND)); |
| |
| initializeSendSource(); |
| |
| sendMessage(WTFMove(encoder), { }); |
| } |
| |
| // Change the message queue length for the receive port. |
| setMachPortQueueLength(m_receivePort, MACH_PORT_QLIMIT_LARGE); |
| |
| m_receiveSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, m_receivePort, 0, m_connectionQueue->dispatchQueue()); |
| dispatch_source_set_event_handler(m_receiveSource, [this, protectedThis = makeRefPtr(this)] { |
| receiveSourceEventHandler(); |
| }); |
| dispatch_source_set_cancel_handler(m_receiveSource, [protectedThis = makeRefPtr(this), receivePort = m_receivePort] { |
| #if !PLATFORM(WATCHOS) |
| mach_port_unguard(mach_task_self(), receivePort, reinterpret_cast<mach_port_context_t>(protectedThis.get())); |
| #endif |
| mach_port_mod_refs(mach_task_self(), receivePort, MACH_PORT_RIGHT_RECEIVE, -1); |
| }); |
| |
| ref(); |
| dispatch_async(m_connectionQueue->dispatchQueue(), ^{ |
| dispatch_resume(m_receiveSource); |
| |
| if (m_sendSource) |
| dispatch_resume(m_sendSource); |
| |
| deref(); |
| }); |
| |
| return true; |
| } |
| |
| bool Connection::sendMessage(std::unique_ptr<MachMessage> message) |
| { |
| ASSERT(message); |
| ASSERT(!m_pendingOutgoingMachMessage); |
| ASSERT(!m_isInitializingSendSource); |
| |
| // 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: |
| WebKit::setCrashReportApplicationSpecificInformation((__bridge CFStringRef)[NSString stringWithFormat:@"Unhandled error code %x, message '%s::%s'", kr, message->messageReceiverName().data(), message->messageName().data()]); |
| CRASH(); |
| } |
| } |
| |
| bool Connection::platformCanSendOutgoingMessages() const |
| { |
| return !m_pendingOutgoingMachMessage && !m_isInitializingSendSource; |
| } |
| |
| bool Connection::sendOutgoingMessage(std::unique_ptr<Encoder> encoder) |
| { |
| ASSERT(!m_pendingOutgoingMachMessage && !m_isInitializingSendSource); |
| |
| auto attachments = encoder->releaseAttachments(); |
| |
| auto numberOfPortDescriptors = std::count_if(attachments.begin(), attachments.end(), [](auto& attachment) |
| { |
| return attachment.type() == Attachment::MachPortType; |
| }); |
| |
| bool messageBodyIsOOL = false; |
| auto messageSize = MachMessage::messageSize(encoder->bufferSize(), numberOfPortDescriptors, messageBodyIsOOL); |
| if (messageSize > inlineMessageMaxSize) { |
| messageBodyIsOOL = true; |
| messageSize = MachMessage::messageSize(0, numberOfPortDescriptors, messageBodyIsOOL); |
| } |
| |
| auto message = MachMessage::create(encoder->messageReceiverName().toString(), encoder->messageName().toString(), messageSize); |
| |
| auto* 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; |
| header->msgh_local_port = MACH_PORT_NULL; |
| header->msgh_id = messageBodyIsOOL ? outOfLineBodyMessageID : inlineBodyMessageID; |
| |
| auto* messageData = reinterpret_cast<uint8_t*>(header + 1); |
| |
| bool isComplex = numberOfPortDescriptors || messageBodyIsOOL; |
| if (isComplex) { |
| header->msgh_bits |= MACH_MSGH_BITS_COMPLEX; |
| |
| auto* body = reinterpret_cast<mach_msg_body_t*>(messageData); |
| body->msgh_descriptor_count = numberOfPortDescriptors + messageBodyIsOOL; |
| messageData = reinterpret_cast<uint8_t*>(body + 1); |
| |
| auto getDescriptorAndAdvance = [](uint8_t*& data, std::size_t toAdvance) { |
| return reinterpret_cast<mach_msg_descriptor_t*>(std::exchange(data, data + toAdvance)); |
| }; |
| |
| for (auto& attachment : attachments) { |
| ASSERT(attachment.type() == Attachment::MachPortType); |
| if (attachment.type() == Attachment::MachPortType) { |
| auto* descriptor = getDescriptorAndAdvance(messageData, sizeof(mach_msg_port_descriptor_t)); |
| descriptor->port.name = attachment.port(); |
| descriptor->port.disposition = attachment.disposition(); |
| descriptor->port.type = MACH_MSG_PORT_DESCRIPTOR; |
| } |
| } |
| |
| if (messageBodyIsOOL) { |
| auto* descriptor = getDescriptorAndAdvance(messageData, sizeof(mach_msg_ool_descriptor_t)); |
| descriptor->out_of_line.address = encoder->buffer(); |
| descriptor->out_of_line.size = encoder->bufferSize(); |
| descriptor->out_of_line.copy = MACH_MSG_VIRTUAL_COPY; |
| descriptor->out_of_line.deallocate = false; |
| descriptor->out_of_line.type = MACH_MSG_OOL_DESCRIPTOR; |
| } |
| } |
| |
| // Copy the data if it is not being sent out-of-line. |
| if (!messageBodyIsOOL) |
| memcpy(messageData, encoder->buffer(), encoder->bufferSize()); |
| |
| ASSERT(m_sendPort); |
| ASSERT(MACH_PORT_VALID(m_sendPort)); |
| |
| return sendMessage(WTFMove(message)); |
| } |
| |
| void Connection::initializeSendSource() |
| { |
| m_sendSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, m_sendPort, DISPATCH_MACH_SEND_DEAD | DISPATCH_MACH_SEND_POSSIBLE, m_connectionQueue->dispatchQueue()); |
| m_isInitializingSendSource = true; |
| |
| dispatch_source_set_registration_handler(m_sendSource, [this, protectedThis = makeRefPtr(this)] { |
| if (!m_sendSource) |
| return; |
| m_isInitializingSendSource = false; |
| resumeSendSource(); |
| }); |
| dispatch_source_set_event_handler(m_sendSource, [this, protectedThis = makeRefPtr(this)] { |
| if (!m_sendSource) |
| return; |
| |
| unsigned long data = dispatch_source_get_data(m_sendSource); |
| |
| if (data & DISPATCH_MACH_SEND_DEAD) { |
| connectionDidClose(); |
| return; |
| } |
| |
| if (data & DISPATCH_MACH_SEND_POSSIBLE) { |
| // FIXME: Figure out why we get spurious DISPATCH_MACH_SEND_POSSIBLE events. |
| resumeSendSource(); |
| return; |
| } |
| }); |
| |
| ASSERT(MACH_PORT_VALID(m_sendPort)); |
| mach_port_t sendPort = m_sendPort; |
| dispatch_source_set_cancel_handler(m_sendSource, ^{ |
| // Release our send right. |
| deallocateSendRightSafely(sendPort); |
| }); |
| } |
| |
| void Connection::resumeSendSource() |
| { |
| ASSERT(!m_isInitializingSendSource); |
| if (m_pendingOutgoingMachMessage) |
| sendMessage(WTFMove(m_pendingOutgoingMachMessage)); |
| sendOutgoingMessages(); |
| } |
| |
| 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 makeUnique<Decoder>(body, bodySize, nullptr, Vector<Attachment> { }); |
| } |
| |
| bool messageBodyIsOOL = header->msgh_id == outOfLineBodyMessageID; |
| |
| mach_msg_body_t* body = reinterpret_cast<mach_msg_body_t*>(header + 1); |
| mach_msg_size_t numDescriptors = body->msgh_descriptor_count; |
| ASSERT(numDescriptors); |
| |
| uint8_t* descriptorData = reinterpret_cast<uint8_t*>(body + 1); |
| |
| // If the message body was sent out-of-line, don't treat the last descriptor |
| // as an attachment, since it is really the message body. |
| if (messageBodyIsOOL) |
| --numDescriptors; |
| |
| // Build attachment list |
| Vector<Attachment> attachments(numDescriptors); |
| |
| for (mach_msg_size_t i = 0; i < numDescriptors; ++i) { |
| mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData); |
| ASSERT(descriptor->type.type == MACH_MSG_PORT_DESCRIPTOR); |
| if (descriptor->type.type != MACH_MSG_PORT_DESCRIPTOR) |
| return nullptr; |
| |
| attachments[numDescriptors - i - 1] = Attachment(descriptor->port.name, descriptor->port.disposition); |
| descriptorData += sizeof(mach_msg_port_descriptor_t); |
| } |
| |
| if (messageBodyIsOOL) { |
| mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData); |
| ASSERT(descriptor->type.type == MACH_MSG_OOL_DESCRIPTOR); |
| if (descriptor->type.type != MACH_MSG_OOL_DESCRIPTOR) |
| return nullptr; |
| |
| uint8_t* messageBody = static_cast<uint8_t*>(descriptor->out_of_line.address); |
| size_t messageBodySize = descriptor->out_of_line.size; |
| |
| return makeUnique<Decoder>(messageBody, messageBodySize, [](const uint8_t* buffer, size_t length) { |
| vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(buffer), length); |
| }, WTFMove(attachments)); |
| } |
| |
| uint8_t* messageBody = descriptorData; |
| size_t messageBodySize = header->msgh_size - (descriptorData - reinterpret_cast<uint8_t*>(header)); |
| |
| return makeUnique<Decoder>(messageBody, messageBodySize, nullptr, WTFMove(attachments)); |
| } |
| |
| // The receive buffer size should always include the maximum trailer size. |
| static const size_t receiveBufferSize = inlineMessageMaxSize + MAX_TRAILER_SIZE; |
| typedef Vector<char, receiveBufferSize> ReceiveBuffer; |
| |
| static mach_msg_header_t* readFromMachPort(mach_port_t machPort, ReceiveBuffer& buffer) |
| { |
| ASSERT(MACH_PORT_VALID(machPort)); |
| |
| buffer.resize(receiveBufferSize); |
| |
| mach_msg_header_t* header = reinterpret_cast<mach_msg_header_t*>(buffer.data()); |
| kern_return_t kr = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER, 0, buffer.size(), machPort, 0, MACH_PORT_NULL); |
| if (kr == MACH_RCV_TIMED_OUT) |
| return nullptr; |
| |
| if (kr == MACH_RCV_TOO_LARGE) { |
| // The message was too large, resize the buffer and try again. |
| buffer.resize(header->msgh_size + MAX_TRAILER_SIZE); |
| header = reinterpret_cast<mach_msg_header_t*>(buffer.data()); |
| |
| kr = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER, 0, buffer.size(), machPort, 0, MACH_PORT_NULL); |
| ASSERT(kr != MACH_RCV_TOO_LARGE); |
| } |
| |
| if (kr != MACH_MSG_SUCCESS) { |
| #if !ASSERT_DISABLED |
| WebKit::setCrashReportApplicationSpecificInformation((__bridge CFStringRef)[NSString stringWithFormat:@"Unhandled error code %x from mach_msg, receive port is %x", kr, machPort]); |
| #endif |
| ASSERT_NOT_REACHED(); |
| return nullptr; |
| } |
| |
| return header; |
| } |
| |
| void Connection::receiveSourceEventHandler() |
| { |
| ReceiveBuffer buffer; |
| |
| ASSERT(MACH_PORT_VALID(m_receivePort)); |
| mach_msg_header_t* header = readFromMachPort(m_receivePort, buffer); |
| if (!header) |
| return; |
| |
| switch (header->msgh_id) { |
| case MACH_NOTIFY_NO_SENDERS: |
| ASSERT(m_isServer); |
| if (!m_sendPort) |
| connectionDidClose(); |
| return; |
| |
| case inlineBodyMessageID: |
| case outOfLineBodyMessageID: |
| break; |
| |
| case MACH_NOTIFY_SEND_ONCE: |
| default: |
| return; |
| } |
| |
| std::unique_ptr<Decoder> decoder = createMessageDecoder(header); |
| if (!decoder) |
| return; |
| |
| #if PLATFORM(MAC) |
| decoder->setImportanceAssertion(makeUnique<ImportanceAssertion>(header)); |
| #endif |
| |
| if (decoder->messageReceiverName() == "IPC" && decoder->messageName() == "InitializeConnection") { |
| ASSERT(m_isServer); |
| ASSERT(!m_isConnected); |
| ASSERT(!m_sendPort); |
| |
| MachPort port; |
| if (!decoder->decode(port)) { |
| // FIXME: Disconnect. |
| return; |
| } |
| |
| m_sendPort = port.port(); |
| |
| if (m_sendPort) { |
| ASSERT(MACH_PORT_VALID(m_receivePort)); |
| mach_port_t previousNotificationPort = MACH_PORT_NULL; |
| auto kr = mach_port_request_notification(mach_task_self(), m_receivePort, MACH_NOTIFY_NO_SENDERS, 0, MACH_PORT_NULL, MACH_MSG_TYPE_MOVE_SEND_ONCE, &previousNotificationPort); |
| ASSERT(kr == KERN_SUCCESS); |
| if (kr != KERN_SUCCESS) { |
| // If mach_port_request_notification fails, 'previousNotificationPort' will be uninitialized. |
| LOG_ERROR("mach_port_request_notification failed: (%x) %s", kr, mach_error_string(kr)); |
| previousNotificationPort = MACH_PORT_NULL; |
| } |
| |
| if (previousNotificationPort != MACH_PORT_NULL) |
| deallocateSendRightSafely(previousNotificationPort); |
| |
| initializeSendSource(); |
| dispatch_resume(m_sendSource); |
| } |
| |
| m_isConnected = true; |
| |
| // Send any pending outgoing messages. |
| sendOutgoingMessages(); |
| |
| return; |
| } |
| |
| #if !PLATFORM(IOS_FAMILY) |
| if (decoder->messageReceiverName() == "IPC" && decoder->messageName() == "SetExceptionPort") { |
| if (m_isServer) { |
| // Server connections aren't supposed to have their exception ports overridden. Treat this as an invalid message. |
| 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; |
| if (!decoder->decode(exceptionPort)) |
| return; |
| |
| setMachExceptionPort(exceptionPort.port()); |
| return; |
| } |
| #endif |
| |
| processIncomingMessage(WTFMove(decoder)); |
| } |
| |
| IPC::Connection::Identifier Connection::identifier() const |
| { |
| return Identifier(m_isServer ? m_receivePort : m_sendPort, m_xpcConnection); |
| } |
| |
| Optional<audit_token_t> Connection::getAuditToken() |
| { |
| if (!m_xpcConnection) |
| return WTF::nullopt; |
| |
| audit_token_t auditToken; |
| xpc_connection_get_audit_token(m_xpcConnection.get(), &auditToken); |
| return WTFMove(auditToken); |
| } |
| |
| bool Connection::kill() |
| { |
| if (m_xpcConnection) { |
| xpc_connection_kill(m_xpcConnection.get(), SIGKILL); |
| m_wasKilled = true; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void AccessibilityProcessSuspendedNotification(bool suspended) |
| { |
| #if PLATFORM(MAC) |
| _AXUIElementNotifyProcessSuspendStatus(suspended ? AXSuspendStatusSuspended : AXSuspendStatusRunning); |
| #elif PLATFORM(IOS_FAMILY) |
| 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(OptionSet<SendSyncOption> sendSyncOptions) |
| { |
| 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 |