Commit d6edf4bb authored by Simon Zünd's avatar Simon Zünd Committed by Chromium LUCI CQ

[devtools] Report TrustToken operation results with a new CDP event

This CL introduces a new experimental CDP event in the network domain.
As mentioned in the event documentation, the event can fire before
the corresponding network request is sent, or after the response
is received, depending on success/failure and the type of the
Trust Token operation.

R=caseq@chromium.org, sigurds@chromium.org

Bug: chromium:1126824
Change-Id: I57fad66b08200ce8c98c44b8406b805627fd3b6a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2554542
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#831980}
parent aadf089b
...@@ -761,6 +761,24 @@ void OnCorsPreflightRequestCompleted( ...@@ -761,6 +761,24 @@ void OnCorsPreflightRequestCompleted(
protocol::Network::ResourceTypeEnum::Preflight, status); protocol::Network::ResourceTypeEnum::Preflight, status);
} }
void OnTrustTokenOperationDone(
int32_t process_id,
int32_t routing_id,
const std::string& devtools_request_id,
const network::mojom::TrustTokenOperationResultPtr result) {
FrameTreeNode* ftn = GetFtnForNetworkRequest(process_id, routing_id);
if (ftn) {
DispatchToAgents(ftn, &protocol::NetworkHandler::OnTrustTokenOperationDone,
devtools_request_id, *result);
return;
}
// See comment on DispatchToWorkerAgents in OnRequestWillBeSentExtraInfo.
DispatchToWorkerAgents(process_id, routing_id,
&protocol::NetworkHandler::OnTrustTokenOperationDone,
devtools_request_id, *result);
}
namespace { namespace {
std::unique_ptr<protocol::Array<protocol::String>> BuildExclusionReasons( std::unique_ptr<protocol::Array<protocol::String>> BuildExclusionReasons(
net::CookieInclusionStatus status) { net::CookieInclusionStatus status) {
......
...@@ -172,6 +172,11 @@ void OnCorsPreflightRequestCompleted( ...@@ -172,6 +172,11 @@ void OnCorsPreflightRequestCompleted(
int32_t render_frame_id, int32_t render_frame_id,
const base::UnguessableToken& devtools_request_id, const base::UnguessableToken& devtools_request_id,
const network::URLLoaderCompletionStatus& status); const network::URLLoaderCompletionStatus& status);
void OnTrustTokenOperationDone(
int32_t process_id,
int32_t routing_id,
const std::string& devtools_request_id,
const network::mojom::TrustTokenOperationResultPtr result);
std::vector<std::unique_ptr<NavigationThrottle>> CreateNavigationThrottles( std::vector<std::unique_ptr<NavigationThrottle>> CreateNavigationThrottles(
NavigationHandle* navigation_handle); NavigationHandle* navigation_handle);
......
...@@ -3,11 +3,13 @@ ...@@ -3,11 +3,13 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "content/browser/devtools/protocol/devtools_protocol_test_support.h" #include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "content/browser/devtools/protocol/network.h"
#include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h" #include "content/shell/browser/shell.h"
#include "content/test/trust_token_browsertest.h" #include "content/test/trust_token_browsertest.h"
...@@ -65,6 +67,134 @@ IN_PROC_BROWSER_TEST_F(DevToolsTrustTokenBrowsertest, ...@@ -65,6 +67,134 @@ IN_PROC_BROWSER_TEST_F(DevToolsTrustTokenBrowsertest,
// 4) Verify the request is marked as successful and not as failed. // 4) Verify the request is marked as successful and not as failed.
WaitForNotification("Network.requestServedFromCache", true); WaitForNotification("Network.requestServedFromCache", true);
WaitForNotification("Network.loadingFinished", true); WaitForNotification("Network.loadingFinished", true);
WaitForNotification("Network.trustTokenOperationDone", true);
}
namespace {
bool MatchStatus(const std::string& expected_status,
base::DictionaryValue* params) {
std::string actual_status;
EXPECT_TRUE(params->GetString("status", &actual_status));
return expected_status == actual_status;
}
base::RepeatingCallback<bool(base::DictionaryValue*)> okStatusMatcher =
base::BindRepeating(
&MatchStatus,
protocol::Network::TrustTokenOperationDone::StatusEnum::Ok);
} // namespace
IN_PROC_BROWSER_TEST_F(DevToolsTrustTokenBrowsertest, FetchEndToEnd) {
ProvideRequestHandlerKeyCommitmentsToNetworkService({"a.test"});
// 1) Navigate to a test site.
GURL start_url = server_.GetURL("a.test", "/title1.html");
ASSERT_TRUE(NavigateToURL(shell(), start_url));
// 2) Open DevTools and enable Network domain.
Attach();
SendCommand("Network.enable", std::make_unique<base::DictionaryValue>());
// 3) Request and redeem a token, then use the redeemed token in a Signing
// request.
std::string command = R"(
(async () => {
await fetch('/issue', {trustToken: {type: 'token-request'}});
await fetch('/redeem', {trustToken: {type: 'token-redemption'}});
await fetch('/sign', {trustToken: {type: 'send-redemption-record',
signRequestData: 'include',
issuers: [$1]}});
return 'Success'; })(); )";
// We use EvalJs here, not ExecJs, because EvalJs waits for promises to
// resolve.
EXPECT_EQ(
"Success",
EvalJs(shell(), JsReplace(command, IssuanceOriginFromHost("a.test"))));
// 4) Verify that we received three successful events.
WaitForMatchingNotification("Network.trustTokenOperationDone",
okStatusMatcher);
WaitForMatchingNotification("Network.trustTokenOperationDone",
okStatusMatcher);
WaitForMatchingNotification("Network.trustTokenOperationDone",
okStatusMatcher);
}
IN_PROC_BROWSER_TEST_F(DevToolsTrustTokenBrowsertest, IframeEndToEnd) {
ProvideRequestHandlerKeyCommitmentsToNetworkService({"a.test"});
// 1) Navigate to a test site.
GURL start_url = server_.GetURL("a.test", "/page_with_iframe.html");
ASSERT_TRUE(NavigateToURL(shell(), start_url));
// 2) Open DevTools and enable Network domain.
Attach();
SendCommand("Network.enable", std::make_unique<base::DictionaryValue>());
// 3) Request and redeem a token, then use the redeemed token in a Signing
// request.
auto execute_op_via_iframe = [&](base::StringPiece path,
base::StringPiece trust_token) {
// It's important to set the trust token arguments before updating src, as
// the latter triggers a load.
EXPECT_TRUE(ExecJs(
shell(), JsReplace(
R"( const myFrame = document.getElementById('test_iframe');
myFrame.trustToken = $1;
myFrame.src = $2;)",
trust_token, path)));
TestNavigationObserver load_observer(shell()->web_contents());
load_observer.WaitForNavigationFinished();
};
execute_op_via_iframe("/issue", R"({"type": "token-request"})");
execute_op_via_iframe("/redeem", R"({"type": "token-redemption"})");
execute_op_via_iframe("/sign", JsReplace(
R"({"type": "send-redemption-record",
"signRequestData": "include", "issuers": [$1]})",
IssuanceOriginFromHost("a.test")));
// 4) Verify that we received three successful events.
WaitForMatchingNotification("Network.trustTokenOperationDone",
okStatusMatcher);
WaitForMatchingNotification("Network.trustTokenOperationDone",
okStatusMatcher);
WaitForMatchingNotification("Network.trustTokenOperationDone",
okStatusMatcher);
}
// When the server rejects issuance, DevTools gets a failed notification.
IN_PROC_BROWSER_TEST_F(DevToolsTrustTokenBrowsertest,
FailedIssuanceFiresFailedOperationEvent) {
TrustTokenRequestHandler::Options options;
options.issuance_outcome =
TrustTokenRequestHandler::ServerOperationOutcome::kUnconditionalFailure;
request_handler_.UpdateOptions(std::move(options));
// 1) Navigate to a test site.
ProvideRequestHandlerKeyCommitmentsToNetworkService({"a.test"});
GURL start_url = server_.GetURL("a.test", "/title1.html");
ASSERT_TRUE(NavigateToURL(shell(), start_url));
// 2) Open DevTools and enable Network domain.
Attach();
SendCommand("Network.enable", std::make_unique<base::DictionaryValue>());
// 3) Request some Trust Tokens.
EXPECT_EQ("OperationError", EvalJs(shell(), R"(fetch('/issue',
{ trustToken: { type: 'token-request' } })
.then(()=>'Success').catch(err => err.name); )"));
// 4) Verify that we received an Trust Token operation failed event.
WaitForMatchingNotification(
"Network.trustTokenOperationDone",
base::BindRepeating(
&MatchStatus,
protocol::Network::TrustTokenOperationDone::StatusEnum::BadResponse));
} }
} // namespace content } // namespace content
...@@ -2661,5 +2661,67 @@ void NetworkHandler::LoadNetworkResource( ...@@ -2661,5 +2661,67 @@ void NetworkHandler::LoadNetworkResource(
callback->sendFailure(Response::ServerError("Target not supported")); callback->sendFailure(Response::ServerError("Target not supported"));
} }
namespace {
String GetTrustTokenOperationStatus(
network::mojom::TrustTokenOperationStatus status) {
switch (status) {
case network::mojom::TrustTokenOperationStatus::kOk:
return protocol::Network::TrustTokenOperationDone::StatusEnum::Ok;
case network::mojom::TrustTokenOperationStatus::kInvalidArgument:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
InvalidArgument;
case network::mojom::TrustTokenOperationStatus::kFailedPrecondition:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
FailedPrecondition;
case network::mojom::TrustTokenOperationStatus::kResourceExhausted:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
ResourceExhausted;
case network::mojom::TrustTokenOperationStatus::kAlreadyExists:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
AlreadyExists;
case network::mojom::TrustTokenOperationStatus::kUnavailable:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
Unavailable;
case network::mojom::TrustTokenOperationStatus::kBadResponse:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
BadResponse;
case network::mojom::TrustTokenOperationStatus::kInternalError:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
InternalError;
case network::mojom::TrustTokenOperationStatus::kUnknownError:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
UnknownError;
case network::mojom::TrustTokenOperationStatus::
kOperationSuccessfullyFulfilledLocally:
return protocol::Network::TrustTokenOperationDone::StatusEnum::
FulfilledLocally;
}
}
} // namespace
void NetworkHandler::OnTrustTokenOperationDone(
const std::string& devtools_request_id,
const network::mojom::TrustTokenOperationResult& result) {
if (!enabled_)
return;
Maybe<String> top_level_origin;
if (result.top_level_origin) {
top_level_origin = result.top_level_origin->Serialize();
}
Maybe<String> issuer;
if (result.issuer) {
issuer = result.issuer->Serialize();
}
frontend()->TrustTokenOperationDone(
GetTrustTokenOperationStatus(result.status),
GetTrustTokenOperationType(result.type), devtools_request_id,
std::move(top_level_origin), std::move(issuer),
result.issued_token_count);
}
} // namespace protocol } // namespace protocol
} // namespace content } // namespace content
...@@ -214,6 +214,9 @@ class NetworkHandler : public DevToolsDomainHandler, ...@@ -214,6 +214,9 @@ class NetworkHandler : public DevToolsDomainHandler,
const net::CookieAndLineAccessResultList& response_cookie_list, const net::CookieAndLineAccessResultList& response_cookie_list,
const std::vector<network::mojom::HttpRawHeaderPairPtr>& response_headers, const std::vector<network::mojom::HttpRawHeaderPairPtr>& response_headers,
const base::Optional<std::string>& response_headers_text); const base::Optional<std::string>& response_headers_text);
void OnTrustTokenOperationDone(
const std::string& devtools_request_id,
const network::mojom::TrustTokenOperationResult& result);
bool enabled() const { return enabled_; } bool enabled() const { return enabled_; }
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
{ {
"domain": "Network", "domain": "Network",
"include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "setExtraHTTPHeaders", "canEmulateNetworkConditions", "emulateNetworkConditions", "setBypassServiceWorker", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setCacheDisabled", "takeResponseBodyForInterceptionAsStream", "getSecurityIsolationStatus", "loadNetworkResource"], "include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "setExtraHTTPHeaders", "canEmulateNetworkConditions", "emulateNetworkConditions", "setBypassServiceWorker", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setCacheDisabled", "takeResponseBodyForInterceptionAsStream", "getSecurityIsolationStatus", "loadNetworkResource"],
"include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted", "signedExchangeReceived", "requestWillBeSentExtraInfo", "responseReceivedExtraInfo"], "include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted", "signedExchangeReceived", "requestWillBeSentExtraInfo", "responseReceivedExtraInfo", "trustTokenOperationDone"],
"async": ["clearBrowserCookies", "clearBrowserCache", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "continueInterceptedRequest", "getResponseBodyForInterception", "takeResponseBodyForInterceptionAsStream", "loadNetworkResource"] "async": ["clearBrowserCookies", "clearBrowserCache", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "continueInterceptedRequest", "getResponseBodyForInterception", "takeResponseBodyForInterceptionAsStream", "loadNetworkResource"]
}, },
{ {
......
...@@ -267,8 +267,8 @@ void NetworkServiceClient::OnTrustTokenOperationDone( ...@@ -267,8 +267,8 @@ void NetworkServiceClient::OnTrustTokenOperationDone(
int32_t routing_id, int32_t routing_id,
const std::string& devtools_request_id, const std::string& devtools_request_id,
network::mojom::TrustTokenOperationResultPtr result) { network::mojom::TrustTokenOperationResultPtr result) {
// TODO(crbug.com/1126824): Implement by forwarding to a devtools_instrumentation::OnTrustTokenOperationDone(
// devtools_instrumentation method. process_id, routing_id, devtools_request_id, std::move(result));
} }
} // namespace content } // namespace content
...@@ -5553,6 +5553,36 @@ domain Network ...@@ -5553,6 +5553,36 @@ domain Network
# available, such as in the case of HTTP/2 or QUIC. # available, such as in the case of HTTP/2 or QUIC.
optional string headersText optional string headersText
# Fired exactly once for each Trust Token operation. Depending on
# the type of the operation and whether the operation succeeded or
# failed, the event is fired before the corresponding request was sent
# or after the response was received.
experimental event trustTokenOperationDone
parameters
# Detailed success or error status of the operation.
# 'AlreadyExists' also signifies a successful operation, as the result
# of the operation already exists und thus, the operation was abort
# preemptively (e.g. a cache hit).
enum status
Ok
InvalidArgument
FailedPrecondition
ResourceExhausted
AlreadyExists
Unavailable
BadResponse
InternalError
UnknownError
FulfilledLocally
TrustTokenOperationType type
RequestId requestId
# Top level origin. The context in which the operation was attempted.
optional string topLevelOrigin
# Origin of the issuer in case of a "Issuance" or "Redemption" operation.
optional string issuerOrigin
# The number of obtained Trust Tokens on a successful "Issuance" operation.
optional integer issuedTokenCount
experimental type CrossOriginOpenerPolicyValue extends string experimental type CrossOriginOpenerPolicyValue extends string
enum enum
SameOrigin SameOrigin
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment