| /* |
| * Copyright (C) 2019 Sony Interactive Entertainment Inc. |
| * |
| * 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 "RemoteInspectorServer.h" |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| |
| #include <wtf/JSONValues.h> |
| #include <wtf/MainThread.h> |
| |
| namespace Inspector { |
| |
| Optional<PlatformSocketType> RemoteInspectorServer::connect() |
| { |
| if (!m_server) { |
| LOG_ERROR("Inspector server is not running"); |
| return WTF::nullopt; |
| } |
| |
| if (auto sockets = Socket::createPair()) { |
| if (auto id = m_server->createClient(sockets->at(0))) { |
| LockHolder lock(m_connectionsLock); |
| m_inspectorConnections.append(id.value()); |
| |
| return sockets->at(1); |
| } |
| } |
| |
| return WTF::nullopt; |
| } |
| |
| Optional<uint16_t> RemoteInspectorServer::listenForTargets() |
| { |
| if (!m_server) { |
| LOG_ERROR("Inspector server is not running"); |
| return WTF::nullopt; |
| } |
| |
| if (m_inspectorListener) { |
| LOG_ERROR("Inspector server is already listening for targets."); |
| return WTF::nullopt; |
| } |
| |
| if (auto connection = m_server->listenInet("127.0.0.1", 0)) { |
| m_inspectorListener = connection; |
| return m_server->getPort(*connection); |
| } |
| |
| return WTF::nullopt; |
| } |
| |
| void RemoteInspectorServer::didAccept(ConnectionID acceptedID, ConnectionID listenerID, Socket::Domain type) |
| { |
| ASSERT(!isMainThread()); |
| |
| if (type == Socket::Domain::Local || (m_inspectorListener && listenerID == *m_inspectorListener)) { |
| LockHolder lock(m_connectionsLock); |
| m_inspectorConnections.append(acceptedID); |
| } else if (type == Socket::Domain::Network) { |
| if (m_clientConnection) { |
| LOG_ERROR("Inspector server can accept only 1 client"); |
| return; |
| } |
| m_clientConnection = acceptedID; |
| } |
| } |
| |
| void RemoteInspectorServer::didClose(ConnectionID id) |
| { |
| ASSERT(!isMainThread()); |
| |
| if (id == m_clientConnection) { |
| // Connection from the remote client closed. |
| callOnMainThread([this] { |
| clientConnectionClosed(); |
| }); |
| return; |
| } |
| |
| // Connection from WebProcess closed. |
| callOnMainThread([this, id] { |
| connectionClosed(id); |
| }); |
| } |
| |
| HashMap<String, RemoteInspectorConnectionClient::CallHandler>& RemoteInspectorServer::dispatchMap() |
| { |
| static NeverDestroyed<HashMap<String, CallHandler>> dispatchMap = HashMap<String, CallHandler>({ |
| { "SetTargetList"_s, reinterpret_cast<CallHandler>(&RemoteInspectorServer::setTargetList) }, |
| { "SetupInspectorClient"_s, reinterpret_cast<CallHandler>(&RemoteInspectorServer::setupInspectorClient) }, |
| { "Setup"_s, reinterpret_cast<CallHandler>(&RemoteInspectorServer::setup) }, |
| { "FrontendDidClose"_s, reinterpret_cast<CallHandler>(&RemoteInspectorServer::close) }, |
| { "SendMessageToFrontend"_s, reinterpret_cast<CallHandler>(&RemoteInspectorServer::sendMessageToFrontend) }, |
| { "SendMessageToBackend"_s, reinterpret_cast<CallHandler>(&RemoteInspectorServer::sendMessageToBackend) }, |
| }); |
| |
| return dispatchMap; |
| } |
| |
| void RemoteInspectorServer::sendWebInspectorEvent(ConnectionID id, const String& event) |
| { |
| const CString message = event.utf8(); |
| m_server->send(id, reinterpret_cast<const uint8_t*>(message.data()), message.length()); |
| } |
| |
| RemoteInspectorServer& RemoteInspectorServer::singleton() |
| { |
| static NeverDestroyed<RemoteInspectorServer> server; |
| return server; |
| } |
| |
| bool RemoteInspectorServer::start(uint16_t port) |
| { |
| m_server = RemoteInspectorSocketEndpoint::create(this, "RemoteInspectorServer"); |
| |
| if (!m_server->listenInet(nullptr, port)) { |
| m_server = nullptr; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void RemoteInspectorServer::setTargetList(const Event& event) |
| { |
| ASSERT(isMainThread()); |
| |
| if (!m_clientConnection || !event.message) |
| return; |
| |
| auto targetListEvent = JSON::Object::create(); |
| targetListEvent->setString("event"_s, "SetTargetList"_s); |
| targetListEvent->setInteger("connectionID"_s, event.clientID); |
| targetListEvent->setString("message"_s, event.message.value()); |
| sendWebInspectorEvent(m_clientConnection.value(), targetListEvent->toJSONString()); |
| } |
| |
| void RemoteInspectorServer::setupInspectorClient(const Event&) |
| { |
| ASSERT(isMainThread()); |
| |
| auto setupEvent = JSON::Object::create(); |
| setupEvent->setString("event"_s, "GetTargetList"_s); |
| |
| LockHolder lock(m_connectionsLock); |
| for (auto connection : m_inspectorConnections) |
| sendWebInspectorEvent(connection, setupEvent->toJSONString()); |
| } |
| |
| void RemoteInspectorServer::setup(const Event& event) |
| { |
| ASSERT(isMainThread()); |
| |
| if (!event.targetID || !event.connectionID) |
| return; |
| |
| m_inspectionTargets.add(std::make_pair(event.connectionID.value(), event.targetID.value())); |
| |
| auto setupEvent = JSON::Object::create(); |
| setupEvent->setString("event"_s, "Setup"_s); |
| setupEvent->setInteger("targetID"_s, event.targetID.value()); |
| sendWebInspectorEvent(event.connectionID.value(), setupEvent->toJSONString()); |
| } |
| |
| void RemoteInspectorServer::sendCloseEvent(ConnectionID connectionID, TargetID targetID) |
| { |
| ASSERT(isMainThread()); |
| |
| auto closeEvent = JSON::Object::create(); |
| closeEvent->setString("event"_s, "FrontendDidClose"_s); |
| closeEvent->setInteger("targetID"_s, targetID); |
| sendWebInspectorEvent(connectionID, closeEvent->toJSONString()); |
| } |
| |
| void RemoteInspectorServer::close(const Event& event) |
| { |
| ASSERT(isMainThread()); |
| |
| sendCloseEvent(event.connectionID.value(), event.targetID.value()); |
| m_inspectionTargets.remove(std::make_pair(event.connectionID.value(), event.targetID.value())); |
| } |
| |
| void RemoteInspectorServer::clientConnectionClosed() |
| { |
| ASSERT(isMainThread()); |
| |
| for (auto connectionTargetPair : m_inspectionTargets) |
| sendCloseEvent(connectionTargetPair.first, connectionTargetPair.second); |
| |
| m_inspectionTargets.clear(); |
| m_clientConnection = WTF::nullopt; |
| } |
| |
| void RemoteInspectorServer::connectionClosed(ConnectionID clientID) |
| { |
| ASSERT(isMainThread()); |
| |
| LockHolder lock(m_connectionsLock); |
| if (m_inspectorConnections.removeFirst(clientID) && m_clientConnection) { |
| auto closedEvent = JSON::Object::create(); |
| closedEvent->setString("event"_s, "SetTargetList"_s); |
| closedEvent->setInteger("connectionID"_s, clientID); |
| auto targetList = JSON::Array::create(); |
| closedEvent->setString("message"_s, targetList->toJSONString()); |
| sendWebInspectorEvent(m_clientConnection.value(), closedEvent->toJSONString()); |
| } |
| } |
| |
| void RemoteInspectorServer::sendMessageToBackend(const Event& event) |
| { |
| ASSERT(isMainThread()); |
| |
| if (!event.connectionID || !event.targetID || !event.message) |
| return; |
| |
| auto sendEvent = JSON::Object::create(); |
| sendEvent->setString("event"_s, "SendMessageToTarget"_s); |
| sendEvent->setInteger("targetID"_s, event.targetID.value()); |
| sendEvent->setString("message"_s, event.message.value()); |
| sendWebInspectorEvent(event.connectionID.value(), sendEvent->toJSONString()); |
| } |
| |
| void RemoteInspectorServer::sendMessageToFrontend(const Event& event) |
| { |
| if (!m_clientConnection) |
| return; |
| |
| ASSERT(isMainThread()); |
| |
| if (!event.targetID || !event.message) |
| return; |
| |
| auto sendEvent = JSON::Object::create(); |
| sendEvent->setString("event"_s, "SendMessageToFrontend"_s); |
| sendEvent->setInteger("targetID"_s, event.targetID.value()); |
| sendEvent->setInteger("connectionID"_s, event.clientID); |
| sendEvent->setString("message"_s, event.message.value()); |
| sendWebInspectorEvent(m_clientConnection.value(), sendEvent->toJSONString()); |
| } |
| |
| } // namespace Inspector |
| |
| #endif // ENABLE(REMOTE_INSPECTOR) |