blob: 2ae08943d0367a6731b15fc6a894355e42f6cf64 [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 "core/inspector/InspectorWorkerAgent.h"
#include "base/memory/scoped_refptr.h"
#include "core/dom/Document.h"
#include "core/inspector/InspectedFrames.h"
#include "platform/weborigin/KURL.h"
#include "platform/wtf/text/WTFString.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)
: inspected_frames_(inspected_frames) {}
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();
}
Response InspectorWorkerAgent::setAttachToFrames(bool attach) {
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::SetTracingSessionId(
const String& tracing_session_id) {
tracing_session_id_ = tracing_session_id;
if (tracing_session_id.IsEmpty())
return;
for (auto& id_proxy : connected_proxies_)
id_proxy.value->WriteTimelineStartedEvent(tracing_session_id);
}
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);
if (!tracing_session_id_.IsEmpty())
proxy->WriteTimelineStartedEvent(tracing_session_id_);
}
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() {
for (WorkerInspectorProxy* proxy : WorkerInspectorProxy::AllProxies()) {
// For now we assume this is document. TODO(kinuko): Fix this.
DCHECK(proxy->GetExecutionContext()->IsDocument());
Document* document = ToDocument(proxy->GetExecutionContext());
if (document->GetFrame() &&
inspected_frames_->Contains(document->GetFrame())) {
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,
inspected_frames_->GetDevToolsFrameToken(), 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_);
InspectorBaseAgent::Trace(visitor);
}
} // namespace blink