summaryrefslogtreecommitdiff
path: root/Source/WebKit2/Platform/IPC/win/ConnectionWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/Platform/IPC/win/ConnectionWin.cpp')
-rw-r--r--Source/WebKit2/Platform/IPC/win/ConnectionWin.cpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/Source/WebKit2/Platform/IPC/win/ConnectionWin.cpp b/Source/WebKit2/Platform/IPC/win/ConnectionWin.cpp
new file mode 100644
index 000000000..a2730ca69
--- /dev/null
+++ b/Source/WebKit2/Platform/IPC/win/ConnectionWin.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "config.h"
+#include "Connection.h"
+
+#include "DataReference.h"
+#include <wtf/Functional.h>
+#include <wtf/RandomNumber.h>
+#include <wtf/text/WTFString.h>
+#include <wtf/threads/BinarySemaphore.h>
+
+using namespace std;
+
+namespace IPC {
+
+// FIXME: Rename this or use a different constant on windows.
+static const size_t inlineMessageMaxSize = 4096;
+
+bool Connection::createServerAndClientIdentifiers(HANDLE& serverIdentifier, HANDLE& clientIdentifier)
+{
+ String pipeName;
+
+ while (true) {
+ unsigned uniqueID = randomNumber() * std::numeric_limits<unsigned>::max();
+ pipeName = String::format("\\\\.\\pipe\\com.apple.WebKit.%x", uniqueID);
+
+ serverIdentifier = ::CreateNamedPipe(pipeName.charactersWithNullTermination().data(),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, inlineMessageMaxSize, inlineMessageMaxSize,
+ 0, 0);
+ if (!serverIdentifier && ::GetLastError() == ERROR_PIPE_BUSY) {
+ // There was already a pipe with this name, try again.
+ continue;
+ }
+
+ break;
+ }
+
+ if (!serverIdentifier)
+ return false;
+
+ clientIdentifier = ::CreateFileW(pipeName.charactersWithNullTermination().data(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (!clientIdentifier) {
+ ::CloseHandle(serverIdentifier);
+ return false;
+ }
+
+ DWORD mode = PIPE_READMODE_MESSAGE;
+ if (!::SetNamedPipeHandleState(clientIdentifier, &mode, 0, 0)) {
+ ::CloseHandle(serverIdentifier);
+ ::CloseHandle(clientIdentifier);
+ return false;
+ }
+
+ return true;
+}
+
+void Connection::platformInitialize(Identifier identifier)
+{
+ memset(&m_readState, 0, sizeof(m_readState));
+ m_readState.hEvent = ::CreateEventW(0, FALSE, FALSE, 0);
+
+ memset(&m_writeState, 0, sizeof(m_writeState));
+ m_writeState.hEvent = ::CreateEventW(0, FALSE, FALSE, 0);
+
+ m_connectionPipe = identifier;
+}
+
+void Connection::platformInvalidate()
+{
+ if (m_connectionPipe == INVALID_HANDLE_VALUE)
+ return;
+
+ m_isConnected = false;
+
+ m_connectionQueue->unregisterAndCloseHandle(m_readState.hEvent);
+ m_readState.hEvent = 0;
+
+ m_connectionQueue->unregisterAndCloseHandle(m_writeState.hEvent);
+ m_writeState.hEvent = 0;
+
+ ::CloseHandle(m_connectionPipe);
+ m_connectionPipe = INVALID_HANDLE_VALUE;
+}
+
+void Connection::readEventHandler()
+{
+ if (m_connectionPipe == INVALID_HANDLE_VALUE)
+ return;
+
+ while (true) {
+ // Check if we got some data.
+ DWORD numberOfBytesRead = 0;
+ if (!::GetOverlappedResult(m_connectionPipe, &m_readState, &numberOfBytesRead, FALSE)) {
+ DWORD error = ::GetLastError();
+
+ switch (error) {
+ case ERROR_BROKEN_PIPE:
+ connectionDidClose();
+ return;
+ case ERROR_MORE_DATA: {
+ // Read the rest of the message out of the pipe.
+
+ DWORD bytesToRead = 0;
+ if (!::PeekNamedPipe(m_connectionPipe, 0, 0, 0, 0, &bytesToRead)) {
+ DWORD error = ::GetLastError();
+ if (error == ERROR_BROKEN_PIPE) {
+ connectionDidClose();
+ return;
+ }
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ // ::GetOverlappedResult told us there's more data. ::PeekNamedPipe shouldn't
+ // contradict it!
+ ASSERT(bytesToRead);
+ if (!bytesToRead)
+ break;
+
+ m_readBuffer.grow(m_readBuffer.size() + bytesToRead);
+ if (!::ReadFile(m_connectionPipe, m_readBuffer.data() + numberOfBytesRead, bytesToRead, 0, &m_readState)) {
+ DWORD error = ::GetLastError();
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ continue;
+ }
+
+ // FIXME: We should figure out why we're getting this error.
+ case ERROR_IO_INCOMPLETE:
+ return;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ }
+
+ if (!m_readBuffer.isEmpty()) {
+ // We have a message, let's dispatch it.
+
+ OwnPtr<MessageDecoder> decoder = MessageDecoder::create(DataReference(m_readBuffer.data(), m_readBuffer.size()));
+ processIncomingMessage(decoder.release());
+ }
+
+ // Find out the size of the next message in the pipe (if there is one) so that we can read
+ // it all in one operation. (This is just an optimization to avoid an extra pass through the
+ // loop (if we chose a buffer size that was too small) or allocating extra memory (if we
+ // chose a buffer size that was too large).)
+ DWORD bytesToRead = 0;
+ if (!::PeekNamedPipe(m_connectionPipe, 0, 0, 0, 0, &bytesToRead)) {
+ DWORD error = ::GetLastError();
+ if (error == ERROR_BROKEN_PIPE) {
+ connectionDidClose();
+ return;
+ }
+ ASSERT_NOT_REACHED();
+ }
+ if (!bytesToRead) {
+ // There's no message waiting in the pipe. Schedule a read of the first byte of the
+ // next message. We'll find out the message's actual size when it arrives. (If we
+ // change this to read more than a single byte for performance reasons, we'll have to
+ // deal with m_readBuffer potentially being larger than the message we read after
+ // calling ::GetOverlappedResult above.)
+ bytesToRead = 1;
+ }
+
+ m_readBuffer.resize(bytesToRead);
+
+ // Either read the next available message (which should occur synchronously), or start an
+ // asynchronous read of the next message that becomes available.
+ BOOL result = ::ReadFile(m_connectionPipe, m_readBuffer.data(), m_readBuffer.size(), 0, &m_readState);
+ if (result) {
+ // There was already a message waiting in the pipe, and we read it synchronously.
+ // Process it.
+ continue;
+ }
+
+ DWORD error = ::GetLastError();
+
+ if (error == ERROR_IO_PENDING) {
+ // There are no messages in the pipe currently. readEventHandler will be called again once there is a message.
+ return;
+ }
+
+ if (error == ERROR_MORE_DATA) {
+ // Either a message is available when we didn't think one was, or the message is larger
+ // than ::PeekNamedPipe told us. The former seems far more likely. Probably the message
+ // became available between our calls to ::PeekNamedPipe and ::ReadFile above. Go back
+ // to the top of the loop to use ::GetOverlappedResult to retrieve the available data.
+ continue;
+ }
+
+ // FIXME: We need to handle other errors here.
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void Connection::writeEventHandler()
+{
+ if (m_connectionPipe == INVALID_HANDLE_VALUE)
+ return;
+
+ DWORD numberOfBytesWritten = 0;
+ if (!::GetOverlappedResult(m_connectionPipe, &m_writeState, &numberOfBytesWritten, FALSE)) {
+ DWORD error = ::GetLastError();
+ if (error == ERROR_IO_INCOMPLETE) {
+ // FIXME: We should figure out why we're getting this error.
+ return;
+ }
+ if (error == ERROR_BROKEN_PIPE) {
+ connectionDidClose();
+ return;
+ }
+ ASSERT_NOT_REACHED();
+ }
+
+ // The pending write has finished, so we are now done with its encoder. Clearing this member
+ // will allow us to send messages again.
+ m_pendingWriteEncoder = nullptr;
+
+ // Now that the pending write has finished, we can try to send a new message.
+ sendOutgoingMessages();
+}
+
+bool Connection::open()
+{
+ // We connected the two ends of the pipe in createServerAndClientIdentifiers.
+ m_isConnected = true;
+
+ // Start listening for read and write state events.
+ m_connectionQueue->registerHandle(m_readState.hEvent, bind(&Connection::readEventHandler, this));
+ m_connectionQueue->registerHandle(m_writeState.hEvent, bind(&Connection::writeEventHandler, this));
+
+ // Schedule a read.
+ m_connectionQueue->dispatch(bind(&Connection::readEventHandler, this));
+
+ return true;
+}
+
+bool Connection::platformCanSendOutgoingMessages() const
+{
+ // We only allow sending one asynchronous message at a time. If we wanted to send more than one
+ // at once, we'd have to use multiple OVERLAPPED structures and hold onto multiple pending
+ // MessageEncoders (one of each for each simultaneous asynchronous message).
+ return !m_pendingWriteEncoder;
+}
+
+bool Connection::sendOutgoingMessage(PassOwnPtr<MessageEncoder> encoder)
+{
+ ASSERT(!m_pendingWriteEncoder);
+
+ // Just bail if the handle has been closed.
+ if (m_connectionPipe == INVALID_HANDLE_VALUE)
+ return false;
+
+ // We put the message ID last.
+ *encoder << 0;
+
+ // Write the outgoing message.
+
+ if (::WriteFile(m_connectionPipe, encoder->buffer(), encoder->bufferSize(), 0, &m_writeState)) {
+ // We successfully sent this message.
+ return true;
+ }
+
+ DWORD error = ::GetLastError();
+
+ if (error == ERROR_NO_DATA) {
+ // The pipe is being closed.
+ connectionDidClose();
+ return false;
+ }
+
+ if (error != ERROR_IO_PENDING) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+
+ // The message will be sent soon. Hold onto the encoder so that it won't be destroyed
+ // before the write completes.
+ m_pendingWriteEncoder = encoder;
+
+ // We can only send one asynchronous message at a time (see comment in platformCanSendOutgoingMessages).
+ return false;
+}
+
+bool Connection::dispatchSentMessagesUntil(const Vector<HWND>& windows, WTF::BinarySemaphore& semaphore, double absoluteTime)
+{
+ if (windows.isEmpty())
+ return semaphore.wait(absoluteTime);
+
+ HANDLE handle = semaphore.event();
+ DWORD handleCount = 1;
+
+ while (true) {
+ DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime);
+ if (!interval) {
+ // Consider the wait to have timed out, even if the semaphore is currently signaled.
+ // This matches the WTF::ThreadCondition implementation of BinarySemaphore::wait.
+ return false;
+ }
+
+ DWORD result = ::MsgWaitForMultipleObjectsEx(handleCount, &handle, interval, QS_SENDMESSAGE, 0);
+ if (result == WAIT_OBJECT_0) {
+ // The semaphore was signaled.
+ return true;
+ }
+ if (result == WAIT_TIMEOUT) {
+ // absoluteTime was reached.
+ return false;
+ }
+ if (result == WAIT_OBJECT_0 + handleCount) {
+ // One or more sent messages are available. Process sent messages for all the windows
+ // we were given, since we don't have a way of knowing which window has available sent
+ // messages.
+ for (size_t i = 0; i < windows.size(); ++i) {
+ MSG message;
+ ::PeekMessageW(&message, windows[i], 0, 0, PM_NOREMOVE | PM_QS_SENDMESSAGE);
+ }
+ continue;
+ }
+ ASSERT_WITH_MESSAGE(result != WAIT_FAILED, "::MsgWaitForMultipleObjectsEx failed with error %lu", ::GetLastError());
+ ASSERT_WITH_MESSAGE(false, "::MsgWaitForMultipleObjectsEx returned unexpected result %lu", result);
+ return false;
+ }
+}
+
+} // namespace IPC