summaryrefslogtreecommitdiffstats
path: root/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp')
-rw-r--r--Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp401
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