Commit eb493883 authored by Yoav Weiss's avatar Yoav Weiss Committed by Chromium LUCI CQ

[resource-timing] ResourceTimingInfo for failed navigations

Failed navigations currently don't get a ResourceTiming entry.
This CL changes that by properly reporting them.

Bug: 1131929, 1105875
Change-Id: I0808f35e1b0d596c2bafa7630ed873c947254c5e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2567925
Commit-Queue: Yoav Weiss <yoavweiss@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834675}
parent c346c666
......@@ -3403,6 +3403,12 @@ void RenderFrameImpl::CommitFailedNavigation(
WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
"UTF-8", error_html);
navigation_params->unreachable_url = error.url();
if (commit_params->redirects.size()) {
navigation_params->pre_redirect_url_for_failed_navigations =
commit_params->redirects[0];
} else {
navigation_params->pre_redirect_url_for_failed_navigations = error.url();
}
// The error page load (not to confuse with a failed load of original page)
// was not initiated through BeginNavigation, therefore
......
......@@ -1037,6 +1037,7 @@ void RenderThreadImpl::RegisterSchemes() {
WebString error_scheme(WebString::FromASCII(kChromeErrorScheme));
WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(error_scheme);
WebSecurityPolicy::RegisterURLSchemeAsNotAllowingJavascriptURLs(error_scheme);
WebSecurityPolicy::RegisterURLSchemeAsError(error_scheme);
// googlechrome:
WebString google_chrome_scheme(WebString::FromASCII(kGoogleChromeScheme));
......
......@@ -238,6 +238,9 @@ struct BLINK_EXPORT WebNavigationParams {
// history item will contain this URL instead of request's URL.
// This URL can be retrieved through WebDocumentLoader::UnreachableURL.
WebURL unreachable_url;
// If non-null, this gives the pre-redirect URL in case that we're committing
// a failed navigation.
WebURL pre_redirect_url_for_failed_navigations;
// The IP address space from which this document was loaded.
// https://wicg.github.io/cors-rfc1918/#address-space
......
......@@ -127,6 +127,9 @@ class WebSecurityPolicy {
BLINK_EXPORT static void RegisterURLSchemeAsAllowedForReferrer(
const WebString&);
// Registers an URL scheme as an error page.
BLINK_EXPORT static void RegisterURLSchemeAsError(const WebString&);
private:
WebSecurityPolicy() = delete;
};
......
......@@ -140,4 +140,8 @@ void WebSecurityPolicy::RegisterURLSchemeAsAllowedForReferrer(
SchemeRegistry::RegisterURLSchemeAsAllowedForReferrer(scheme);
}
void WebSecurityPolicy::RegisterURLSchemeAsError(const WebString& scheme) {
SchemeRegistry::RegisterURLSchemeAsError(scheme);
}
} // namespace blink
......@@ -14,6 +14,7 @@
#include "third_party/blink/renderer/core/frame/web_remote_frame_impl.h"
#include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
#include "third_party/blink/renderer/core/timing/performance.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
namespace blink {
......
......@@ -170,6 +170,8 @@ DocumentLoader::DocumentLoader(
origin_policy_(params_->origin_policy),
requestor_origin_(params_->requestor_origin),
unreachable_url_(params_->unreachable_url),
pre_redirect_url_for_failed_navigations_(
params_->pre_redirect_url_for_failed_navigations),
ip_address_space_(params_->ip_address_space),
grant_load_local_resources_(params_->grant_load_local_resources),
force_fetch_cache_mode_(params_->force_fetch_cache_mode),
......@@ -197,6 +199,9 @@ DocumentLoader::DocumentLoader(
load_type_ == WebFrameLoadType::kReplaceCurrentItem &&
(!frame_->Loader().Opener() || !url_.IsEmpty())),
data_received_(false),
is_error_page_for_failed_navigation_(
SchemeRegistry::ShouldTreatURLSchemeAsError(
response_.ResponseUrl().Protocol())),
// The input CSP is null when the CSP check done in the FrameLoader failed
content_security_policy_(
content_security_policy
......@@ -646,7 +651,7 @@ void DocumentLoader::BodyLoadingFinished(
probe::ToCoreProbeSink(GetFrame()), main_resource_identifier_, this,
completion_time, total_encoded_data_length, total_decoded_body_length,
should_report_corb_blocking);
if (response_.IsHTTP()) {
if (response_.IsHTTP() || is_error_page_for_failed_navigation_) {
// The response is being copied here to pass the Encoded and Decoded
// sizes.
// TODO(yoav): copy the sizes info directly.
......@@ -1827,10 +1832,16 @@ void DocumentLoader::CommitNavigation() {
document->SetDeferredCompositorCommitIsAllowed(false);
}
if (response_.IsHTTP() && navigation_timing_info_) {
if ((response_.IsHTTP() || is_error_page_for_failed_navigation_) &&
navigation_timing_info_) {
// The response is being copied here to pass the ServerTiming info.
// TODO(yoav): copy the ServerTiming info directly.
navigation_timing_info_->SetFinalResponse(response_);
// Make sure we're properly reporting error documents.
if (is_error_page_for_failed_navigation_) {
navigation_timing_info_->SetInitialURL(
pre_redirect_url_for_failed_navigations_);
}
}
{
......
......@@ -449,6 +449,7 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
base::Optional<WebOriginPolicy> origin_policy_;
const scoped_refptr<const SecurityOrigin> requestor_origin_;
const KURL unreachable_url_;
const KURL pre_redirect_url_for_failed_navigations_;
std::unique_ptr<WebNavigationBodyLoader> body_loader_;
const network::mojom::IPAddressSpace ip_address_space_ =
network::mojom::IPAddressSpace::kUnknown;
......@@ -482,6 +483,7 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
bool is_client_redirect_;
bool replaces_current_history_item_;
bool data_received_;
const bool is_error_page_for_failed_navigation_;
const Member<ContentSecurityPolicy> content_security_policy_;
mojo::Remote<mojom::blink::ContentSecurityNotifier>
......
......@@ -102,6 +102,7 @@ class URLSchemesRegistry final {
content_security_policy_bypassing_schemes;
URLSchemesSet secure_context_bypassing_schemes;
URLSchemesSet allowed_in_referrer_schemes;
URLSchemesSet error_schemes;
URLSchemesSet wasm_eval_csp_schemes;
private:
......@@ -392,6 +393,18 @@ bool SchemeRegistry::ShouldTreatURLSchemeAsAllowedForReferrer(
return GetURLSchemesRegistry().allowed_in_referrer_schemes.Contains(scheme);
}
void SchemeRegistry::RegisterURLSchemeAsError(const String& scheme) {
DCHECK_EQ(scheme, scheme.LowerASCII());
GetMutableURLSchemesRegistry().error_schemes.insert(scheme);
}
bool SchemeRegistry::ShouldTreatURLSchemeAsError(const String& scheme) {
DCHECK_EQ(scheme, scheme.LowerASCII());
if (scheme.IsEmpty())
return false;
return GetURLSchemesRegistry().error_schemes.Contains(scheme);
}
void SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
const String& scheme,
PolicyAreas policy_areas) {
......
......@@ -132,6 +132,10 @@ class PLATFORM_EXPORT SchemeRegistry {
static void RemoveURLSchemeAsAllowedForReferrer(const String& scheme);
static bool ShouldTreatURLSchemeAsAllowedForReferrer(const String& scheme);
// Schemes used for internal error pages, for failed navigations.
static void RegisterURLSchemeAsError(const String&);
static bool ShouldTreatURLSchemeAsError(const String& scheme);
// Allow resources from some schemes to load on a page, regardless of its
// Content Security Policy.
enum PolicyAreas : uint32_t {
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - test that unsuccessful iframes create entries</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
test(() => {
const entries = performance.getEntriesByType("resource");
assert_equals(entries.length, 3, "Precondition - Entries for blocking scripts fired");
}, "Precondition");
const run_test = async (t, url, csp) => {
const setPerformanceObserver = new Promise(resolve => {
const po = new PerformanceObserver(resolve);
po.observe({type: "resource"});
});
const timeout = new Promise(resolve => t.step_timeout(resolve, 1000));
const frame = document.createElement("iframe");
frame.src = url;
if (csp) {
frame.csp = csp;
}
document.body.appendChild(frame);
const list = await Promise.race([setPerformanceObserver, timeout]);
assert_equals(typeof(list), "object", "No iframe entry was fired");
const entries = list.getEntriesByName(url);
assert_equals(entries.length, 1);
}
const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTPS_PORT} = get_host_info();
const nonexistent_url = "https://nonexistent." + ORIGINAL_HOST + ":" + HTTPS_PORT + "/";
promise_test(t => {
return run_test(t, nonexistent_url);
}, "Test iframe from non-existent host");
promise_test(t => {
const url = new URL("resources/fake_responses.py?redirect=" + nonexistent_url, location.href);
return run_test(t, url.toString());
}, "Test iframe redirecting to non-existent host");
const csp_directive = "default-src 'none'";
promise_test(t => {
const url = new URL("/resource-timing/resources/csp-default-none.html", location.href);
return run_test(t, url, csp_directive);
}, "Same-origin iframe that complies with CSP attribute gets reported");
promise_test(t => {
const url = new URL("/resource-timing/resources/green_frame.htm", location.href);
return run_test(t, url.toString(), csp_directive);
}, "Same-origin iframe that doesn't comply with CSP attribute gets reported");
promise_test(t => {
const url = new URL("/resource-timing/resources/csp-default-none.html", REMOTE_ORIGIN);
return run_test(t, url.toString(), csp_directive);
}, "Cross-origin iframe that complies with CSP attribute gets reported");
promise_test(t => {
const url = new URL("/resource-timing/resources/green_frame.htm", REMOTE_ORIGIN);
return run_test(t, url.toString(), csp_directive);
}, "Cross-origin iframe that doesn't comply with CSP attribute gets reported");
</script>
</body>
</html>
<!DOCTYPE html>
<meta charset="utf-8">
<title>empty page</title>
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