blob: e365e18d0769509ac12394cdbdf7a77407d685b5 [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
* OWNER 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 "third_party/blink/renderer/core/inspector/InspectorWorkerAgent.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/inspector/inspected_frames.h"
#include "third_party/blink/renderer/core/workers/execution_context_worker_registry.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
using protocol::Maybe;
using protocol::Response;
namespace WorkerAgentState {
static const char kAutoAttach[] = "autoAttach";
static const char kWaitForDebuggerOnStart[] = "waitForDebuggerOnStart";
static const char kAttachedSessionIds[] = "attachedSessionIds";
};
int InspectorWorkerAgent::s_last_connection_ = 0;
InspectorWorkerAgent::InspectorWorkerAgent(
InspectedFrames* inspected_frames,
WorkerGlobalScope* worker_global_scope)
: inspected_frames_(inspected_frames),
worker_global_scope_(worker_global_scope) {}
InspectorWorkerAgent::~InspectorWorkerAgent() = default;
void InspectorWorkerAgent::Restore() {
if (!AutoAttachEnabled())
return;
instrumenting_agents_->addInspectorWorkerAgent(this);
protocol::DictionaryValue* attached = AttachedSessionIds();
for (size_t i = 0; i < attached->size(); ++i)
GetFrontend()->detachedFromTarget(attached->at(i).first);
state_->remove(WorkerAgentState::kAttachedSessionIds);
ConnectToAllProxies();
}
Response InspectorWorkerAgent::disable() {
if (AutoAttachEnabled()) {
DisconnectFromAllProxies(false);
instrumenting_agents_->removeInspectorWorkerAgent(this);
}
state_->setBoolean(WorkerAgentState::kAutoAttach, false);
state_->setBoolean(WorkerAgentState::kWaitForDebuggerOnStart, false);
state_->remove(WorkerAgentState::kAttachedSessionIds);
return Response::OK();
}
Response InspectorWorkerAgent::setAutoAttach(bool auto_attach,
bool wait_for_debugger_on_start) {
state_->setBoolean(WorkerAgentState::kWaitForDebuggerOnStart,
wait_for_debugger_on_start);
if (auto_attach == AutoAttachEnabled())
return Response::OK();
state_->setBoolean(WorkerAgentState::kAutoAttach, auto_attach);
if (auto_attach) {
instrumenting_agents_->addInspectorWorkerAgent(this);
ConnectToAllProxies();
} else {
DisconnectFromAllProxies(true);
instrumenting_agents_->removeInspectorWorkerAgent(this);
}
return Response::OK();
}
bool InspectorWorkerAgent::AutoAttachEnabled() {
return state_->booleanProperty(WorkerAgentState::kAutoAttach, false);
}
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::ShouldWaitForDebuggerOnWorkerStart(bool* result) {
if (AutoAttachEnabled() &&
state_->booleanProperty(WorkerAgentState::kWaitForDebuggerOnStart, false))
*result = true;
}
void InspectorWorkerAgent::DidStartWorker(WorkerInspectorProxy* proxy,
bool waiting_for_debugger) {
DCHECK(GetFrontend() && AutoAttachEnabled());
ConnectToProxy(proxy, waiting_for_debugger);
}
void InspectorWorkerAgent::WorkerTerminated(WorkerInspectorProxy* proxy) {
DCHECK(GetFrontend() && AutoAttachEnabled());
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() {
if (worker_global_scope_) {
for (WorkerInspectorProxy* proxy :
ExecutionContextWorkerRegistry::From(*worker_global_scope_)
->GetWorkerInspectorProxies()) {
ConnectToProxy(proxy, false);
}
return;
}
for (LocalFrame* frame : *inspected_frames_) {
for (WorkerInspectorProxy* proxy :
ExecutionContextWorkerRegistry::From(*frame->GetDocument())
->GetWorkerInspectorProxies()) {
ConnectToProxy(proxy, false);
}
}
}
void InspectorWorkerAgent::DisconnectFromAllProxies(bool report_to_frontend) {
for (auto& it : session_id_to_connection_) {
WorkerInspectorProxy* proxy = connected_proxies_.at(it.value);
if (report_to_frontend) {
AttachedSessionIds()->remove(it.key);
GetFrontend()->detachedFromTarget(it.key, proxy->InspectorId());
}
proxy->DisconnectFromInspector(it.value, this);
}
connection_to_session_id_.clear();
session_id_to_connection_.clear();
connected_proxies_.clear();
}
void InspectorWorkerAgent::DidCommitLoadForLocalFrame(LocalFrame* frame) {
if (!AutoAttachEnabled() || frame != inspected_frames_->Root())
return;
// 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.
DisconnectFromAllProxies(true);
}
protocol::DictionaryValue* InspectorWorkerAgent::AttachedSessionIds() {
protocol::DictionaryValue* ids =
state_->getObject(WorkerAgentState::kAttachedSessionIds);
if (!ids) {
std::unique_ptr<protocol::DictionaryValue> new_ids =
protocol::DictionaryValue::create();
ids = new_ids.get();
state_->setObject(WorkerAgentState::kAttachedSessionIds,
std::move(new_ids));
}
return ids;
}
void InspectorWorkerAgent::ConnectToProxy(WorkerInspectorProxy* proxy,
bool waiting_for_debugger) {
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());
AttachedSessionIds()->setBoolean(session_id, true);
GetFrontend()->attachedToTarget(session_id,
protocol::Target::TargetInfo::create()
.setTargetId(proxy->InspectorId())
.setType("worker")
.setTitle(proxy->Url())
.setUrl(proxy->Url())
.setAttached(true)
.build(),
waiting_for_debugger);
}
void InspectorWorkerAgent::DispatchMessageFromWorker(
WorkerInspectorProxy* proxy,
int connection,
const String& message) {
auto it = connection_to_session_id_.find(connection);
if (it == connection_to_session_id_.end())
return;
GetFrontend()->receivedMessageFromTarget(it->value, message,
proxy->InspectorId());
}
void InspectorWorkerAgent::Trace(blink::Visitor* visitor) {
visitor->Trace(connected_proxies_);
visitor->Trace(inspected_frames_);
visitor->Trace(worker_global_scope_);
InspectorBaseAgent::Trace(visitor);
}
} // namespace blink