diff options
Diffstat (limited to 'Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp')
-rw-r--r-- | Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp b/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp new file mode 100644 index 0000000..92ffff3 --- /dev/null +++ b/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "config.h" +#include "Connection.h" + +#include "ArgumentEncoder.h" +#include "ProcessLauncher.h" +#include "WorkItem.h" +#include "SharedMemory.h" +#include "WebProcessProxy.h" +#include <sys/socket.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <wtf/Assertions.h> + +#if PLATFORM(QT) +#include <QApplication> +#include <QSocketNotifier> +#elif PLATFORM(GTK) +#include <glib.h> +#endif + +using namespace std; + +namespace CoreIPC { + +static const size_t messageMaxSize = 4096; +static const size_t attachmentMaxAmount = 255; + +enum { + MessageBodyIsOOL = 1U << 31 +}; + +class MessageInfo { +public: + MessageInfo() { } + + MessageInfo(MessageID messageID, size_t bodySize, size_t initialAttachmentCount) + : m_messageID(messageID.toInt()) + , m_bodySize(bodySize) + , m_attachmentCount(initialAttachmentCount) + { + ASSERT(!(m_messageID & MessageBodyIsOOL)); + } + + void setMessageBodyOOL() + { + ASSERT(!isMessageBodyOOL()); + + m_messageID |= MessageBodyIsOOL; + m_attachmentCount++; + } + + bool isMessageBodyOOL() const { return m_messageID & MessageBodyIsOOL; } + + size_t bodySize() const { return m_bodySize; } + + MessageID messageID() const { return MessageID::fromInt(m_messageID & ~MessageBodyIsOOL); } + + size_t attachmentCount() const { return m_attachmentCount; } + +private: + uint32_t m_messageID; + size_t m_bodySize; + size_t m_attachmentCount; +}; + +void Connection::platformInitialize(Identifier identifier) +{ + m_socketDescriptor = identifier; + m_readBuffer.resize(messageMaxSize); + m_currentMessageSize = 0; + +#if PLATFORM(QT) + m_socketNotifier = 0; +#endif +} + +void Connection::platformInvalidate() +{ + if (m_socketDescriptor != -1) + while (close(m_socketDescriptor) == -1 && errno == EINTR) { } + + if (!m_isConnected) + return; + +#if PLATFORM(GTK) + m_connectionQueue.unregisterEventSourceHandler(m_socketDescriptor); +#endif + +#if PLATFORM(QT) + delete m_socketNotifier; + m_socketNotifier = 0; +#endif + + m_socketDescriptor = -1; + m_isConnected = false; +} + +#if PLATFORM(QT) +class SocketNotifierResourceGuard { +public: + SocketNotifierResourceGuard(QSocketNotifier* socketNotifier) + : m_socketNotifier(socketNotifier) + { + m_socketNotifier->setEnabled(false); + } + + ~SocketNotifierResourceGuard() + { + m_socketNotifier->setEnabled(true); + } + +private: + QSocketNotifier* const m_socketNotifier; +}; +#endif + +template<class T, class iterator> +class AttachmentResourceGuard { +public: + AttachmentResourceGuard(T& attachments) + : m_attachments(attachments) + { + } + ~AttachmentResourceGuard() + { + iterator end = m_attachments.end(); + for (iterator i = m_attachments.begin(); i != end; ++i) + i->dispose(); + } +private: + T& m_attachments; +}; + +void Connection::readyReadHandler() +{ + Deque<Attachment> attachments; +#if PLATFORM(QT) + SocketNotifierResourceGuard socketNotifierEnabler(m_socketNotifier); +#endif + AttachmentResourceGuard<Deque<Attachment>, Deque<Attachment>::iterator> attachementDisposer(attachments); + + OwnArrayPtr<char> attachmentDescriptorBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * (attachmentMaxAmount))]); + struct msghdr message; + memset(&message, 0, sizeof(message)); + + struct iovec iov[1]; + memset(&iov, 0, sizeof(iov)); + + message.msg_control = attachmentDescriptorBuffer.get(); + message.msg_controllen = CMSG_SPACE(sizeof(int) * (attachmentMaxAmount)); + + iov[0].iov_base = m_readBuffer.data(); + iov[0].iov_len = m_readBuffer.size(); + + message.msg_iov = iov; + message.msg_iovlen = 1; + + + int messageLength = 0; + while ((messageLength = recvmsg(m_socketDescriptor, &message, 0)) == -1) { + if (errno != EINTR) + return; + } + + struct cmsghdr* controlMessage = CMSG_FIRSTHDR(&message); + + MessageInfo messageInfo; + unsigned char* messageData = m_readBuffer.data(); + + memcpy(&messageInfo, messageData, sizeof(messageInfo)); + ASSERT(messageLength == sizeof(messageInfo) + messageInfo.attachmentCount() * sizeof(size_t) + (messageInfo.isMessageBodyOOL() ? 0 : messageInfo.bodySize())); + + messageData += sizeof(messageInfo); + + RefPtr<WebKit::SharedMemory> oolMessageBody; + + if (messageInfo.attachmentCount()) { + if (controlMessage && controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) { + OwnArrayPtr<size_t> attachmentSizes = adoptArrayPtr(new size_t[messageInfo.attachmentCount()]); + memcpy(attachmentSizes.get(), messageData, sizeof(size_t) * messageInfo.attachmentCount()); + + messageData += sizeof(attachmentSizes); + + OwnArrayPtr<int> fileDescriptors = adoptArrayPtr(new int[messageInfo.attachmentCount()]); + memcpy(fileDescriptors.get(), CMSG_DATA(controlMessage), sizeof(int) * messageInfo.attachmentCount()); + + int attachmentCount = messageInfo.attachmentCount(); + + if (messageInfo.isMessageBodyOOL()) + attachmentCount--; + + for (int i = 0; i < attachmentCount; ++i) { + while (fcntl(fileDescriptors[i], F_SETFL, FD_CLOEXEC) == -1) { + if (errno != EINTR) { + ASSERT_NOT_REACHED(); + return; + } + } + } + + for (int i = 0; i < attachmentCount; ++i) + attachments.append(Attachment(fileDescriptors[i], attachmentSizes[i])); + + if (messageInfo.isMessageBodyOOL()) { + ASSERT(messageInfo.bodySize()); + + WebKit::SharedMemory::Handle handle; + handle.adoptFromAttachment(fileDescriptors[attachmentCount], attachmentSizes[attachmentCount]); + if (handle.isNull()) { + ASSERT_NOT_REACHED(); + return; + } + + oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly); + if (!oolMessageBody) { + ASSERT_NOT_REACHED(); + return; + } + } + + controlMessage = CMSG_NXTHDR(&message, controlMessage); + } else { + ASSERT_NOT_REACHED(); + return; + } + } + + ASSERT(attachments.size() == messageInfo.isMessageBodyOOL() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount()); + + unsigned char* messageBody = messageData; + + if (messageInfo.isMessageBodyOOL()) + messageBody = reinterpret_cast<unsigned char*>(oolMessageBody->data()); + + ArgumentDecoder* argumentDecoder; + if (attachments.isEmpty()) + argumentDecoder = new ArgumentDecoder(messageBody, messageInfo.bodySize()); + else + argumentDecoder = new ArgumentDecoder(messageBody, messageInfo.bodySize(), attachments); + + processIncomingMessage(messageInfo.messageID(), adoptPtr(argumentDecoder)); + + ASSERT(!controlMessage); +} + +bool Connection::open() +{ +#if PLATFORM(QT) + ASSERT(!m_socketNotifier); +#endif + + int flags = fcntl(m_socketDescriptor, F_GETFL, 0); + while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) { + if (errno != EINTR) { + ASSERT_NOT_REACHED(); + return false; + } + } + + m_isConnected = true; +#if PLATFORM(QT) + m_socketNotifier = m_connectionQueue.registerSocketEventHandler(m_socketDescriptor, QSocketNotifier::Read, WorkItem::create(this, &Connection::readyReadHandler)); +#elif PLATFORM(GTK) + m_connectionQueue.registerEventSourceHandler(m_socketDescriptor, (G_IO_HUP | G_IO_ERR), WorkItem::create(this, &Connection::connectionDidClose)); + m_connectionQueue.registerEventSourceHandler(m_socketDescriptor, G_IO_IN, WorkItem::create(this, &Connection::readyReadHandler)); +#endif + + // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal + // handler. + m_connectionQueue.scheduleWork(WorkItem::create(this, &Connection::readyReadHandler)); + + return true; +} + +bool Connection::platformCanSendOutgoingMessages() const +{ + return m_isConnected; +} + +bool Connection::sendOutgoingMessage(MessageID messageID, PassOwnPtr<ArgumentEncoder> arguments) +{ +#if PLATFORM(QT) + ASSERT(m_socketNotifier); +#endif + + COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline); + + Vector<Attachment> attachments = arguments->releaseAttachments(); + AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments); + + if (attachments.size() > (attachmentMaxAmount - 1)) { + ASSERT_NOT_REACHED(); + return false; + } + + MessageInfo messageInfo(messageID, arguments->bufferSize(), attachments.size()); + size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(size_t)) + arguments->bufferSize(); + if (messageSizeWithBodyInline > messageMaxSize && arguments->bufferSize()) { + RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::create(arguments->bufferSize()); + if (!oolMessageBody) + return false; + + WebKit::SharedMemory::Handle handle; + if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly)) + return false; + + messageInfo.setMessageBodyOOL(); + + memcpy(oolMessageBody->data(), arguments->buffer(), arguments->bufferSize()); + + attachments.append(handle.releaseToAttachment()); + } + + struct msghdr message; + memset(&message, 0, sizeof(message)); + + struct iovec iov[3]; + memset(&iov, 0, sizeof(iov)); + + message.msg_iov = iov; + int iovLength = 1; + + iov[0].iov_base = reinterpret_cast<void*>(&messageInfo); + iov[0].iov_len = sizeof(messageInfo); + + OwnArrayPtr<char> attachmentFDBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * attachments.size())]); + OwnArrayPtr<size_t> attachmentSizes = adoptArrayPtr(new size_t[attachments.size()]); + + if (!attachments.isEmpty()) { + message.msg_control = attachmentFDBuffer.get(); + message.msg_controllen = sizeof(char) * CMSG_SPACE(sizeof(int) * attachments.size()); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachments.size()); + + int* fdptr = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + for (size_t i = 0; i < attachments.size(); ++i) { + attachmentSizes[i] = attachments[i].size(); + fdptr[i] = attachments[i].fileDescriptor(); + } + + message.msg_controllen = cmsg->cmsg_len; + + iov[iovLength].iov_base = attachmentSizes.get(); + iov[iovLength].iov_len = sizeof(size_t) * attachments.size(); + ++iovLength; + } + + if (!messageInfo.isMessageBodyOOL() && arguments->bufferSize()) { + iov[iovLength].iov_base = reinterpret_cast<void*>(arguments->buffer()); + iov[iovLength].iov_len = arguments->bufferSize(); + ++iovLength; + } + + message.msg_iovlen = iovLength; + + int bytesSent = 0; + while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) { + if (errno != EINTR) + return false; + } + return true; +} + +#if PLATFORM(QT) || PLATFORM(GTK) +void Connection::setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process) +{ + m_connectionQueue.scheduleWorkOnTermination(process, WorkItem::create(this, &Connection::connectionDidClose)); +} +#endif + +} // namespace CoreIPC |