blob: 4b8fe1bd09b49158e7c3c55179ebf7ec35543237 [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/debug/backend/script_debugger_agent.h"
#include <string>
#include "base/strings/stringprintf.h"
#include "cobalt/debug/json_object.h"
namespace {
// State keys
constexpr char kScriptDebuggerState[] = "script_debugger";
// JSON attribute names
constexpr char kId[] = "id";
constexpr char kMethod[] = "method";
constexpr char kParams[] = "params";
} // namespace
namespace cobalt {
namespace debug {
namespace backend {
ScriptDebuggerAgent::ScriptDebuggerAgent(
DebugDispatcher* dispatcher, script::ScriptDebugger* script_debugger)
: dispatcher_(dispatcher),
script_debugger_(script_debugger),
supported_domains_(script_debugger->SupportedProtocolDomains()) {}
void ScriptDebuggerAgent::Thaw(JSONObject agent_state) {
for (auto domain : supported_domains_) {
dispatcher_->AddDomain(domain, base::Bind(&ScriptDebuggerAgent::RunCommand,
base::Unretained(this)));
}
std::string script_debugger_state;
if (agent_state) {
agent_state->GetString(kScriptDebuggerState, &script_debugger_state);
}
script_debugger_->Attach(script_debugger_state);
}
JSONObject ScriptDebuggerAgent::Freeze() {
for (auto domain : supported_domains_) {
dispatcher_->RemoveDomain(domain);
}
JSONObject agent_state(new base::DictionaryValue());
std::string script_debugger_state = script_debugger_->Detach();
agent_state->SetString(kScriptDebuggerState, script_debugger_state);
return agent_state;
}
base::Optional<Command> ScriptDebuggerAgent::RunCommand(Command command) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Use an internal ID to store the pending command until we get a response.
int command_id = ++last_command_id_;
JSONObject message(new base::DictionaryValue());
message->SetInteger(kId, command_id);
message->SetString(kMethod, command.GetMethod());
JSONObject params = JSONParse(command.GetParams());
if (params) {
message->Set(kParams, std::move(params));
}
// Store the pending command before dispatching it so that we can find it if
// the script debugger sends a synchronous response before returning.
std::string method = command.GetMethod();
pending_commands_.emplace(command_id, std::move(command));
if (script_debugger_->DispatchProtocolMessage(method,
JSONStringify(message))) {
// The command has been dispached; keep ownership of it in the map.
return base::nullopt;
}
// Take the command back out of the map and return it for fallback.
auto opt_command =
base::make_optional(std::move(pending_commands_.at(command_id)));
pending_commands_.erase(command_id);
return opt_command;
}
void ScriptDebuggerAgent::SendCommandResponse(
const std::string& json_response) {
JSONObject response = JSONParse(json_response);
// Strip the internal ID from the response, and get its value.
int command_id = 0;
response->GetInteger(kId, &command_id);
response->Remove(kId, nullptr);
// Use the stripped ID to lookup the command it's a response for.
auto iter = pending_commands_.find(command_id);
if (iter != pending_commands_.end()) {
iter->second.SendResponse(response);
pending_commands_.erase(iter);
} else {
DLOG(ERROR) << "Spurious debugger response: " << json_response;
}
}
void ScriptDebuggerAgent::SendEvent(const std::string& json_event) {
JSONObject event = JSONParse(json_event);
std::string method;
event->GetString(kMethod, &method);
JSONObject params;
base::Value* value = nullptr;
base::DictionaryValue* dict_value = nullptr;
if (event->Get(kParams, &value) && value->GetAsDictionary(&dict_value)) {
params.reset(dict_value->DeepCopy());
}
dispatcher_->SendEvent(method, params);
}
} // namespace backend
} // namespace debug
} // namespace cobalt