#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 CoreIPC {
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(),
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) {
continue;
}
break;
}
if (!serverIdentifier)
return false;
clientIdentifier = ::CreateFileW(pipeName.charactersWithNullTermination(), 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) {
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: {
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;
}
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;
}
case ERROR_IO_INCOMPLETE:
return;
default:
ASSERT_NOT_REACHED();
}
}
if (!m_readBuffer.isEmpty()) {
OwnPtr<MessageDecoder> decoder = MessageDecoder::create(DataReference(m_readBuffer.data(), m_readBuffer.size()));
processIncomingMessage(decoder.release());
}
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) {
bytesToRead = 1;
}
m_readBuffer.resize(bytesToRead);
BOOL result = ::ReadFile(m_connectionPipe, m_readBuffer.data(), m_readBuffer.size(), 0, &m_readState);
if (result) {
continue;
}
DWORD error = ::GetLastError();
if (error == ERROR_IO_PENDING) {
return;
}
if (error == ERROR_MORE_DATA) {
continue;
}
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) {
return;
}
if (error == ERROR_BROKEN_PIPE) {
connectionDidClose();
return;
}
ASSERT_NOT_REACHED();
}
m_pendingWriteEncoder = nullptr;
sendOutgoingMessages();
}
bool Connection::open()
{
m_isConnected = true;
m_connectionQueue->registerHandle(m_readState.hEvent, bind(&Connection::readEventHandler, this));
m_connectionQueue->registerHandle(m_writeState.hEvent, bind(&Connection::writeEventHandler, this));
m_connectionQueue->dispatch(bind(&Connection::readEventHandler, this));
return true;
}
bool Connection::platformCanSendOutgoingMessages() const
{
return !m_pendingWriteEncoder;
}
bool Connection::sendOutgoingMessage(PassOwnPtr<MessageEncoder> encoder)
{
ASSERT(!m_pendingWriteEncoder);
if (m_connectionPipe == INVALID_HANDLE_VALUE)
return false;
*encoder << 0;
if (::WriteFile(m_connectionPipe, encoder->buffer(), encoder->bufferSize(), 0, &m_writeState)) {
return true;
}
DWORD error = ::GetLastError();
if (error == ERROR_NO_DATA) {
connectionDidClose();
return false;
}
if (error != ERROR_IO_PENDING) {
ASSERT_NOT_REACHED();
return false;
}
m_pendingWriteEncoder = encoder;
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) {
return false;
}
DWORD result = ::MsgWaitForMultipleObjectsEx(handleCount, &handle, interval, QS_SENDMESSAGE, 0);
if (result == WAIT_OBJECT_0) {
return true;
}
if (result == WAIT_TIMEOUT) {
return false;
}
if (result == WAIT_OBJECT_0 + handleCount) {
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;
}
}
}