blob: 279da454402445602031c11ab501c03e83ff0558 [file] [log] [blame]
/*
* Copyright (C) 2017 Igalia S.L.
*
* 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
* 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 "RemoteInspectorServer.h"
#if ENABLE(REMOTE_INSPECTOR)
#include "RemoteInspectorUtils.h"
#include <gio/gio.h>
#include <wtf/Vector.h>
#include <wtf/glib/GUniquePtr.h>
namespace Inspector {
static uint64_t generateConnectionID()
{
static uint64_t connectionID = 0;
return ++connectionID;
}
static RemoteInspector::Client::SessionCapabilities processSessionCapabilities(GVariant* sessionCapabilities)
{
RemoteInspector::Client::SessionCapabilities capabilities;
gboolean acceptInsecureCerts;
if (g_variant_lookup(sessionCapabilities, "acceptInsecureCerts", "b", &acceptInsecureCerts))
capabilities.acceptInsecureCertificates = acceptInsecureCerts;
if (GRefPtr<GVariant> certificates = g_variant_lookup_value(sessionCapabilities, "certificates", G_VARIANT_TYPE("a(ss)"))) {
GVariantIter iter;
auto childCount = g_variant_iter_init(&iter, certificates.get());
capabilities.certificates.reserveCapacity(childCount);
const char* host;
const char* certificateFile;
while (g_variant_iter_loop(&iter, "(&s&s)", &host, &certificateFile))
capabilities.certificates.uncheckedAppend({ String::fromUTF8(host), String::fromUTF8(certificateFile) });
}
if (GRefPtr<GVariant> proxy = g_variant_lookup_value(sessionCapabilities, "proxy", G_VARIANT_TYPE("a{sv}"))) {
capabilities.proxy = RemoteInspector::Client::SessionCapabilities::Proxy();
const char* proxyType;
g_variant_lookup(proxy.get(), "type", "&s", &proxyType);
capabilities.proxy->type = String::fromUTF8(proxyType);
const char* ftpURL;
if (g_variant_lookup(proxy.get(), "ftpURL", "&s", &ftpURL))
capabilities.proxy->ftpURL = String::fromUTF8(ftpURL);
const char* httpURL;
if (g_variant_lookup(proxy.get(), "httpURL", "&s", &httpURL))
capabilities.proxy->httpURL = String::fromUTF8(httpURL);
const char* httpsURL;
if (g_variant_lookup(proxy.get(), "httpsURL", "&s", &httpsURL))
capabilities.proxy->httpsURL = String::fromUTF8(httpsURL);
const char* socksURL;
if (g_variant_lookup(proxy.get(), "socksURL", "&s", &socksURL))
capabilities.proxy->socksURL = String::fromUTF8(socksURL);
if (GRefPtr<GVariant> ignoreAddressList = g_variant_lookup_value(proxy.get(), "ignoreAddressList", G_VARIANT_TYPE("as"))) {
gsize ignoreAddressListLength;
GUniquePtr<char> ignoreAddressArray(reinterpret_cast<char*>(g_variant_get_strv(ignoreAddressList.get(), &ignoreAddressListLength)));
for (unsigned i = 0; i < ignoreAddressListLength; ++i)
capabilities.proxy->ignoreAddressList.append(String::fromUTF8(reinterpret_cast<char**>(ignoreAddressArray.get())[i]));
}
}
return capabilities;
}
const SocketConnection::MessageHandlers RemoteInspectorServer::s_messageHandlers = {
{ "DidClose", std::pair<CString, SocketConnection::MessageCallback> { { },
[](SocketConnection& connection, GVariant*, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
inspectorServer.connectionDidClose(connection);
}}
},
{ "SetTargetList", std::pair<CString, SocketConnection::MessageCallback> { "(a(tsssb)b)",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
inspectorServer.setTargetList(connection, parameters);
}}
},
{ "SetupInspectorClient", std::pair<CString, SocketConnection::MessageCallback> { "(ay)",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
GRefPtr<GVariant> backendCommandsHash;
g_variant_get(parameters, "(@ay)", &backendCommandsHash.outPtr());
auto* backendCommands = inspectorServer.setupInspectorClient(connection, g_variant_get_bytestring(backendCommandsHash.get()));
connection.sendMessage("DidSetupInspectorClient", g_variant_new("(@ay)", backendCommands));
}}
},
{ "Setup", std::pair<CString, SocketConnection::MessageCallback> { "(tt)",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
guint64 connectionID, targetID;
g_variant_get(parameters, "(tt)", &connectionID, &targetID);
inspectorServer.setup(connection, connectionID, targetID);
}}
},
{ "FrontendDidClose", std::pair<CString, SocketConnection::MessageCallback> { "(tt)",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
guint64 connectionID, targetID;
g_variant_get(parameters, "(tt)", &connectionID, &targetID);
inspectorServer.close(connection, connectionID, targetID);
}}
},
{ "SendMessageToFrontend", std::pair<CString, SocketConnection::MessageCallback> { "(ts)",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
guint64 targetID;
const char* message;
g_variant_get(parameters, "(t&s)", &targetID, &message);
inspectorServer.sendMessageToFrontend(connection, targetID, message);
}}
},
{ "SendMessageToBackend", std::pair<CString, SocketConnection::MessageCallback> { "(tts)",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
guint64 connectionID, targetID;
const char* message;
g_variant_get(parameters, "(tt&s)", &connectionID, &targetID, &message);
inspectorServer.sendMessageToBackend(connection, connectionID, targetID, message);
}}
},
{ "StartAutomationSession", std::pair<CString, SocketConnection::MessageCallback> { "(sa{sv})",
[](SocketConnection& connection, GVariant* parameters, gpointer userData) {
auto& inspectorServer = *static_cast<RemoteInspectorServer*>(userData);
const char* sessionID;
GRefPtr<GVariant> sessionCapabilities;
g_variant_get(parameters, "(&s@a{sv})", &sessionID, &sessionCapabilities.outPtr());
auto capabilities = processSessionCapabilities(sessionCapabilities.get());
inspectorServer.startAutomationSession(connection, sessionID, capabilities);
auto clientCapabilities = RemoteInspector::singleton().clientCapabilities();
connection.sendMessage("DidStartAutomationSession", g_variant_new("(ss)",
clientCapabilities ? clientCapabilities->browserName.utf8().data() : "",
clientCapabilities ? clientCapabilities->browserVersion.utf8().data() : ""));
}}
}
};
RemoteInspectorServer& RemoteInspectorServer::singleton()
{
static RemoteInspectorServer server;
return server;
}
RemoteInspectorServer::~RemoteInspectorServer()
{
if (m_service)
g_signal_handlers_disconnect_matched(m_service.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
}
bool RemoteInspectorServer::start(const char* address, unsigned port)
{
m_service = adoptGRef(g_socket_service_new());
g_signal_connect(m_service.get(), "incoming", G_CALLBACK(incomingConnectionCallback), this);
GRefPtr<GSocketAddress> socketAddress = adoptGRef(g_inet_socket_address_new_from_string(address, port));
GUniqueOutPtr<GError> error;
if (!g_socket_listener_add_address(G_SOCKET_LISTENER(m_service.get()), socketAddress.get(), G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, nullptr, nullptr, &error.outPtr())) {
g_warning("Failed to start remote inspector server on %s:%u: %s\n", address, port, error->message);
return false;
}
return true;
}
gboolean RemoteInspectorServer::incomingConnectionCallback(GSocketService*, GSocketConnection* connection, GObject*, RemoteInspectorServer* inspectorServer)
{
inspectorServer->incomingConnection(SocketConnection::create(GRefPtr<GSocketConnection>(connection), s_messageHandlers, inspectorServer));
return TRUE;
}
void RemoteInspectorServer::incomingConnection(Ref<SocketConnection>&& connection)
{
ASSERT(!m_connections.contains(connection.ptr()));
m_connections.add(WTFMove(connection));
}
void RemoteInspectorServer::setTargetList(SocketConnection& remoteInspectorConnection, GVariant* parameters)
{
ASSERT(m_connections.contains(&remoteInspectorConnection));
auto addResult = m_remoteInspectorConnectionToIDMap.add(&remoteInspectorConnection, 0);
if (addResult.isNewEntry) {
addResult.iterator->value = generateConnectionID();
m_idToRemoteInspectorConnectionMap.add(addResult.iterator->value, &remoteInspectorConnection);
}
gboolean remoteAutomationEnabled;
GRefPtr<GVariant> targetList;
g_variant_get(parameters, "(@a(tsssb)b)", &targetList.outPtr(), &remoteAutomationEnabled);
SocketConnection* clientConnection = remoteAutomationEnabled && m_automationConnection ? m_automationConnection : m_clientConnection;
if (!clientConnection)
return;
clientConnection->sendMessage("SetTargetList", g_variant_new("(t@a(tsssb))", addResult.iterator->value, targetList.get()));
}
GVariant* RemoteInspectorServer::setupInspectorClient(SocketConnection& clientConnection, const char* clientBackendCommandsHash)
{
ASSERT(!m_clientConnection);
m_clientConnection = &clientConnection;
GVariant* backendCommands;
if (strcmp(clientBackendCommandsHash, backendCommandsHash().data())) {
auto bytes = Inspector::backendCommands();
backendCommands = g_variant_new_bytestring(static_cast<const char*>(g_bytes_get_data(bytes.get(), nullptr)));
} else
backendCommands = g_variant_new_bytestring("");
// Ask all remote inspectors to push their target lists to notify the new client.
for (auto* remoteInspectorConnection : m_remoteInspectorConnectionToIDMap.keys())
remoteInspectorConnection->sendMessage("GetTargetList", nullptr);
return backendCommands;
}
void RemoteInspectorServer::setup(SocketConnection& clientConnection, uint64_t connectionID, uint64_t targetID)
{
ASSERT(m_clientConnection == &clientConnection || m_automationConnection == &clientConnection);
ASSERT(m_idToRemoteInspectorConnectionMap.contains(connectionID));
if (&clientConnection == m_automationConnection) {
m_automationTargets.add(std::make_pair(connectionID, targetID));
RemoteInspector::singleton().setup(targetID);
return;
}
m_inspectionTargets.add(std::make_pair(connectionID, targetID));
m_idToRemoteInspectorConnectionMap.get(connectionID)->sendMessage("Setup", g_variant_new("(t)", targetID));
}
void RemoteInspectorServer::close(SocketConnection& clientConnection, uint64_t connectionID, uint64_t targetID)
{
ASSERT(m_clientConnection == &clientConnection || m_automationConnection == &clientConnection);
ASSERT(m_idToRemoteInspectorConnectionMap.contains(connectionID));
if (&clientConnection == m_automationConnection) {
// FIXME: automation.
return;
}
ASSERT(m_inspectionTargets.contains(std::make_pair(connectionID, targetID)));
m_idToRemoteInspectorConnectionMap.get(connectionID)->sendMessage("FrontendDidClose", g_variant_new("(t)", targetID));
m_inspectionTargets.remove(std::make_pair(connectionID, targetID));
}
void RemoteInspectorServer::connectionDidClose(SocketConnection& clientConnection)
{
ASSERT(m_connections.contains(&clientConnection));
if (&clientConnection == m_automationConnection) {
for (auto connectionTargetPair : m_automationTargets)
close(clientConnection, connectionTargetPair.first, connectionTargetPair.second);
m_automationConnection = nullptr;
} else if (&clientConnection == m_clientConnection) {
for (auto connectionTargetPair : m_inspectionTargets)
close(clientConnection, connectionTargetPair.first, connectionTargetPair.second);
m_clientConnection = nullptr;
} else if (m_remoteInspectorConnectionToIDMap.contains(&clientConnection)) {
uint64_t connectionID = m_remoteInspectorConnectionToIDMap.take(&clientConnection);
m_idToRemoteInspectorConnectionMap.remove(connectionID);
// Send an empty target list to the clients.
Vector<SocketConnection*> clientConnections = { m_automationConnection, m_clientConnection };
for (auto* connection : clientConnections) {
if (!connection)
continue;
connection->sendMessage("SetTargetList", g_variant_new("(t@a(tsssb))", connectionID, g_variant_new_array(G_VARIANT_TYPE("(tsssb)"), nullptr, 0)));
}
}
m_connections.remove(&clientConnection);
}
void RemoteInspectorServer::sendMessageToBackend(SocketConnection& clientConnection, uint64_t connectionID, uint64_t targetID, const char* message)
{
ASSERT(m_clientConnection == &clientConnection || m_automationConnection == &clientConnection);
ASSERT(m_idToRemoteInspectorConnectionMap.contains(connectionID));
if (&clientConnection == m_automationConnection) {
RemoteInspector::singleton().sendMessageToTarget(targetID, message);
return;
}
m_idToRemoteInspectorConnectionMap.get(connectionID)->sendMessage("SendMessageToTarget", g_variant_new("(t&s)", targetID, message));
}
void RemoteInspectorServer::sendMessageToFrontend(SocketConnection& remoteInspectorConnection, uint64_t targetID, const char* message)
{
ASSERT(m_connections.contains(&remoteInspectorConnection));
ASSERT(m_remoteInspectorConnectionToIDMap.contains(&remoteInspectorConnection));
uint64_t connectionID = m_remoteInspectorConnectionToIDMap.get(&remoteInspectorConnection);
auto connectionTargetPair = std::make_pair(connectionID, targetID);
ASSERT(m_automationTargets.contains(connectionTargetPair) || m_inspectionTargets.contains(connectionTargetPair));
SocketConnection* clientConnection = m_inspectionTargets.contains(connectionTargetPair) ? m_clientConnection : m_automationConnection;
ASSERT(clientConnection);
clientConnection->sendMessage("SendMessageToFrontend", g_variant_new("(tt&s)", connectionID, targetID, message));
}
void RemoteInspectorServer::startAutomationSession(SocketConnection& automationConnection, const char* sessionID, const RemoteInspector::Client::SessionCapabilities& capabilities)
{
if (!m_automationConnection)
m_automationConnection = &automationConnection;
ASSERT(m_automationConnection == &automationConnection);
RemoteInspector::singleton().requestAutomationSession(sessionID, capabilities);
}
} // namespace Inspector
#endif // ENABLE(REMOTE_INSPECTOR)