Skip to content

Commit

Permalink
Expose contentEncoding in PerformanceResourceTiming
Browse files Browse the repository at this point in the history
This CL introduce a contentEncoding field to Performance resource timing
object. This field is behind a feature flag.

PR to resource timing specification:
w3c/resource-timing#411
PR to fetch specification:
whatwg/fetch#1796

Bug: 327941462
Change-Id: I70cad190fe658fb3dbf8b401ff8393bc1d0782f0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6098321
Commit-Queue: Guohui Deng <guohuideng@microsoft.com>
Reviewed-by: Noam Rosenthal <nrosenthal@chromium.org>
Reviewed-by: Matthew Denton <mpdenton@chromium.org>
Reviewed-by: Yoav Weiss (@Shopify) <yoavweiss@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1407331}
  • Loading branch information
Guohui Deng authored and chromium-wpt-export-bot committed Jan 16, 2025
1 parent 9db3ea2 commit 1df2c3e
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 0 deletions.
36 changes: 36 additions & 0 deletions navigation-timing/content-encoding.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta name="variant" , content="?pipe=gzip">
<title>contentEncoding in navigation timing</title>
<link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
<link rel="help" href="http://www.w3.org/TR/navigation-timing/#sec-navigation-timing-interface" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<script>
async_test(function (t) {
var observer = new PerformanceObserver(
t.step_func(function (entryList) {
assert_equals(entryList.getEntries()[0].contentEncoding, "gzip",
"Expected contentEncoding to be gzip.");
observer.disconnect();
t.done();
})
);
observer.observe({ entryTypes: ["navigation"] });

}, "contentEncoding should be gzip.");
</script>
</head>

<body>
<h1>
Description</h1>
<p>
This test validates that when a html is compressed with gzip, navigation timing reports contentEncoding as gzip</p>
</body>

</html>
107 changes: 107 additions & 0 deletions resource-timing/content-encoding.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>contentEncoding in resource timing</title>
<link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming" />
<script src="/common/get-host-info.sub.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
<script>

const { ORIGIN, REMOTE_ORIGIN } = get_host_info();

const run_same_origin_test = (path, contentEncoding) => {
const url = new URL(path, ORIGIN);
attribute_test(
fetch, url,
entry => {
assert_equals(entry.contentEncoding, contentEncoding,
`run_same_origin_test failed, unexpected contentEncoding value.`);
});
}

run_same_origin_test("/resource-timing/resources/compressed-data.py?content_encoding=dcb", "dcb");
run_same_origin_test("/resource-timing/resources/compressed-data.py?content_encoding=dcz", "dcz");
run_same_origin_test("/resource-timing/resources/gzip_xml.py", "gzip");
run_same_origin_test("/resource-timing/resources/foo.text.br", "br");
run_same_origin_test("/resource-timing/resources/foo.text.gz", "gzip");
run_same_origin_test("/resource-timing/resources/foo.text.zst", "zstd");
run_same_origin_test("/resource-timing/resources/compressed-js.py?content_encoding=deflate", "deflate");
run_same_origin_test("/resource-timing/resources/compressed-js.py?content_encoding=gzip", "gzip");
run_same_origin_test("/resource-timing/resources/compressed-js.py?content_encoding=identity", "identity");
// Unrecognized content encoding value should be transformed to "unknown".
run_same_origin_test("/resource-timing/resources/compressed-js.py?content_encoding=unrecognizedname", "unknown");


const run_cross_origin_test = (path) => {
const url = new URL(path, REMOTE_ORIGIN);
attribute_test(
load.xhr_async, url,
entry => {
assert_equals(entry.contentEncoding, "",
`run_cross_origin_test failed, contentEncoding should be empty.`);
});
}

