blob: 14d94c8a26b853259eab84527b56d0f81e3194b3 [file] [log] [blame]
// Copyright 2018 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 "content/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/security_style_explanations.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
namespace {
const char kIdParam[] = "id";
const char kMethodParam[] = "method";
const char kParamsParam[] = "params";
} // namespace
DevToolsProtocolTest::DevToolsProtocolTest()
: last_sent_id_(0),
waiting_for_command_result_id_(0),
in_dispatch_(false),
agent_host_can_close_(false) {}
DevToolsProtocolTest::~DevToolsProtocolTest() = default;
void DevToolsProtocolTest::SetUpOnMainThread() {
host_resolver()->AddRule("*", "127.0.0.1");
}
bool DevToolsProtocolTest::DidAddMessageToConsole(
WebContents* source,
int32_t level,
const base::string16& message,
int32_t line_no,
const base::string16& source_id) {
console_messages_.push_back(base::UTF16ToUTF8(message));
return true;
}
base::DictionaryValue* DevToolsProtocolTest::SendCommand(
const std::string& method,
std::unique_ptr<base::Value> params,
bool wait) {
in_dispatch_ = true;
base::DictionaryValue command;
command.SetInteger(kIdParam, ++last_sent_id_);
command.SetString(kMethodParam, method);
if (params)
command.Set(kParamsParam, std::move(params));
std::string json_command;
base::JSONWriter::Write(command, &json_command);
agent_host_->DispatchProtocolMessage(this, json_command);
// Some messages are dispatched synchronously.
// Only run loop if we are not finished yet.
if (in_dispatch_ && wait) {
WaitForResponse();
in_dispatch_ = false;
return result_.get();
}
in_dispatch_ = false;
return result_.get();
}
void DevToolsProtocolTest::WaitForResponse() {
waiting_for_command_result_id_ = last_sent_id_;
RunLoopUpdatingQuitClosure();
}
bool DevToolsProtocolTest::HasValue(const std::string& path) {
base::Value* value = nullptr;
return result_->Get(path, &value);
}
bool DevToolsProtocolTest::HasListItem(const std::string& path_to_list,
const std::string& name,
const std::string& value) {
base::ListValue* list;
if (!result_->GetList(path_to_list, &list))
return false;
for (size_t i = 0; i != list->GetSize(); i++) {
base::DictionaryValue* item;
if (!list->GetDictionary(i, &item))
return false;
std::string id;
if (!item->GetString(name, &id))
return false;
if (id == value)
return true;
}
return false;
}
void DevToolsProtocolTest::Attach() {
agent_host_ = DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
agent_host_->AttachClient(this);
shell()->web_contents()->SetDelegate(this);
}
void DevToolsProtocolTest::AttachToBrowserTarget() {
// Tethering domain is not used in tests.
agent_host_ = DevToolsAgentHost::CreateForBrowser(
nullptr, DevToolsAgentHost::CreateServerSocketCallback());
agent_host_->AttachClient(this);
shell()->web_contents()->SetDelegate(this);
}
void DevToolsProtocolTest::TearDownOnMainThread() {
Detach();
}
std::unique_ptr<base::DictionaryValue>
DevToolsProtocolTest::WaitForNotification(const std::string& notification,
bool allow_existing) {
if (allow_existing) {
for (size_t i = 0; i < notifications_.size(); i++) {
if (notifications_[i] == notification) {
std::unique_ptr<base::DictionaryValue> result =
std::move(notification_params_[i]);
notifications_.erase(notifications_.begin() + i);
notification_params_.erase(notification_params_.begin() + i);
return result;
}
}
}
waiting_for_notification_ = notification;
RunLoopUpdatingQuitClosure();
return std::move(waiting_for_notification_params_);
}
blink::WebSecurityStyle DevToolsProtocolTest::GetSecurityStyle(
content::WebContents* web_contents,
content::SecurityStyleExplanations* security_style_explanations) {
security_style_explanations->secure_explanations.push_back(
SecurityStyleExplanation(
"an explanation title", "an explanation summary",
"an explanation description", cert_,
blink::WebMixedContentContextType::kNotMixedContent));
return blink::kWebSecurityStyleNeutral;
}
std::unique_ptr<base::DictionaryValue>
DevToolsProtocolTest::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].get())) {
std::unique_ptr<base::DictionaryValue> 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 DevToolsProtocolTest::ProcessNavigationsAnyOrder(
std::vector<ExpectedNavigation> expected_navigations) {
std::unique_ptr<base::DictionaryValue> params;
while (!expected_navigations.empty()) {
std::unique_ptr<base::DictionaryValue> params =
WaitForNotification("Network.requestIntercepted");
std::string interception_id;
ASSERT_TRUE(params->GetString("interceptionId", &interception_id));
bool is_redirect = params->HasKey("redirectUrl");
bool is_navigation;
ASSERT_TRUE(params->GetBoolean("isNavigationRequest", &is_navigation));
std::string resource_type;
ASSERT_TRUE(params->GetString("resourceType", &resource_type));
std::string url;
ASSERT_TRUE(params->GetString("request.url", &url));
if (is_redirect)
ASSERT_TRUE(params->GetString("redirectUrl", &url));
// The url will typically have a random port which we want to remove.
url = RemovePort(GURL(url));
if (!is_navigation) {
params.reset(new base::DictionaryValue());
params->SetString("interceptionId", interception_id);
SendCommand("Network.continueInterceptedRequest", std::move(params),
false);
continue;
}
bool navigation_was_expected = false;
for (auto it = expected_navigations.begin();
it != expected_navigations.end(); it++) {
if (url != it->url || is_redirect != it->is_redirect)
continue;
params.reset(new base::DictionaryValue());
params->SetString("interceptionId", interception_id);
if (it->abort)
params->SetString("errorReason", "Aborted");
SendCommand("Network.continueInterceptedRequest", std::move(params),
false);
navigation_was_expected = true;
expected_navigations.erase(it);
break;
}
EXPECT_TRUE(navigation_was_expected)
<< "url = " << url << "is_redirect = " << is_redirect;
}
}
void DevToolsProtocolTest::RunLoopUpdatingQuitClosure() {
base::RunLoop run_loop;
run_loop_quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
void DevToolsProtocolTest::DispatchProtocolMessage(
DevToolsAgentHost* agent_host,
const std::string& message) {
std::unique_ptr<base::DictionaryValue> root(
static_cast<base::DictionaryValue*>(
base::JSONReader::Read(message).release()));
int id;
if (root->GetInteger("id", &id)) {
result_ids_.push_back(id);
base::DictionaryValue* result;
ASSERT_TRUE(root->GetDictionary("result", &result));
result_.reset(result->DeepCopy());
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;
EXPECT_TRUE(root->GetString("method", &notification));
notifications_.push_back(notification);
base::DictionaryValue* params;
if (root->GetDictionary("params", &params)) {
notification_params_.push_back(params->CreateDeepCopy());
} else {
notification_params_.push_back(
base::WrapUnique(new base::DictionaryValue()));
}
if (waiting_for_notification_ == notification &&
(waiting_for_notification_matcher_.is_null() ||
waiting_for_notification_matcher_.Run(
notification_params_[notification_params_.size() - 1].get()))) {
waiting_for_notification_ = std::string();
waiting_for_notification_matcher_ = NotificationMatcher();
waiting_for_notification_params_ = base::WrapUnique(
notification_params_[notification_params_.size() - 1]->DeepCopy());
std::move(run_loop_quit_closure_).Run();
}
}
}
std::vector<std::string> DevToolsProtocolTest::GetAllFrameUrls() {
std::vector<std::string> urls;
for (RenderFrameHost* render_frame_host :
shell()->web_contents()->GetAllFrames()) {
urls.push_back(RemovePort(render_frame_host->GetLastCommittedURL()));
}
return urls;
}
void DevToolsProtocolTest::AgentHostClosed(DevToolsAgentHost* agent_host) {
if (!agent_host_can_close_)
NOTREACHED();
}
} // namespace content