blob: f09fcf90031bf7d283dce28ac8a795a45b9c7314 [file] [log] [blame]
// Copyright 2022 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/direct_sockets/direct_sockets_test_utils.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/notreached.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/test_future.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/web_contents_tester.h"
namespace content::test {
// MockHostResolver implementation
MockHostResolver::MockHostResolver(
mojo::PendingReceiver<network::mojom::HostResolver> resolver_receiver,
net::HostResolver* internal_resolver)
: receiver_(this), internal_resolver_(internal_resolver) {
receiver_.Bind(std::move(resolver_receiver));
}
MockHostResolver::~MockHostResolver() = default;
void MockHostResolver::ResolveHost(
network::mojom::HostResolverHostPtr host,
const ::net::NetworkIsolationKey& network_isolation_key,
network::mojom::ResolveHostParametersPtr optional_parameters,
::mojo::PendingRemote<network::mojom::ResolveHostClient>
pending_response_client) {
DCHECK(!internal_request_);
DCHECK(!response_client_.is_bound());
internal_request_ =
host->is_host_port_pair()
? internal_resolver_->CreateRequest(
host->get_host_port_pair(), network_isolation_key,
net::NetLogWithSource::Make(net::NetLog::Get(),
net::NetLogSourceType::NONE),
absl::nullopt)
: internal_resolver_->CreateRequest(
host->get_scheme_host_port(), network_isolation_key,
net::NetLogWithSource::Make(net::NetLog::Get(),
net::NetLogSourceType::NONE),
absl::nullopt);
mojo::Remote<network::mojom::ResolveHostClient> response_client(
std::move(pending_response_client));
int rv = internal_request_->Start(
base::BindOnce(&MockHostResolver::OnComplete, base::Unretained(this)));
if (rv != net::ERR_IO_PENDING) {
response_client->OnComplete(
rv, internal_request_->GetResolveErrorInfo(),
base::OptionalFromPtr(internal_request_->GetAddressResults()));
return;
}
response_client_ = std::move(response_client);
}
void MockHostResolver::MdnsListen(
const ::net::HostPortPair& host,
::net::DnsQueryType query_type,
::mojo::PendingRemote<network::mojom::MdnsListenClient> response_client,
MdnsListenCallback callback) {
NOTIMPLEMENTED();
}
void MockHostResolver::OnComplete(int error) {
DCHECK(response_client_.is_bound());
DCHECK(internal_request_);
response_client_->OnComplete(
error, internal_request_->GetResolveErrorInfo(),
base::OptionalFromPtr(internal_request_->GetAddressResults()));
response_client_.reset();
}
// MockUDPSocket implementation
MockUDPSocket::MockUDPSocket(
mojo::PendingReceiver<network::mojom::UDPSocket> receiver,
mojo::PendingRemote<network::mojom::UDPSocketListener> listener) {
receiver_.Bind(std::move(receiver));
listener_.Bind(std::move(listener));
}
MockUDPSocket::~MockUDPSocket() {
if (callback_) {
std::move(callback_).Run(net::OK);
}
}
void MockUDPSocket::Connect(const net::IPEndPoint& remote_addr,
network::mojom::UDPSocketOptionsPtr socket_options,
ConnectCallback callback) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), net::OK,
net::IPEndPoint{net::IPAddress::IPv4Localhost(), 0}));
}
void MockUDPSocket::Send(
base::span<const uint8_t> data,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
SendCallback callback) {
DCHECK(!callback_);
callback_ = std::move(callback);
if (additional_send_callback_) {
std::move(additional_send_callback_).Run();
}
}
void MockUDPSocket::MockSend(int32_t result,
const absl::optional<base::span<uint8_t>>& data) {
listener_->OnReceived(result, {}, data);
}
// MockNetworkContext implementation
MockNetworkContext::MockNetworkContext() = default;
MockNetworkContext::~MockNetworkContext() = default;
void MockNetworkContext::CreateUDPSocket(
mojo::PendingReceiver<network::mojom::UDPSocket> receiver,
mojo::PendingRemote<network::mojom::UDPSocketListener> listener) {
socket_ = CreateMockUDPSocket(std::move(receiver), std::move(listener));
}
void MockNetworkContext::CreateHostResolver(
const absl::optional<net::DnsConfigOverrides>& config_overrides,
mojo::PendingReceiver<network::mojom::HostResolver> receiver) {
internal_resolver_ = net::HostResolver::CreateStandaloneResolver(
net::NetLog::Get(), /*options=*/absl::nullopt, host_mapping_rules_,
/*enable_caching=*/false);
host_resolver_ = std::make_unique<MockHostResolver>(std::move(receiver),
internal_resolver_.get());
}
std::unique_ptr<MockUDPSocket> MockNetworkContext::CreateMockUDPSocket(
mojo::PendingReceiver<network::mojom::UDPSocket> receiver,
mojo::PendingRemote<network::mojom::UDPSocketListener> listener) {
return std::make_unique<MockUDPSocket>(std::move(receiver),
std::move(listener));
}
// AsyncJsRunner implementation
AsyncJsRunner::AsyncJsRunner(content::WebContents* web_contents)
: WebContentsObserver(web_contents) {}
AsyncJsRunner::~AsyncJsRunner() = default;
std::unique_ptr<base::test::TestFuture<std::string>> AsyncJsRunner::RunScript(
const std::string& async_script) {
// Do not leave behind hanging futures from previous invocations.
DCHECK(!future_callback_);
auto future = std::make_unique<base::test::TestFuture<std::string>>();
token_ = base::Token::CreateRandom();
future_callback_ = future->GetCallback();
const std::string wrapped_script =
MakeScriptSendResultToDomQueue(async_script);
ExecuteScriptAsync(web_contents(), wrapped_script);
return future;
}
void AsyncJsRunner::DomOperationResponse(RenderFrameHost* render_frame_host,
const std::string& json_string) {
// Check that future is valid and not yet fulfilled.
DCHECK(future_callback_);
auto parsed = base::JSONReader::ReadAndReturnValueWithError(
json_string, base::JSON_ALLOW_TRAILING_COMMAS);
DCHECK(parsed.has_value());
DCHECK_EQ(parsed->type(), base::Value::Type::LIST);
const auto& list = parsed->GetList();
DCHECK_EQ(list.size(), 2U);
DCHECK(list[0].is_string());
DCHECK(list[1].is_string());
if (list[1].GetString() == token_.ToString()) {
std::move(future_callback_).Run(list[0].GetString());
}
}
std::string AsyncJsRunner::MakeScriptSendResultToDomQueue(
const std::string& script) const {
DCHECK(!script.empty());
return WrapAsync(base::StringPrintf(
R"(
let result = await %s;
window.domAutomationController.send([result, '%s']);
)",
script.c_str(), token_.ToString().c_str()));
}
bool IsolatedAppContentBrowserClient::ShouldUrlUseApplicationIsolationLevel(
BrowserContext* browser_context,
const GURL& url) {
return true;
}
absl::optional<blink::ParsedPermissionsPolicy>
IsolatedAppContentBrowserClient::GetPermissionsPolicyForIsolatedApp(
content::BrowserContext* browser_context,
const url::Origin& app_origin) {
blink::ParsedPermissionsPolicy out;
blink::ParsedPermissionsPolicyDeclaration decl(
blink::mojom::PermissionsPolicyFeature::kDirectSockets,
/*values=*/{app_origin},
/*matches_all_origins=*/false, /*matches_opaque_src=*/false);
out.push_back(decl);
return out;
}
// misc
std::string WrapAsync(const std::string& script) {
DCHECK(!script.empty());
return base::StringPrintf(R"((async () => {%s})())", script.c_str());
}
} // namespace content::test