run_cross_origin_test("/resource-timing/resources/compressed-data.py?content_encoding=dcb");
run_cross_origin_test("/resource-timing/resources/gzip_xml.py");
run_cross_origin_test("/resource-timing/resources/compressed-data.py?content_encoding=dcz");
run_cross_origin_test("/resource-timing/resources/foo.text.br");
run_cross_origin_test("/resource-timing/resources/foo.text.gz");
run_cross_origin_test("/resource-timing/resources/foo.text.zst");
run_cross_origin_test("/resource-timing/resources/compressed-js.py?content_encoding=deflate");
run_cross_origin_test("/resource-timing/resources/compressed-js.py?content_encoding=gzip");

const run_cross_origin_allowed_test = (path, contentEncoding) => {
const url = new URL(path, REMOTE_ORIGIN);
url.searchParams.set("allow_origin", ORIGIN);
attribute_test(
load.xhr_async, url,
entry => {
assert_equals(entry.contentEncoding, contentEncoding,
`run_cross_origin_allowed_test failed, unexpected contentEncoding value.`);
});
}

run_cross_origin_allowed_test("/resource-timing/resources/compressed-data.py?content_encoding=dcb", "dcb");
run_cross_origin_allowed_test("/resource-timing/resources/compressed-data.py?content_encoding=dcz", "dcz");
run_cross_origin_allowed_test("/resource-timing/resources/gzip_xml.py", "gzip");
run_cross_origin_allowed_test("/resource-timing/resources/compressed-js.py?content_encoding=deflate", "deflate");
run_cross_origin_allowed_test("/resource-timing/resources/compressed-js.py?content_encoding=gzip", "gzip");

// Content-Encoding for iframes is empty when cross origin redirects are present.
const multiRedirect = new URL(`${ORIGIN}/resource-timing/resources/multi_redirect.py`);
multiRedirect.searchParams.append("page_origin", ORIGIN);
multiRedirect.searchParams.append("cross_origin", REMOTE_ORIGIN);
multiRedirect.searchParams.append("final_resource", "/resource-timing/resources/compressed-js.py?content_encoding=gzip");
attribute_test(load.iframe, multiRedirect, (entry) => {
assert_equals(entry.contentEncoding, "",
`content-encoding should be empty for iframes having cross origin redirects`);
});


// Content-Encoding for iframes is exposed for same origin redirects.
const redirectCORS = new URL(`${ORIGIN}/resource-timing/resources/redirect-cors.py`);
const dest = `${ORIGIN}/resource-timing/resources/compressed-js.py?content_encoding=gzip`;
redirectCORS.searchParams.append("location", dest)
attribute_test(load.iframe, redirectCORS, (entry) => {
assert_equals(entry.contentEncoding, "gzip",
`content-encoding should be exposed for iframes having only same origin redirects`);
});

</script>
</head>

<body>
<h1>
Description</h1>
<p>
This test validates contentEncoding in resource timing.</p>
</body>

</html>
33 changes: 33 additions & 0 deletions resource-timing/resources/compressed-data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
def main(request, response):
response.headers.set(b"Content-Type", b"text/plain")

