Commit 8f6be972 authored by Annie Sullivan's avatar Annie Sullivan Committed by Commit Bot

Add LayoutShiftTrackerSimTest.OOPIFSubframeWeighting test.


BUG=943668

Change-Id: I0157278fa1b7f8f5a0358e8811f21d4be1dd990c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2208026Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Commit-Queue: Annie Sullivan <sullivan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#772334}
parent a751e8c7
<script src="resources/testharness.js"></script>
<script src="/script/async_buffer.js"></script>
<script>
// Tell testharness.js to not wait for 'real' tests; we only want
// testharness.js for its assertion helpers.
......@@ -6,52 +7,6 @@ setup({'output': false});
</script>
<script>
// 'AsyncBuffer' serves as a helper to buffer LCP reports asynchronously.
class AsyncBuffer {
constructor() {
// 'pending' is an array that will buffer entries reported through the
// PerformanceObserver and can be collected with 'pop'.
this.pending = [];
// 'resolve_fn' is a reference to the 'resolve' function of a
// Promise that blocks for new entries to arrive via 'push()'. Calling
// the function resolves the promise and unblocks calls to 'pop()'.
this.resolve_fn = null;
}
// Concatenates the given 'entries' list to this AsyncBuffer.
push(entries) {
if (entries.length == 0) {
throw new Error("Must not push an empty list of entries!");
}
this.pending = this.pending.concat(entries);
// If there are calls to 'pop' that are blocked waiting for items, signal
// that they can continue.
if (this.resolve_fn != null) {
this.resolve_fn();
this.resolve_fn = null;
}
}
// Takes the current pending entries from this AsyncBuffer. If there are no
// entries queued already, this will block until some show up.
async pop() {
if (this.pending.length == 0) {
// Need to instantiate a promise to block on. The next call to 'push'
// will resolve the promise once it has queued the entries.
await new Promise(resolve => {
this.resolve_fn = resolve;
});
}
assert_true(this.pending.length > 0);
const result = this.pending;
this.pending = [];
return result;
}
}
const buffer = new AsyncBuffer();
const po = new PerformanceObserver(entryList => {
buffer.push(entryList.getEntries());
......
<script src="/resources/testharness.js"></script>
<script src="/script/async_buffer.js"></script>
<script>
var buffer = new AsyncBuffer();
window.addEventListener('message', function() {
buffer.push(event.data);
}, false);
const block_for_next_cls_subframe = async () => {
return buffer.pop().then(seen_events => {
// This test case assumes each CLS entry is handled before the next could
// possibly be generated.
return seen_events[0];
});
};
// Shifts the div in the subframe vertically by 60px
const verticalSubframeShift = () => {
document.getElementById('i').contentWindow.postMessage('shift', '*');
};
// Shifts the iframe right by 50% of the viewport.
const horizontalIframeShift = () => {
document.getElementById('i').style.left = '600px';
document.getElementById('i').contentWindow.postMessage('unshift', '*');
};
const run_test = async () => {
// This test exerciess the following scenario
// - Shift a div in an iframe vertically
// - Report the layout shift to the test
// - Shift the div back, and at the same time, shift the iframe
// horizontally halfway outside the viewport.
// - Report the layout shift to the test
verticalSubframeShift();
const cls_0 = await block_for_next_cls_subframe();
horizontalIframeShift();
const cls_1 = await block_for_next_cls_subframe();
// Now that we've run through the scenario and collected our measurements,
// return them in a structure that the C++ side can easily query.
let output = [
{'score': cls_0.value},
{'score': cls_1.value}
];
return output;
};
</script>
<style>
#i {
border: 0;
position: absolute;
left: 0;
top: 0;
background-color: pink;
}
</style>
<iframe id="i" width="400" height="300"
src="/cross-site/foo.com/layout-instability/sub_frame.html"></iframe>
\ No newline at end of file
<script src="/resources/testharness.js"></script>
<script src="/script/async_buffer.js"></script>
<script>
var buffer = new AsyncBuffer();
const po = new PerformanceObserver(entryList => {
buffer.push(entryList.getEntries());
});
po.observe({type: 'layout-shift', buffered: true});
// The main frame sends a message to shift the 'j' div, and then
// this frame does the shift and then sends a postmessage with
// the resulting layout shift entry.
function receiveMessage(event) {
var top;
if (event.data == 'shift') {
top = '60px';
} else if (event.data == 'unshift') {
top = '';
} else {
return;
}
document.getElementById('j').style.top = top;
buffer.pop().then(seen_events => {
// This test case assumes each CLS entry is handled before the next could
// possibly be generated.
window.parent.postMessage({'value': seen_events[0].value}, '*');
});
}
window.addEventListener("message", receiveMessage, false);
</script>
<style>
#j {
position: relative;
width: 300px;
height: 100px;
background-color: purple;
}
</style>
<div id="j"></div>
\ No newline at end of file
// Copyright 2020 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.
// 'AsyncBuffer' serves as a helper to buffer PerformanceObserver reports
// asynchronously.
class AsyncBuffer {
constructor() {
// 'pending' is an array that will buffer entries reported through the
// PerformanceObserver and can be collected with 'pop'.
this.pending = [];
// 'resolve_fn' is a reference to the 'resolve' function of a
// Promise that blocks for new entries to arrive via 'push()'. Calling
// the function resolves the promise and unblocks calls to 'pop()'.
this.resolve_fn = null;
}
// Concatenates the given 'entries' list to this AsyncBuffer.
push(entries) {
if (entries.length == 0) {
throw new Error("Must not push an empty list of entries!");
}
this.pending = this.pending.concat(entries);
// If there are calls to 'pop' that are blocked waiting for items, signal
// that they can continue.
if (this.resolve_fn != null) {
this.resolve_fn();
this.resolve_fn = null;
}
}
// Takes the current pending entries from this AsyncBuffer. If there are no
// entries queued already, this will block until some show up.
async pop() {
if (this.pending.length == 0) {
// Need to instantiate a promise to block on. The next call to 'push'
// will resolve the promise once it has queued the entries.
await new Promise(resolve => {
this.resolve_fn = resolve;
});
}
assert_true(this.pending.length > 0);
const result = this.pending;
this.pending = [];
return result;
}
}
\ No newline at end of file
......@@ -135,3 +135,49 @@ IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_Enclosure) {
IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_MaxImpact) {
RunWPT("sources-maximpact.html", true);
}
IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, OOPIFSubframeWeighting) {
Start();
StartTracing({"loading"});
Load("/layout-instability/main_frame.html");
content::EvalJsResult result = EvalJs(web_contents(), "run_test()");
EXPECT_EQ("", result.error);
// Verify that the JS API yielded two CLS reports for the subframe.
const auto& list = result.value.GetList();
base::Optional<double> cls_values[2];
for (size_t i = 0; i < 2; i++) {
cls_values[i] = list[i].FindDoublePath("score");
EXPECT_TRUE(cls_values[i].has_value());
}
EXPECT_EQ(0.4 * (60.0 / 400.0), cls_values[0].value())
<< "The first shift value should be 300 * (100 + 60) * (60 / 400) / "
"(default viewport size 800 * 600)";
EXPECT_EQ(0.4 * (60.0 / 400.0), cls_values[0].value())
<< "The second shift value should be 300 * (100 + 60) * (60 / 400) / "
"(default viewport size 800 * 600)";
// Need to navigate away from the test html page to force metrics to get
// flushed/synced.
ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
// Check Trace Events.
std::unique_ptr<TraceAnalyzer> analyzer = StopTracingAndAnalyze();
TraceEventVector events;
analyzer->FindEvents(Query::EventNameIs("LayoutShift"), &events);
EXPECT_EQ(3ul, events.size());
std::unique_ptr<Value> data;
events[0]->GetArgAsValue("data", &data);
EXPECT_EQ(0.4 * (60.0 / 400.0), *data->FindDoubleKey("score"));
// Check UKM.
ExpectUKMPageLoadMetricNear(
PageLoad::kLayoutInstability_CumulativeShiftScoreName,
LayoutShiftUkmValue(0.35), 5);
// Check UMA.
auto samples = histogram_tester().GetAllSamples(
"PageLoad.LayoutInstability.CumulativeShiftScore");
EXPECT_EQ(1ul, samples.size());
}
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