1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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:
// For sessions attached to the Tab target, the mode is set to TabTarget.
// For other sessions, the mode is inherited from the parent.
// This is used as an indication that the client has opted in into using
// tab targets and lets backend use the new logic of target discovery:
// - auto-attach on frame targets will not auto-attach subframes (these
// will be instead attached to the Tab targets)
// - certain features that depend on MPArch support will not be disabled
// (BFCache, Prerender)
enum class Mode {
kSupportsTabTarget,
kDoesNotSupportTabTarget,
};
class ChildObserver : public base::CheckedObserver {
public:
virtual void SessionAttached(DevToolsSession& session) = 0;
};
// For root sessions (see also private constructor for children).
DevToolsSession(DevToolsAgentHostClient* client, Mode mode);
~DevToolsSession() override;
void SetAgentHost(DevToolsAgentHostImpl* agent_host);
void SetRuntimeResumeCallback(base::OnceClosure runtime_resume);
bool IsWaitingForDebuggerOnStart() const;
void Dispose();
// content::DevToolsAgentHostClientChannel implementation.
content::DevToolsAgentHost* GetAgentHost() override;
content::DevToolsAgentHostClient* GetClient() override;
DevToolsSession* GetRootSession();
// Browser-only sessions do not talk to mojom::DevToolsAgent, but instead
// handle all protocol messages locally in the browser process.
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();
};
// For child sessions.
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);
// protocol::FrontendChannel implementation.
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;
// content::DevToolsAgentHostClientChannel implementation.
void DispatchProtocolMessageToClient(std::vector<uint8_t> message) override;
// blink::mojom::DevToolsSessionHost implementation.
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;
// DevToolsExternalAgentProxy implementation.
void DispatchOnClientHost(base::span<const uint8_t> message) override;
void ConnectionClosed() override;
// Merges the |updates| received from the renderer into session_state_cookie_.
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_; // empty if this is the root session.
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;
// Messages that were sent to the agent or queued after suspending.
std::list<PendingMessage> pending_messages_;
// Pending messages that were sent and are thus waiting for a response.
base::flat_map<int, std::list<PendingMessage>::iterator>
waiting_for_response_;
// |session_state_cookie_| always corresponds to a state before
// any of the waiting for response messages have been handled.
// |session_state_cookie_| is nullptr before first attach.
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};
};
} // namespace content
#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SESSION_H_
|