blob: 41af9ef4ff3a9b3978da55b35bf30b760502ec98 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "base/bind.h"
#include "base/containers/span.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/run_loop.h"
#include "chrome/browser/ui/browser.h"
namespace {
const char kIdParam[] = "id";
const char kMethodParam[] = "method";
const char kParamsParam[] = "params";
} // namespace
DevToolsProtocolTestBase::DevToolsProtocolTestBase() = default;
DevToolsProtocolTestBase::~DevToolsProtocolTestBase() = default;
void DevToolsProtocolTestBase::TearDownOnMainThread() {
Detach();
}
void DevToolsProtocolTestBase::DispatchProtocolMessage(
content::DevToolsAgentHost* agent_host,
base::span<const uint8_t> message) {
base::StringPiece message_str(reinterpret_cast<const char*>(message.data()),
message.size());
absl::optional<base::Value> parsed_message =
base::JSONReader::Read(message_str);
ASSERT_TRUE(parsed_message.has_value());
if (auto id = parsed_message->FindIntPath("id")) {
base::Value* result = parsed_message->FindDictPath("result");
ASSERT_TRUE(result) << "message: " << message_str;
result_ = result->Clone();
in_dispatch_ = false;
if (*id && *id == waiting_for_command_result_id_) {
waiting_for_command_result_id_ = 0;
std::move(run_loop_quit_closure_).Run();
}
} else {
std::string* notification = parsed_message->FindStringPath("method");
ASSERT_TRUE(notification) << "message: " << message_str;
notifications_.push_back(*notification);
base::Value* params = parsed_message->FindPath("params");
notification_params_.push_back(params ? params->Clone() : base::Value());
if (waiting_for_notification_ == *notification &&
(waiting_for_notification_matcher_.is_null() ||
waiting_for_notification_matcher_.Run(notification_params_.back()))) {
waiting_for_notification_.clear();
waiting_for_notification_matcher_ = NotificationMatcher();
waiting_for_notification_params_ = notification_params_.back().Clone();
std::move(run_loop_quit_closure_).Run();
}
}
}
void DevToolsProtocolTestBase::SendCommand(const std::string& method,
base::Value params,
bool synchronous) {
in_dispatch_ = true;
base::Value command(base::Value::Type::DICTIONARY);
command.SetKey(kIdParam, base::Value(++last_sent_id_));
command.SetKey(kMethodParam, base::Value(method));
if (!params.is_none())
command.SetPath(kParamsParam, std::move(params));
std::string json_command;
base::JSONWriter::Write(command, &json_command);
agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(json_command)));
// Some messages are dispatched synchronously.
// Only run loop if we are not finished yet.
if (in_dispatch_ && synchronous)
WaitForResponse();
in_dispatch_ = false;
}
void DevToolsProtocolTestBase::WaitForResponse() {
waiting_for_command_result_id_ = last_sent_id_;
RunLoopUpdatingQuitClosure();
}
void DevToolsProtocolTestBase::RunLoopUpdatingQuitClosure() {
base::RunLoop run_loop;
CHECK(!run_loop_quit_closure_);
run_loop_quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
void DevToolsProtocolTestBase::AttachToBrowser() {
agent_host_ = content::DevToolsAgentHost::CreateForBrowser(
nullptr, content::DevToolsAgentHost::CreateServerSocketCallback());
agent_host_->AttachClient(this);
}
void DevToolsProtocolTestBase::Attach() {
agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents());
agent_host_->AttachClient(this);
}
void DevToolsProtocolTestBase::Detach() {
if (agent_host_) {
agent_host_->DetachClient(this);
agent_host_ = nullptr;
}
}
content::WebContents* DevToolsProtocolTestBase::web_contents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
base::Value DevToolsProtocolTestBase::WaitForNotification(
const std::string& notification) {
auto always_match =
base::BindRepeating([](const base::Value&) { return true; });
return WaitForMatchingNotification(notification, always_match);
}
base::Value DevToolsProtocolTestBase::WaitForMatchingNotification(
const std::string& notification,
const NotificationMatcher& matcher) {
for (size_t i = 0; i < notifications_.size(); ++i) {
if (notifications_[i] == notification &&
matcher.Run(notification_params_[i])) {
base::Value result = std::move(notification_params_[i]);
notifications_.erase(notifications_.begin() + i);
notification_params_.erase(notification_params_.begin() + i);
return result;
}
}
waiting_for_notification_ = notification;
waiting_for_notification_matcher_ = matcher;
RunLoopUpdatingQuitClosure();
return std::move(waiting_for_notification_params_);
}
void DevToolsProtocolTestBase::AgentHostClosed(
content::DevToolsAgentHost* agent_host) {}