0

Third reland of Add LayoutInstabilityTest.OOPIFSubframeWeighting.

Refactor test to focus on subframe; print window width/height.

Bug: 943668
Change-Id: I28f9c66350f709fa792b242334d117255c36dc1e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2227184
Reviewed-by: Steve Kobes <skobes@chromium.org>
Commit-Queue: Annie Sullivan <sullivan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#774591}
This commit is contained in:
Annie Sullivan
2020-06-03 13:13:40 +00:00
committed by Commit Bot
parent c0ea25a2f9
commit e1f9cdbe64
5 changed files with 189 additions and 46 deletions
chrome/browser/page_load_metrics/integration_tests

@ -1,4 +1,5 @@
<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());

@ -0,0 +1,32 @@
<script>
// This debugging statement is to help catch issues with tests failing due
// to window sizes on different bots being different.
console.log('Main Frame widthxheight = ', window.innerWidth, 'x', window.innerHeight);
function resolveAfterIframeLoad(iframe) {
return new Promise(resolve => {
iframe.addEventListener('load', function() {
return resolve(true);
})
})
}
const wait_for_iframe_load = async () => {
let iframe = document.getElementById('i');
if (iframe.contentWindow.document.readyState == 'complete') {
return true;
}
await resolveAfterIframeLoad(iframe);
}
</script>
<style>
#i {
border: 0;
position: absolute;
left: 0;
top: 0;
background-color: pink;
}
</style>
<iframe id="i" width="400" height="300" src="sub_frame.html"></iframe>

@ -0,0 +1,49 @@
<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});
const block_for_next_cls = 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].value;
});
};
const run_test = async () => {
shiftFrame();
const cls_0 = await block_for_next_cls();
unshiftFrame();
const cls_1 = await block_for_next_cls();
// 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},
{'score': cls_1}
];
return output;
};
function shiftFrame() {
document.getElementById('j').style.top = '60px';
}
function unshiftFrame() {
document.getElementById('j').style.top = '';
}
</script>
<style>
#j {
position: relative;
width: 300px;
height: 100px;
background-color: purple;
}
</style>
<div id="j"></div>

@ -0,0 +1,50 @@
// 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;
}
}

@ -5,6 +5,7 @@
#include "chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h"
#include "base/test/trace_event_analyzer.h"
#include "build/build_config.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "services/metrics/public/cpp/ukm_builders.h"
@ -135,3 +136,59 @@ IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_Enclosure) {
IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_MaxImpact) {
RunWPT("sources-maximpact.html", true);
}
// Fails on Windows 7 only.
#if defined(OS_WIN)
#define MAYBE_OOPIFSubframeWeighting DISABLED_OOPIFSubframeWeighting
#else
#define MAYBE_OOPIFSubframeWeighting OOPIFSubframeWeighting
#endif
IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, MAYBE_OOPIFSubframeWeighting) {
Start();
StartTracing({"loading"});
Load("/layout-instability/main_frame.html");
content::EvalJsResult load_result =
EvalJs(web_contents()->GetAllFrames()[0], "wait_for_iframe_load()");
EXPECT_EQ("", load_result.error);
content::EvalJsResult result =
EvalJs(web_contents()->GetAllFrames()[1], "run_test()");
EXPECT_EQ("", result.error);
// Verify that the JS API yielded two CLS reports for the subframe.
const auto& list = result.value.GetList();
EXPECT_EQ(2ul, list.size());
base::Optional<double> cls_first_value = list[0].FindDoublePath("score");
EXPECT_EQ(0.4 * (60.0 / 400.0), cls_first_value)
<< "The first shift value should be 300 * (100 + 60) * (60 / 400) / "
"(default viewport size 800 * 600)";
base::Optional<double> cls_second_value = list[1].FindDoublePath("score");
EXPECT_EQ(0.4 * (60.0 / 400.0), cls_second_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(2ul, events.size());
std::unique_ptr<Value> data;
events[0]->GetArgAsValue("data", &data);
EXPECT_EQ(0.4 * (60.0 / 400.0), *data->FindDoubleKey("score"));
events[1]->GetArgAsValue("data", &data);
EXPECT_EQ(0.4 * (60.0 / 400.0), *data->FindDoubleKey("score"));
// Check UKM.
ExpectUKMPageLoadMetricNear(
PageLoad::kLayoutInstability_CumulativeShiftScoreName,
LayoutShiftUkmValue(0.03), 1);
// Check UMA.
ExpectUniqueUMAPageLoadMetricNear(
"PageLoad.LayoutInstability.CumulativeShiftScore",
LayoutShiftUmaValue(0.03));
}