#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SESSION_H_
#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SESSION_H_
#include <list>
#include <map>
#include <memory>
#include <string>
#include <type_traits>
#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "content/browser/devtools/protocol/protocol.h"
#include "content/public/browser/devtools_agent_host_client_channel.h"
#include "content/public/browser/devtools_external_agent_proxy.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
namespace content {
class DevToolsAgentHostClient;
class DevToolsAgentHostImpl;
class DevToolsExternalAgentProxyDelegate;
namespace protocol {
class DevToolsDomainHandler;
class AuditsHandler;
class DOMHandler;
class DeviceOrientationHandler;
class EmulationHandler;
class InputHandler;
class InspectorHandler;
class IOHandler;
class OverlayHandler;
class NetworkHandler;
class FetchHandler;
class StorageHandler;
class TargetHandler;
class PageHandler;
class TracingHandler;
class LogHandler;
class WebAuthnHandler;
}
class DevToolsSession : public protocol::FrontendChannel,
public blink::mojom::DevToolsSessionHost,
public DevToolsExternalAgentProxy,
public content::DevToolsAgentHostClientChannel {
public:
enum class Mode {
kSupportsTabTarget,
kDoesNotSupportTabTarget,
};
class ChildObserver : public base::CheckedObserver {
public:
virtual void SessionAttached(DevToolsSession& session) = 0;
};
DevToolsSession(DevToolsAgentHostClient* client, Mode mode);
~DevToolsSession() override;
void SetAgentHost(DevToolsAgentHostImpl* agent_host);
void SetRuntimeResumeCallback(base::OnceClosure runtime_resume);
bool IsWaitingForDebuggerOnStart() const;
void Dispose();
content::DevToolsAgentHost* GetAgentHost() override;
content::DevToolsAgentHostClient* GetClient() override;
DevToolsSession* GetRootSession();
void SetBrowserOnly(bool browser_only);
template <typename Handler, typename... Args>
Handler* CreateAndAddHandler(Args&&... args) {
if (!IsDomainAvailableToUntrustedClient<Handler>() &&
!client_->IsTrusted()) {
return nullptr;
}
auto handler = std::make_unique<Handler>(std::forward<Args>(args)...);
Handler* handler_ptr = handler.get();
AddHandler(std::move(handler));
return handler_ptr;
}
void TurnIntoExternalProxy(DevToolsExternalAgentProxyDelegate* delegate);
void AttachToAgent(blink::mojom::DevToolsAgent* agent,
bool force_using_io_session);
void DispatchProtocolMessage(base::span<const uint8_t> message);
void SuspendSendingMessagesToAgent();
void ResumeSendingMessagesToAgent();
void ClearPendingMessages(bool did_crash);
using HandlersMap =
base::flat_map<std::string,
std::unique_ptr<protocol::DevToolsDomainHandler>>;
HandlersMap& handlers() { return handlers_; }
DevToolsSession* AttachChildSession(const std::string& session_id,
DevToolsAgentHostImpl* agent_host,
DevToolsAgentHostClient* client,
Mode mode,
base::OnceClosure resume_callback);
void DetachChildSession(const std::string& session_id);
bool HasChildSession(const std::string& session_id);
Mode session_mode() const { return mode_; }
void AddObserver(ChildObserver* obs);
void RemoveObserver(ChildObserver* obs);
base::RepeatingCallback<void(std::string)> MakePrepareForReloadCallback() {
return base::BindRepeating(&DevToolsSession::PrepareForReload,
base::Unretained(this));
}
private:
struct PendingMessage {
int call_id;
std::string method;
std::vector<uint8_t> payload;
PendingMessage() = delete;
PendingMessage(const PendingMessage&) = delete;
PendingMessage& operator=(const PendingMessage&) = delete;
PendingMessage(PendingMessage&&);
PendingMessage(int call_id,
crdtp::span<uint8_t> method,
crdtp::span<uint8_t> payload);
~PendingMessage();
};
DevToolsSession(DevToolsAgentHostClient* client,
const std::string& session_id,
DevToolsSession* parent,
Mode mode);
void MojoConnectionDestroyed();
void DispatchToAgent(const PendingMessage& message);
void HandleCommand(base::span<const uint8_t> message);
void HandleCommandInternal(crdtp::Dispatchable dispatchable,
base::span<const uint8_t> message);
void DispatchProtocolMessageInternal(crdtp::Dispatchable dispatchable,
base::span<const uint8_t> message);
void SendProtocolResponse(
int call_id,
std::unique_ptr<protocol::Serializable> message) override;
void SendProtocolNotification(
std::unique_ptr<protocol::Serializable> message) override;
void FlushProtocolNotifications() override;
void FallThrough(int call_id,
crdtp::span<uint8_t> method,
crdtp::span<uint8_t> message) override;
void DispatchProtocolMessageToClient(std::vector<uint8_t> message) override;
void DispatchProtocolResponse(
blink::mojom::DevToolsMessagePtr message,
int call_id,
blink::mojom::DevToolsSessionStatePtr updates) override;
void DispatchProtocolNotification(
blink::mojom::DevToolsMessagePtr message,
blink::mojom::DevToolsSessionStatePtr updates) override;
void DispatchOnClientHost(base::span<const uint8_t> message) override;
void ConnectionClosed() override;
void ApplySessionStateUpdates(blink::mojom::DevToolsSessionStatePtr updates);
template <typename T>
bool IsDomainAvailableToUntrustedClient() {
return std::disjunction_v<
std::is_same<T, protocol::AuditsHandler>,
std::is_same<T, protocol::DOMHandler>,
std::is_same<T, protocol::DeviceOrientationHandler>,
std::is_same<T, protocol::EmulationHandler>,
std::is_same<T, protocol::InputHandler>,
std::is_same<T, protocol::InspectorHandler>,
std::is_same<T, protocol::IOHandler>,
std::is_same<T, protocol::OverlayHandler>,
std::is_same<T, protocol::NetworkHandler>,
std::is_same<T, protocol::FetchHandler>,
std::is_same<T, protocol::StorageHandler>,
std::is_same<T, protocol::TargetHandler>,
std::is_same<T, protocol::PageHandler>,
std::is_same<T, protocol::TracingHandler>,
std::is_same<T, protocol::LogHandler>,
std::is_same<T, protocol::WebAuthnHandler>>;
}
void AddHandler(std::unique_ptr<protocol::DevToolsDomainHandler> handler);
void PrepareForReload(std::string script_to_evaluate_on_load);
const raw_ptr<DevToolsAgentHostClient> client_;
const raw_ptr<DevToolsSession> root_session_ = nullptr;
const std::string session_id_;
const Mode mode_;
mojo::AssociatedReceiver<blink::mojom::DevToolsSessionHost> receiver_{this};
mojo::AssociatedRemote<blink::mojom::DevToolsSession> session_;
mojo::Remote<blink::mojom::DevToolsSession> io_session_;
bool use_io_session_{false};
raw_ptr<DevToolsAgentHostImpl> agent_host_ = nullptr;
bool browser_only_ = false;
HandlersMap handlers_;
std::unique_ptr<protocol::UberDispatcher> dispatcher_{
new protocol::UberDispatcher(this)};
bool suspended_sending_messages_to_agent_ = false;
std::list<PendingMessage> pending_messages_;
base::flat_map<int, std::list<PendingMessage>::iterator>
waiting_for_response_;
blink::mojom::DevToolsSessionStatePtr session_state_cookie_;
std::string script_to_evaluate_on_load_;
base::flat_map<std::string, raw_ptr<DevToolsSession, CtnExperimental>>
child_sessions_;
base::OnceClosure runtime_resume_;
raw_ptr<DevToolsExternalAgentProxyDelegate> proxy_delegate_ = nullptr;
base::ObserverList<ChildObserver, true, false> child_observers_;
base::WeakPtrFactory<DevToolsSession> weak_factory_{this};
};
}
#endif