# `dcb_data` and `dcz_data` are generated using the following commands:
#
# $ echo "This is a test dictionary." > /tmp/dict
# $ echo -n "This is compressed test data using a test dictionary" \
# > /tmp/data
#
# $ echo -en '\xffDCB' > /tmp/out.dcb
# $ openssl dgst -sha256 -binary /tmp/dict >> /tmp/out.dcb
# $ brotli --stdout -D /tmp/dict /tmp/data >> /tmp/out.dcb
# $ xxd -p /tmp/out.dcb | tr -d '\n' | sed 's/\(..\)/\\x\1/g'
dcb_data = b"\xff\x44\x43\x42\x53\x96\x9b\xcf\x5e\x96\x0e\x0e\xdb\xf0\xa4\xbd\xde\x6b\x0b\x3e\x93\x81\xe1\x56\xde\x7f\x5b\x91\xce\x83\x91\x62\x42\x70\xf4\x16\xa1\x98\x01\x80\x62\xa4\x4c\x1d\xdf\x12\x84\x8c\xae\xc2\xca\x60\x22\x07\x6e\x81\x05\x14\xc9\xb7\xc3\x44\x8e\xbc\x16\xe0\x15\x0e\xec\xc1\xee\x34\x33\x3e\x0d"
# $ echo -en '\x5e\x2a\x4d\x18\x20\x00\x00\x00' > /tmp/out.dcz
# $ openssl dgst -sha256 -binary /tmp/dict >> /tmp/out.dcz
# $ zstd -D /tmp/dict -f -o /tmp/tmp.zstd /tmp/data
# $ cat /tmp/tmp.zstd >> /tmp/out.dcz
# $ xxd -p /tmp/out.dcz | tr -d '\n' | sed 's/\(..\)/\\x\1/g'
dcz_data = b"\x5e\x2a\x4d\x18\x20\x00\x00\x00\x53\x96\x9b\xcf\x5e\x96\x0e\x0e\xdb\xf0\xa4\xbd\xde\x6b\x0b\x3e\x93\x81\xe1\x56\xde\x7f\x5b\x91\xce\x83\x91\x62\x42\x70\xf4\x16\x28\xb5\x2f\xfd\x24\x34\xf5\x00\x00\x98\x63\x6f\x6d\x70\x72\x65\x73\x73\x65\x64\x61\x74\x61\x20\x75\x73\x69\x6e\x67\x03\x00\x59\xf9\x73\x54\x46\x27\x26\x10\x9e\x99\xf2\xbc"

if b'content_encoding' in request.GET:
content_encoding = request.GET.first(b"content_encoding")
response.headers.set(b"Content-Encoding", content_encoding)
if content_encoding == b"dcb":
# Send the pre compressed file
response.content = dcb_data
if content_encoding == b"dcz":
# Send the pre compressed file
response.content = dcz_data

if b'allow_origin' in request.GET:
response.headers.set(b'access-control-allow-origin', request.GET.first(b'allow_origin'))
29 changes: 29 additions & 0 deletions resource-timing/resources/compressed-js.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import os.path
import zlib
import gzip

def read(file):
path = os.path.join(os.path.dirname(__file__), file)
return open(path, u"rb").read()

def main(request, response):
response.headers.set(b"Content-Type", b"text/javascript")

if b'allow_origin' in request.GET:
response.headers.set(
b'access-control-allow-origin',
request.GET.first(b'allow_origin'))

if b'content_encoding' in request.GET:
content_encoding = request.GET.first(b"content_encoding")
response.headers.set(b"Content-Encoding", content_encoding)
if content_encoding == b"deflate":
response.content = zlib.compress(read(u"./dummy.js"))
if content_encoding == b"gzip":
response.content = gzip.compress(read(u"./dummy.js"))
if content_encoding == b"identity":
# Uncompressed
response.content = read(u"./dummy.js")
if content_encoding == b"unrecognizedname":
# Just return something
response.content = gzip.compress(read(u"./dummy.js"))
1 change: 1 addition & 0 deletions resource-timing/resources/dummy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// A dummy js file to be compressed and transferred.
Binary file added resource-timing/resources/foo.text.br
Binary file not shown.
2 changes: 2 additions & 0 deletions resource-timing/resources/foo.text.br.headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Content-type: text/plain
Content-Encoding: br
Binary file added resource-timing/resources/foo.text.gz
Binary file not shown.
2 changes: 2 additions & 0 deletions resource-timing/resources/foo.text.gz.headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Content-type: text/plain
Content-Encoding: gzip
Binary file added resource-timing/resources/foo.text.zst
Binary file not shown.
2 changes: 2 additions & 0 deletions resource-timing/resources/foo.text.zst.headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Content-type: text/plain
Content-Encoding: zstd
3 changes: 3 additions & 0 deletions resource-timing/resources/gzip_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ def main(request, response):
(b"Content-Encoding", b"gzip"),
(b"Content-Length", len(output))]

if b'allow_origin' in request.GET:
headers.append((b'access-control-allow-origin', request.GET.first(b'allow_origin')))

return headers, output

0 comments on commit 1df2c3e

Please sign in to comment.