Commit 237f8276 authored by Dmitry Gozman's avatar Dmitry Gozman Committed by Commit Bot

[DevTools] Support multiple sessions in Target domain

BUG=590878

Change-Id: Idcebf0336623322813b4cdfac66183d37c28f2bd
Reviewed-on: https://chromium-review.googlesource.com/573411
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#488395}
parent 17173492
......@@ -186,6 +186,10 @@ void DevToolsAgentHostImpl::ForceAttachClient(DevToolsAgentHostClient* client) {
InnerAttachClient(client);
}
void DevToolsAgentHostImpl::AttachMultiClient(DevToolsAgentHostClient* client) {
InnerAttachClient(client);
}
bool DevToolsAgentHostImpl::DetachClient(DevToolsAgentHostClient* client) {
if (!SessionByClient(client))
return false;
......
......@@ -44,6 +44,7 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost {
void DisconnectWebContents() override;
void ConnectWebContents(WebContents* wc) override;
void AttachMultiClient(DevToolsAgentHostClient* client);
bool Inspect();
protected:
......
......@@ -1657,6 +1657,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, TargetDiscovery) {
EXPECT_TRUE(params->GetBoolean("targetInfo.attached", &is_attached));
EXPECT_TRUE(is_attached);
params = WaitForNotification("Target.attachedToTarget", true);
std::string session_id;
EXPECT_TRUE(params->GetString("sessionId", &session_id));
EXPECT_TRUE(params->GetString("targetInfo.targetId", &temp));
EXPECT_EQ(attached_id, temp);
EXPECT_TRUE(notifications_.empty());
......@@ -1667,9 +1669,11 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, TargetDiscovery) {
EXPECT_TRUE(notifications_.empty());
command_params.reset(new base::DictionaryValue());
command_params->SetString("targetId", attached_id);
command_params->SetString("sessionId", session_id);
SendCommand("Target.detachFromTarget", std::move(command_params), true);
params = WaitForNotification("Target.detachedFromTarget", true);
EXPECT_TRUE(params->GetString("sessionId", &temp));
EXPECT_EQ(session_id, temp);
EXPECT_TRUE(params->GetString("targetId", &temp));
EXPECT_EQ(attached_id, temp);
EXPECT_TRUE(notifications_.empty());
......@@ -1737,6 +1741,8 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsProtocolTest, TargetNoDiscovery) {
command_params->SetBoolean("value", true);
SendCommand("Target.setAttachToFrames", std::move(command_params), false);
params = WaitForNotification("Target.attachedToTarget", true);
std::string session_id;
EXPECT_TRUE(params->GetString("sessionId", &session_id));
EXPECT_TRUE(params->GetString("targetInfo.targetId", &target_id));
EXPECT_TRUE(params->GetString("targetInfo.type", &temp));
EXPECT_EQ("iframe", temp);
......@@ -1748,10 +1754,13 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsProtocolTest, TargetNoDiscovery) {
params = WaitForNotification("Target.detachedFromTarget", true);
EXPECT_TRUE(params->GetString("targetId", &temp));
EXPECT_EQ(target_id, temp);
EXPECT_TRUE(params->GetString("sessionId", &temp));
EXPECT_EQ(session_id, temp);
// Navigate back to cross-site iframe.
NavigateFrameToURL(root->child_at(0), cross_site_url);
params = WaitForNotification("Target.attachedToTarget", true);
EXPECT_TRUE(params->GetString("sessionId", &session_id));
EXPECT_TRUE(params->GetString("targetInfo.targetId", &target_id));
EXPECT_TRUE(params->GetString("targetInfo.type", &temp));
EXPECT_EQ("iframe", temp);
......@@ -1764,6 +1773,8 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsProtocolTest, TargetNoDiscovery) {
params = WaitForNotification("Target.detachedFromTarget", true);
EXPECT_TRUE(params->GetString("targetId", &temp));
EXPECT_EQ(target_id, temp);
EXPECT_TRUE(params->GetString("sessionId", &temp));
EXPECT_EQ(session_id, temp);
}
IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, SetAndGetCookies) {
......
......@@ -176,7 +176,7 @@ void TargetAutoAttacher::ReattachTargetsOfType(const Hosts& new_hosts,
for (auto& it : new_hosts) {
DevToolsAgentHost* host = it.get();
if (old_hosts.find(host) == old_hosts.end()) {
if (attach_callback_.Run(host, waiting_for_debugger))
attach_callback_.Run(host, waiting_for_debugger);
auto_attached_hosts_.insert(host);
}
}
......
......@@ -18,7 +18,7 @@ namespace protocol {
class TargetAutoAttacher : public ServiceWorkerDevToolsManager::Observer {
public:
// Second parameter is |waiting_for_debugger|, returns whether it succeeded.
using AttachCallback = base::Callback<bool(DevToolsAgentHost*, bool)>;
using AttachCallback = base::Callback<void(DevToolsAgentHost*, bool)>;
using DetachCallback = base::Callback<void(DevToolsAgentHost*)>;
TargetAutoAttacher(AttachCallback attach_callback,
......
......@@ -4,8 +4,10 @@
#include "content/browser/devtools/protocol/target_handler.h"
#include "base/strings/stringprintf.h"
#include "content/browser/devtools/devtools_manager.h"
#include "content/browser/devtools/devtools_session.h"
#include "content/public/browser/devtools_agent_host_client.h"
namespace content {
namespace protocol {
......@@ -24,12 +26,77 @@ std::unique_ptr<Target::TargetInfo> CreateInfo(DevToolsAgentHost* host) {
} // namespace
class TargetHandler::Session : public DevToolsAgentHostClient {
public:
static std::string Attach(TargetHandler* handler,
DevToolsAgentHost* agent_host,
bool waiting_for_debugger) {
std::string id = base::StringPrintf("%s:%d", agent_host->GetId().c_str(),
++handler->last_session_id_);
Session* session = new Session(handler, agent_host, id);
handler->attached_sessions_[id].reset(session);
static_cast<DevToolsAgentHostImpl*>(agent_host)->AttachMultiClient(session);
handler->frontend_->AttachedToTarget(id, CreateInfo(agent_host),
waiting_for_debugger);
return id;
}
~Session() override {
if (agent_host_)
agent_host_->DetachClient(this);
}
void Detach(bool host_closed) {
handler_->frontend_->DetachedFromTarget(id_, agent_host_->GetId());
if (host_closed)
handler_->auto_attacher_.AgentHostClosed(agent_host_.get());
else
agent_host_->DetachClient(this);
handler_->auto_attached_sessions_.erase(agent_host_.get());
agent_host_ = nullptr;
handler_->attached_sessions_.erase(id_);
}
void SendMessageToAgentHost(const std::string& message) {
agent_host_->DispatchProtocolMessage(this, message);
}
bool IsAttachedTo(const std::string& target_id) {
return agent_host_->GetId() == target_id;
}
private:
Session(TargetHandler* handler,
DevToolsAgentHost* agent_host,
const std::string& id)
: handler_(handler), agent_host_(agent_host), id_(id) {}
// DevToolsAgentHostClient implementation.
void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) override {
DCHECK(agent_host == agent_host_.get());
handler_->frontend_->ReceivedMessageFromTarget(id_, message,
agent_host_->GetId());
}
void AgentHostClosed(DevToolsAgentHost* agent_host,
bool replaced_with_another_client) override {
DCHECK(agent_host == agent_host_.get());
Detach(true);
}
TargetHandler* handler_;
scoped_refptr<DevToolsAgentHost> agent_host_;
std::string id_;
DISALLOW_COPY_AND_ASSIGN(Session);
};
TargetHandler::TargetHandler()
: DevToolsDomainHandler(Target::Metainfo::domainName),
auto_attacher_(base::Bind(&TargetHandler::AttachToTargetInternal,
base::Unretained(this)),
base::Bind(&TargetHandler::DetachFromTargetInternal,
base::Unretained(this))),
auto_attacher_(
base::Bind(&TargetHandler::AutoAttach, base::Unretained(this)),
base::Bind(&TargetHandler::AutoDetach, base::Unretained(this))),
discover_(false) {}
TargetHandler::~TargetHandler() {
......@@ -54,9 +121,8 @@ void TargetHandler::SetRenderFrameHost(RenderFrameHostImpl* render_frame_host) {
Response TargetHandler::Disable() {
SetAutoAttach(false, false);
SetDiscoverTargets(false);
for (const auto& id_host : attached_hosts_)
id_host.second->DetachClient(this);
attached_hosts_.clear();
auto_attached_sessions_.clear();
attached_sessions_.clear();
return Response::OK();
}
......@@ -68,46 +134,52 @@ void TargetHandler::RenderFrameHostChanged() {
auto_attacher_.UpdateFrames();
}
void TargetHandler::TargetCreatedInternal(DevToolsAgentHost* host) {
if (reported_hosts_.find(host->GetId()) != reported_hosts_.end())
return;
frontend_->TargetCreated(CreateInfo(host));
reported_hosts_[host->GetId()] = host;
}
void TargetHandler::TargetInfoChangedInternal(DevToolsAgentHost* host) {
if (reported_hosts_.find(host->GetId()) == reported_hosts_.end())
return;
frontend_->TargetInfoChanged(CreateInfo(host));
void TargetHandler::AutoAttach(DevToolsAgentHost* host,
bool waiting_for_debugger) {
std::string session_id = Session::Attach(this, host, waiting_for_debugger);
auto_attached_sessions_[host] = attached_sessions_[session_id].get();
}
void TargetHandler::TargetDestroyedInternal(DevToolsAgentHost* host) {
auto it = reported_hosts_.find(host->GetId());
if (it == reported_hosts_.end())
void TargetHandler::AutoDetach(DevToolsAgentHost* host) {
auto it = auto_attached_sessions_.find(host);
if (it == auto_attached_sessions_.end())
return;
if (discover_)
frontend_->TargetDestroyed(host->GetId());
reported_hosts_.erase(it);
}
bool TargetHandler::AttachToTargetInternal(
DevToolsAgentHost* host, bool waiting_for_debugger) {
attached_hosts_[host->GetId()] = host;
if (!host->AttachClient(this)) {
attached_hosts_.erase(host->GetId());
return false;
it->second->Detach(false);
}
Response TargetHandler::FindSession(Maybe<std::string> session_id,
Maybe<std::string> target_id,
Session** session,
bool fall_through) {
*session = nullptr;
if (session_id.isJust()) {
auto it = attached_sessions_.find(session_id.fromJust());
if (it == attached_sessions_.end()) {
if (fall_through)
return Response::FallThrough();
return Response::InvalidParams("No session with given id");
}
frontend_->AttachedToTarget(CreateInfo(host), waiting_for_debugger);
return true;
}
void TargetHandler::DetachFromTargetInternal(DevToolsAgentHost* host) {
auto it = attached_hosts_.find(host->GetId());
if (it == attached_hosts_.end())
return;
host->DetachClient(this);
frontend_->DetachedFromTarget(host->GetId());
attached_hosts_.erase(it);
*session = it->second.get();
return Response::OK();
}
if (target_id.isJust()) {
for (auto& it : attached_sessions_) {
if (it.second->IsAttachedTo(target_id.fromJust())) {
if (*session)
return Response::Error("Multiple sessions attached, specify id.");
*session = it.second.get();
}
}
if (!*session) {
if (fall_through)
return Response::FallThrough();
return Response::InvalidParams("No session for given target id");
}
return Response::OK();
}
if (fall_through)
return Response::FallThrough();
return Response::InvalidParams("Session id must be specified");
}
// ----------------- Protocol ----------------------
......@@ -120,9 +192,7 @@ Response TargetHandler::SetDiscoverTargets(bool discover) {
DevToolsAgentHost::AddObserver(this);
} else {
DevToolsAgentHost::RemoveObserver(this);
RawHostsMap copy = reported_hosts_;
for (const auto& id_host : copy)
TargetDestroyedInternal(id_host.second);
reported_hosts_.clear();
}
return Response::OK();
}
......@@ -144,32 +214,36 @@ Response TargetHandler::SetRemoteLocations(
}
Response TargetHandler::AttachToTarget(const std::string& target_id,
bool* out_success) {
std::string* out_session_id) {
// TODO(dgozman): only allow reported hosts.
scoped_refptr<DevToolsAgentHost> agent_host =
DevToolsAgentHost::GetForId(target_id);
if (!agent_host)
return Response::InvalidParams("No target with given id found");
*out_success = AttachToTargetInternal(agent_host.get(), false);
*out_session_id = Session::Attach(this, agent_host.get(), false);
return Response::OK();
}
Response TargetHandler::DetachFromTarget(const std::string& target_id) {
auto it = attached_hosts_.find(target_id);
if (it == attached_hosts_.end())
return Response::Error("Not attached to the target");
DevToolsAgentHost* agent_host = it->second.get();
DetachFromTargetInternal(agent_host);
Response TargetHandler::DetachFromTarget(Maybe<std::string> session_id,
Maybe<std::string> target_id) {
Session* session = nullptr;
Response response =
FindSession(std::move(session_id), std::move(target_id), &session, false);
if (!response.isSuccess())
return response;
session->Detach(false);
return Response::OK();
}
Response TargetHandler::SendMessageToTarget(
const std::string& target_id,
const std::string& message) {
auto it = attached_hosts_.find(target_id);
if (it == attached_hosts_.end())
return Response::FallThrough();
it->second->DispatchProtocolMessage(this, message);
Response TargetHandler::SendMessageToTarget(const std::string& message,
Maybe<std::string> session_id,
Maybe<std::string> target_id) {
Session* session = nullptr;
Response response =
FindSession(std::move(session_id), std::move(target_id), &session, true);
if (!response.isSuccess())
return response;
session->SendMessageToAgentHost(message);
return Response::OK();
}
......@@ -239,49 +313,38 @@ Response TargetHandler::GetTargets(
return Response::OK();
}
// ---------------- DevToolsAgentHostClient ----------------
void TargetHandler::DispatchProtocolMessage(
DevToolsAgentHost* host,
const std::string& message) {
auto it = attached_hosts_.find(host->GetId());
if (it == attached_hosts_.end())
return; // Already disconnected.
frontend_->ReceivedMessageFromTarget(host->GetId(), message);
}
void TargetHandler::AgentHostClosed(
DevToolsAgentHost* host,
bool replaced_with_another_client) {
frontend_->DetachedFromTarget(host->GetId());
attached_hosts_.erase(host->GetId());
auto_attacher_.AgentHostClosed(host);
}
// -------------- DevToolsAgentHostObserver -----------------
bool TargetHandler::ShouldForceDevToolsAgentHostCreation() {
return true;
}
void TargetHandler::DevToolsAgentHostCreated(DevToolsAgentHost* agent_host) {
void TargetHandler::DevToolsAgentHostCreated(DevToolsAgentHost* host) {
// If we start discovering late, all existing agent hosts will be reported,
// but we could have already attached to some.
TargetCreatedInternal(agent_host);
if (reported_hosts_.find(host) != reported_hosts_.end())
return;
frontend_->TargetCreated(CreateInfo(host));
reported_hosts_.insert(host);
}
void TargetHandler::DevToolsAgentHostDestroyed(DevToolsAgentHost* agent_host) {
DCHECK(attached_hosts_.find(agent_host->GetId()) == attached_hosts_.end());
TargetDestroyedInternal(agent_host);
void TargetHandler::DevToolsAgentHostDestroyed(DevToolsAgentHost* host) {
if (reported_hosts_.find(host) == reported_hosts_.end())
return;
frontend_->TargetDestroyed(host->GetId());
reported_hosts_.erase(host);
}
void TargetHandler::DevToolsAgentHostAttached(DevToolsAgentHost* host) {
TargetInfoChangedInternal(host);
if (reported_hosts_.find(host) == reported_hosts_.end())
return;
frontend_->TargetInfoChanged(CreateInfo(host));
}
void TargetHandler::DevToolsAgentHostDetached(DevToolsAgentHost* host) {
TargetInfoChangedInternal(host);
if (reported_hosts_.find(host) == reported_hosts_.end())
return;
frontend_->TargetInfoChanged(CreateInfo(host));
}
} // namespace protocol
......
......@@ -11,7 +11,6 @@
#include "content/browser/devtools/protocol/devtools_domain_handler.h"
#include "content/browser/devtools/protocol/target.h"
#include "content/browser/devtools/protocol/target_auto_attacher.h"
#include "content/public/browser/devtools_agent_host_client.h"
#include "content/public/browser/devtools_agent_host_observer.h"
namespace content {
......@@ -23,7 +22,6 @@ namespace protocol {
class TargetHandler : public DevToolsDomainHandler,
public Target::Backend,
public DevToolsAgentHostClient,
public DevToolsAgentHostObserver {
public:
TargetHandler();
......@@ -46,10 +44,12 @@ class TargetHandler : public DevToolsDomainHandler,
Response SetRemoteLocations(
std::unique_ptr<protocol::Array<Target::RemoteLocation>>) override;
Response AttachToTarget(const std::string& target_id,
bool* out_success) override;
Response DetachFromTarget(const std::string& target_id) override;
Response SendMessageToTarget(const std::string& target_id,
const std::string& message) override;
std::string* out_session_id) override;
Response DetachFromTarget(Maybe<std::string> session_id,
Maybe<std::string> target_id) override;
Response SendMessageToTarget(const std::string& message,
Maybe<std::string> session_id,
Maybe<std::string> target_id) override;
Response GetTargetInfo(
const std::string& target_id,
std::unique_ptr<Target::TargetInfo>* target_info) override;
......@@ -69,14 +69,14 @@ class TargetHandler : public DevToolsDomainHandler,
override;
private:
using RawHostsMap = std::map<std::string, DevToolsAgentHost*>;
class Session;
void TargetCreatedInternal(DevToolsAgentHost* host);
void TargetInfoChangedInternal(DevToolsAgentHost* host);
void TargetDestroyedInternal(DevToolsAgentHost* host);
bool AttachToTargetInternal(DevToolsAgentHost* host,
bool waiting_for_debugger);
void DetachFromTargetInternal(DevToolsAgentHost* host);
void AutoAttach(DevToolsAgentHost* host, bool waiting_for_debugger);
void AutoDetach(DevToolsAgentHost* host);
Response FindSession(Maybe<std::string> session_id,
Maybe<std::string> target_id,
Session** session,
bool fall_through);
// DevToolsAgentHostObserver implementation.
bool ShouldForceDevToolsAgentHostCreation() override;
......@@ -85,17 +85,13 @@ class TargetHandler : public DevToolsDomainHandler,
void DevToolsAgentHostAttached(DevToolsAgentHost* agent_host) override;
void DevToolsAgentHostDetached(DevToolsAgentHost* agent_host) override;
// DevToolsAgentHostClient implementation.
void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) override;
void AgentHostClosed(DevToolsAgentHost* agent_host,
bool replaced_with_another_client) override;
std::unique_ptr<Target::Frontend> frontend_;
TargetAutoAttacher auto_attacher_;
bool discover_;
std::map<std::string, scoped_refptr<DevToolsAgentHost>> attached_hosts_;
RawHostsMap reported_hosts_;
std::map<std::string, std::unique_ptr<Session>> attached_sessions_;
std::map<DevToolsAgentHost*, Session*> auto_attached_sessions_;
std::set<DevToolsAgentHost*> reported_hosts_;
int last_session_id_ = 0;
DISALLOW_COPY_AND_ASSIGN(TargetHandler);
};
......
......@@ -184,9 +184,9 @@ TestRunner.Page = class {
}
async createSession() {
await DevToolsAPI._sendCommandOrDie('Target.attachToTarget', {targetId: this._targetId});
var session = new TestRunner.Session(this);
DevToolsAPI._sessions.set(this._targetId, session);
var sessionId = (await DevToolsAPI._sendCommandOrDie('Target.attachToTarget', {targetId: this._targetId})).sessionId;
var session = new TestRunner.Session(this, sessionId);
DevToolsAPI._sessions.set(sessionId, session);
return session;
}
......@@ -195,18 +195,12 @@ TestRunner.Page = class {
}
async _navigate(url) {
if (DevToolsAPI._sessions.get(this._targetId))
this._testRunner.die(`Cannot navigate to ${url} with active session`, new Error());
var session = await this.createSession();
await session._navigate(url);
await session.disconnect();
}
async loadHTML(html) {
if (DevToolsAPI._sessions.get(this._targetId))
this._testRunner.die('Cannot loadHTML with active session', new Error());
html = html.replace(/'/g, "\\'").replace(/\n/g, '\\n');
var session = await this.createSession();
await session.protocol.Runtime.evaluate({expression: `document.write('${html}');document.close();`});
......@@ -215,9 +209,10 @@ TestRunner.Page = class {
};
TestRunner.Session = class {
constructor(page) {
constructor(page, sessionId) {
this._testRunner = page._testRunner;
this._page = page;
this._sessionId = sessionId;
this._requestId = 0;
this._dispatchTable = new Map();
this._eventHandlers = new Map();
......@@ -225,12 +220,12 @@ TestRunner.Session = class {
}
async disconnect() {
await DevToolsAPI._sendCommandOrDie('Target.detachFromTarget', {targetId: this._page._targetId});
await DevToolsAPI._sendCommandOrDie('Target.detachFromTarget', {sessionId: this._sessionId});
DevToolsAPI._sessions.delete(this._page._targetId);
}
sendRawCommand(requestId, message) {
DevToolsAPI._sendCommandOrDie('Target.sendMessageToTarget', {targetId: this._page._targetId, message: message});
DevToolsAPI._sendCommandOrDie('Target.sendMessageToTarget', {sessionId: this._sessionId, message: message});
return new Promise(f => this._dispatchTable.set(requestId, f));
}
......@@ -394,9 +389,9 @@ DevToolsAPI.dispatchMessage = function(messageOrObject) {
} else {
var eventName = messageObject.method;
if (eventName === 'Target.receivedMessageFromTarget') {
var targetId = messageObject.params.targetId;
var sessionId = messageObject.params.sessionId;
var message = messageObject.params.message;
var session = DevToolsAPI._sessions.get(targetId);
var session = DevToolsAPI._sessions.get(sessionId);
if (session)
session._dispatchMessage(JSON.parse(message));
}
......
......@@ -4,18 +4,18 @@ Debugger-related command should be issued: {
"id": "<id>",
"method": "Target.sendMessageToTarget",
"params": {
"targetId": "<id>",
"message": {
"id": "<id>",
"method": "Target.sendMessageToTarget",
"params": {
"targetId": "<id>",
"message": {
"id": "<id>",
"method": "Debugger.enable"
},
"sessionId": "<id>"
}
}
}
},
"sessionId": "<id>"
}
}
Suspending targets.
......@@ -24,18 +24,18 @@ Debugger-related command should be issued: {
"id": "<id>",
"method": "Target.sendMessageToTarget",
"params": {
"targetId": "<id>",
"message": {
"id": "<id>",
"method": "Target.sendMessageToTarget",
"params": {
"targetId": "<id>",
"message": {
"id": "<id>",
"method": "Debugger.enable"
},
"sessionId": "<id>"
}
}
}
},
"sessionId": "<id>"
}
}
......@@ -38,18 +38,16 @@
namespace blink {
using protocol::Maybe;
using protocol::Response;
namespace WorkerAgentState {
static const char kAutoAttach[] = "autoAttach";
static const char kWaitForDebuggerOnStart[] = "waitForDebuggerOnStart";
static const char kAttachedWorkerIds[] = "attachedWorkerIds";
static const char kAttachedSessionIds[] = "attachedSessionIds";
};
namespace {
// TODO(dgozman): support multiple sessions in protocol.
static const int kSessionId = 1;
} // namespace
int InspectorWorkerAgent::s_last_connection_ = 0;
InspectorWorkerAgent::InspectorWorkerAgent(InspectedFrames* inspected_frames)
: inspected_frames_(inspected_frames) {}
......@@ -60,10 +58,10 @@ void InspectorWorkerAgent::Restore() {
if (!AutoAttachEnabled())
return;
instrumenting_agents_->addInspectorWorkerAgent(this);
protocol::DictionaryValue* attached = AttachedWorkerIds();
protocol::DictionaryValue* attached = AttachedSessionIds();
for (size_t i = 0; i < attached->size(); ++i)
GetFrontend()->detachedFromTarget(attached->at(i).first);
state_->remove(WorkerAgentState::kAttachedWorkerIds);
state_->remove(WorkerAgentState::kAttachedSessionIds);
ConnectToAllProxies();
}
......@@ -74,7 +72,7 @@ Response InspectorWorkerAgent::disable() {
}
state_->setBoolean(WorkerAgentState::kAutoAttach, false);
state_->setBoolean(WorkerAgentState::kWaitForDebuggerOnStart, false);
state_->remove(WorkerAgentState::kAttachedWorkerIds);
state_->remove(WorkerAgentState::kAttachedSessionIds);
return Response::OK();
}
......@@ -104,13 +102,33 @@ bool InspectorWorkerAgent::AutoAttachEnabled() {
return state_->booleanProperty(WorkerAgentState::kAutoAttach, false);
}
Response InspectorWorkerAgent::sendMessageToTarget(const String& target_id,
const String& message) {
WorkerInspectorProxy* proxy = connected_proxies_.at(target_id);
if (!proxy)
return Response::Error("Not attached to a target with given id");
proxy->SendMessageToInspector(kSessionId, message);
Response InspectorWorkerAgent::sendMessageToTarget(const String& message,
Maybe<String> session_id,
Maybe<String> target_id) {
if (session_id.isJust()) {
auto it = session_id_to_connection_.find(session_id.fromJust());
if (it == session_id_to_connection_.end())
return Response::Error("No session with given id");
WorkerInspectorProxy* proxy = connected_proxies_.at(it->value);
proxy->SendMessageToInspector(it->value, message);
return Response::OK();
}
if (target_id.isJust()) {
int connection = 0;
for (auto& it : connected_proxies_) {
if (it.value->InspectorId() == target_id.fromJust()) {
if (connection)
return Response::Error("Multiple sessions attached, specify id");
connection = it.key;
}
}
if (!connection)
return Response::Error("No target with given id");
WorkerInspectorProxy* proxy = connected_proxies_.at(connection);
proxy->SendMessageToInspector(connection, message);
return Response::OK();
}
return Response::Error("Session id must be specified");
}
void InspectorWorkerAgent::SetTracingSessionId(
......@@ -138,12 +156,20 @@ void InspectorWorkerAgent::DidStartWorker(WorkerInspectorProxy* proxy,
void InspectorWorkerAgent::WorkerTerminated(WorkerInspectorProxy* proxy) {
DCHECK(GetFrontend() && AutoAttachEnabled());
if (connected_proxies_.find(proxy->InspectorId()) == connected_proxies_.end())
return;
AttachedWorkerIds()->remove(proxy->InspectorId());
GetFrontend()->detachedFromTarget(proxy->InspectorId());
proxy->DisconnectFromInspector(kSessionId, this);
connected_proxies_.erase(proxy->InspectorId());
Vector<String> session_ids;
for (auto& it : session_id_to_connection_) {
if (connected_proxies_.at(it.value) == proxy)
session_ids.push_back(it.key);
}
for (const String& session_id : session_ids) {
AttachedSessionIds()->remove(session_id);
GetFrontend()->detachedFromTarget(session_id, proxy->InspectorId());
int connection = session_id_to_connection_.at(session_id);
proxy->DisconnectFromInspector(connection, this);
connected_proxies_.erase(connection);
connection_to_session_id_.erase(connection);
session_id_to_connection_.erase(session_id);
}
}
void InspectorWorkerAgent::ConnectToAllProxies() {
......@@ -152,19 +178,23 @@ void InspectorWorkerAgent::ConnectToAllProxies() {
DCHECK(proxy->GetExecutionContext()->IsDocument());
Document* document = ToDocument(proxy->GetExecutionContext());
if (document->GetFrame() &&
inspected_frames_->Contains(document->GetFrame()))
inspected_frames_->Contains(document->GetFrame())) {
ConnectToProxy(proxy, false);
}
}
}
void InspectorWorkerAgent::DisconnectFromAllProxies(bool report_to_frontend) {
for (auto& id_proxy : connected_proxies_) {
for (auto& it : session_id_to_connection_) {
WorkerInspectorProxy* proxy = connected_proxies_.at(it.value);
if (report_to_frontend) {
AttachedWorkerIds()->remove(id_proxy.key);
GetFrontend()->detachedFromTarget(id_proxy.key);
AttachedSessionIds()->remove(it.key);
GetFrontend()->detachedFromTarget(it.key, proxy->InspectorId());
}
id_proxy.value->DisconnectFromInspector(kSessionId, this);
proxy->DisconnectFromInspector(it.value, this);
}
connection_to_session_id_.clear();
session_id_to_connection_.clear();
connected_proxies_.clear();
}
......@@ -175,33 +205,36 @@ void InspectorWorkerAgent::DidCommitLoadForLocalFrame(LocalFrame* frame) {
// During navigation workers from old page may die after a while.
// Usually, it's fine to report them terminated later, but some tests
// expect strict set of workers, and we reuse renderer between tests.
for (auto& id_proxy : connected_proxies_) {
AttachedWorkerIds()->remove(id_proxy.key);
GetFrontend()->detachedFromTarget(id_proxy.key);
id_proxy.value->DisconnectFromInspector(kSessionId, this);
}
connected_proxies_.clear();
DisconnectFromAllProxies(true);
}
protocol::DictionaryValue* InspectorWorkerAgent::AttachedWorkerIds() {
protocol::DictionaryValue* InspectorWorkerAgent::AttachedSessionIds() {
protocol::DictionaryValue* ids =
state_->getObject(WorkerAgentState::kAttachedWorkerIds);
state_->getObject(WorkerAgentState::kAttachedSessionIds);
if (!ids) {
std::unique_ptr<protocol::DictionaryValue> new_ids =
protocol::DictionaryValue::create();
ids = new_ids.get();
state_->setObject(WorkerAgentState::kAttachedWorkerIds, std::move(new_ids));
state_->setObject(WorkerAgentState::kAttachedSessionIds,
std::move(new_ids));
}
return ids;
}
void InspectorWorkerAgent::ConnectToProxy(WorkerInspectorProxy* proxy,
bool waiting_for_debugger) {
connected_proxies_.Set(proxy->InspectorId(), proxy);
proxy->ConnectToInspector(kSessionId, this);
int connection = ++s_last_connection_;
connected_proxies_.Set(connection, proxy);
String session_id = proxy->InspectorId() + "-" + String::Number(connection);
session_id_to_connection_.Set(session_id, connection);
connection_to_session_id_.Set(connection, session_id);
proxy->ConnectToInspector(connection, this);
DCHECK(GetFrontend());
AttachedWorkerIds()->setBoolean(proxy->InspectorId(), true);
GetFrontend()->attachedToTarget(protocol::Target::TargetInfo::create()
AttachedSessionIds()->setBoolean(session_id, true);
GetFrontend()->attachedToTarget(session_id,
protocol::Target::TargetInfo::create()
.setTargetId(proxy->InspectorId())
.setType("worker")
.setTitle(proxy->Url())
......@@ -213,10 +246,13 @@ void InspectorWorkerAgent::ConnectToProxy(WorkerInspectorProxy* proxy,
void InspectorWorkerAgent::DispatchMessageFromWorker(
WorkerInspectorProxy* proxy,
int session_id,
int connection,
const String& message) {
DCHECK(session_id == kSessionId);
GetFrontend()->receivedMessageFromTarget(proxy->InspectorId(), message);
auto it = connection_to_session_id_.find(connection);
if (it == connection_to_session_id_.end())
return;
GetFrontend()->receivedMessageFromTarget(it->value, message,
proxy->InspectorId());
}
DEFINE_TRACE(InspectorWorkerAgent) {
......
......@@ -65,8 +65,10 @@ class CORE_EXPORT InspectorWorkerAgent final
protocol::Response setAutoAttach(bool auto_attach,
bool wait_for_debugger_on_start) override;
protocol::Response setAttachToFrames(bool attach) override;
protocol::Response sendMessageToTarget(const String& target_id,
const String& message) override;
protocol::Response sendMessageToTarget(
const String& message,
protocol::Maybe<String> session_id,
protocol::Maybe<String> target_id) override;
void SetTracingSessionId(const String&);
......@@ -75,16 +77,19 @@ class CORE_EXPORT InspectorWorkerAgent final
void ConnectToAllProxies();
void DisconnectFromAllProxies(bool report_to_frontend);
void ConnectToProxy(WorkerInspectorProxy*, bool waiting_for_debugger);
protocol::DictionaryValue* AttachedWorkerIds();
protocol::DictionaryValue* AttachedSessionIds();
// WorkerInspectorProxy::PageInspector implementation.
void DispatchMessageFromWorker(WorkerInspectorProxy*,
int session_id,
int connection,
const String& message) override;
Member<InspectedFrames> inspected_frames_;
HeapHashMap<String, Member<WorkerInspectorProxy>> connected_proxies_;
HeapHashMap<int, Member<WorkerInspectorProxy>> connected_proxies_;
HashMap<int, String> connection_to_session_id_;
HashMap<String, int> session_id_to_connection_;
String tracing_session_id_;
static int s_last_connection_;
};
} // namespace blink
......
......@@ -3601,6 +3601,11 @@
"id": "TargetID",
"type": "string"
},
{
"id": "SessionID",
"type": "string",
"description": "Unique identifier of attached debugging session."
},
{
"id": "BrowserContextID",
"type": "string"
......@@ -3656,10 +3661,11 @@
},
{
"name": "sendMessageToTarget",
"description": "Sends protocol message to the target with given id.",
"description": "Sends protocol message over session with given id.",
"parameters": [
{ "name": "targetId", "$ref": "TargetID" },
{ "name": "message", "type": "string" }
{ "name": "message", "type": "string" },
{ "name": "sessionId", "$ref": "SessionID", "optional": true, "description": "Identifier of the session." },
{ "name": "targetId", "$ref": "TargetID", "optional": true, "deprecated": true, "description": "Deprecated." }
]
},
{
......@@ -3696,14 +3702,15 @@
{ "name": "targetId", "$ref": "TargetID" }
],
"returns": [
{ "name": "success", "type": "boolean", "description": "Whether attach succeeded." }
{ "name": "sessionId", "$ref": "SessionID", "description": "Id assigned to the session." }
]
},
{
"name": "detachFromTarget",
"description": "Detaches from the target with given id.",
"description": "Detaches session with given id.",
"parameters": [
{ "name": "targetId", "$ref": "TargetID" }
{ "name": "sessionId", "$ref": "SessionID", "optional": true, "description": "Session to detach." },
{ "name": "targetId", "$ref": "TargetID", "optional": true, "deprecated": true, "description": "Deprecated." }
]
},
{
......@@ -3770,23 +3777,26 @@
"name": "attachedToTarget",
"description": "Issued when attached to target because of auto-attach or <code>attachToTarget</code> command.",
"parameters": [
{ "name": "sessionId", "$ref": "SessionID", "description": "Identifier assigned to the session used to send/receive messages." },
{ "name": "targetInfo", "$ref": "TargetInfo" },
{ "name": "waitingForDebugger", "type": "boolean" }
]
},
{
"name": "detachedFromTarget",
"description": "Issued when detached from target for any reason (including <code>detachFromTarget</code> command).",
"description": "Issued when detached from target for any reason (including <code>detachFromTarget</code> command). Can be issued multiple times per target if multiple sessions have been attached to it.",
"parameters": [
{ "name": "targetId", "$ref": "TargetID" }
{ "name": "sessionId", "$ref": "SessionID", "description": "Detached session identifier." },
{ "name": "targetId", "$ref": "TargetID", "optional": true, "deprecated": true, "description": "Deprecated." }
]
},
{
"name": "receivedMessageFromTarget",
"description": "Notifies about new protocol message from attached target.",
"description": "Notifies about a new protocol message received from the session (as reported in <code>attachedToTarget</code> event).",
"parameters": [
{ "name": "targetId", "$ref": "TargetID" },
{ "name": "message", "type": "string" }
{ "name": "sessionId", "$ref": "SessionID", "description": "Identifier of a session which sends a message." },
{ "name": "message", "type": "string" },
{ "name": "targetId", "$ref": "TargetID", "optional": true, "deprecated": true, "description": "Deprecated." }
]
}
]
......
......@@ -457,10 +457,8 @@ SDK.ChildTargetManager = class {
InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
}
// TODO(dgozman): this is O(n^2) when removing main target.
var childTargets = this._targetManager._targets.filter(child => child.parentTarget() === this._parentTarget);
for (var child of childTargets)
this.detachedFromTarget(child.id());
for (var sessionId of this._childConnections.keys())
this.detachedFromTarget(sessionId, undefined);
}
/**
......@@ -534,10 +532,11 @@ SDK.ChildTargetManager = class {
/**
* @override
* @param {string} sessionId
* @param {!Protocol.Target.TargetInfo} targetInfo
* @param {boolean} waitingForDebugger
*/
attachedToTarget(targetInfo, waitingForDebugger) {
attachedToTarget(sessionId, targetInfo, waitingForDebugger) {
var targetName = '';
if (targetInfo.type === 'node') {
targetName = Common.UIString('Node.js: %s', targetInfo.url);
......@@ -548,7 +547,7 @@ SDK.ChildTargetManager = class {
}
var target = this._targetManager.createTarget(
targetInfo.targetId, targetName, this._capabilitiesForType(targetInfo.type),
this._createChildConnection.bind(this, this._targetAgent, targetInfo.targetId), this._parentTarget);
this._createChildConnection.bind(this, this._targetAgent, sessionId), this._parentTarget);
// Only pause the new worker if debugging SW - we are going through the pause on start checkbox.
if (!this._parentTarget.parentTarget() && Runtime.queryParam('isSharedWorker') && waitingForDebugger) {
......@@ -564,33 +563,35 @@ SDK.ChildTargetManager = class {
/**
* @override
* @param {string} childTargetId
* @param {string} sessionId
* @param {string=} childTargetId
*/
detachedFromTarget(childTargetId) {
this._childConnections.get(childTargetId)._onDisconnect.call(null, 'target terminated');
this._childConnections.delete(childTargetId);
detachedFromTarget(sessionId, childTargetId) {
this._childConnections.get(sessionId)._onDisconnect.call(null, 'target terminated');
this._childConnections.delete(sessionId);
}
/**
* @override
* @param {string} childTargetId
* @param {string} sessionId
* @param {string} message
* @param {string=} childTargetId
*/
receivedMessageFromTarget(childTargetId, message) {
var connection = this._childConnections.get(childTargetId);
receivedMessageFromTarget(sessionId, message, childTargetId) {
var connection = this._childConnections.get(sessionId);
if (connection)
connection._onMessage.call(null, message);
}
/**
* @param {!Protocol.TargetAgent} agent
* @param {string} childTargetId
* @param {string} sessionId
* @param {!Protocol.InspectorBackend.Connection.Params} params
* @return {!Protocol.InspectorBackend.Connection}
*/
_createChildConnection(agent, childTargetId, params) {
var connection = new SDK.ChildConnection(agent, childTargetId, params);
this._childConnections.set(childTargetId, connection);
_createChildConnection(agent, sessionId, params) {
var connection = new SDK.ChildConnection(agent, sessionId, params);
this._childConnections.set(sessionId, connection);
return connection;
}
};
......@@ -601,12 +602,12 @@ SDK.ChildTargetManager = class {
SDK.ChildConnection = class {
/**
* @param {!Protocol.TargetAgent} agent
* @param {string} targetId
* @param {string} sessionId
* @param {!Protocol.InspectorBackend.Connection.Params} params
*/
constructor(agent, targetId, params) {
constructor(agent, sessionId, params) {
this._agent = agent;
this._targetId = targetId;
this._sessionId = sessionId;
this._onMessage = params.onMessage;
this._onDisconnect = params.onDisconnect;
}
......@@ -616,7 +617,7 @@ SDK.ChildConnection = class {
* @param {string} message
*/
sendMessage(message) {
this._agent.sendMessageToTarget(this._targetId, message);
this._agent.sendMessageToTarget(message, this._sessionId);
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment