|  | <!DOCTYPE HTML> | 
|  | <meta charset=utf-8> | 
|  | <title>Layout Instability: observe layout shift value via PerformanceObserver</title> | 
|  | <body> | 
|  | <style> | 
|  | #myDiv { position: relative; width: 300px; height: 100px; } | 
|  |  | 
|  | /* Disable the button's focus ring, which otherwise expands its visual rect by | 
|  | * 1px on all sides, triggering a layout shift event. | 
|  | */ | 
|  | #button { outline: none; } | 
|  | </style> | 
|  | <div id='myDiv'></div> | 
|  | <button id='button'>Generate a 'click' event</button> | 
|  | <script src="/resources/testharness.js"></script> | 
|  | <script src="/resources/testharnessreport.js"></script> | 
|  | <script src=/resources/testdriver.js></script> | 
|  | <script src=/resources/testdriver-vendor.js></script><script> | 
|  | let timeAfterClick; | 
|  | function mainThreadBusy(duration) { | 
|  | const now = performance.now(); | 
|  | while (performance.now() < now + duration); | 
|  | } | 
|  |  | 
|  | function clickOnElement(id, callback) { | 
|  | const element = document.getElementById(id); | 
|  | const rect = element.getBoundingClientRect(); | 
|  | const xCenter = rect.x + rect.width / 2; | 
|  | const yCenter = rect.y + rect.height / 2; | 
|  | const leftButton = 0; | 
|  | const clickHandler = () => { | 
|  | mainThreadBusy(120); | 
|  | if (callback) | 
|  | callback(); | 
|  | element.removeEventListener("mousedown", clickHandler); | 
|  | }; | 
|  | element.addEventListener("mousedown", clickHandler); | 
|  | test_driver.click(element); | 
|  | } | 
|  |  | 
|  | function clickAndBlockMain(id) { | 
|  | return new Promise((resolve, reject) => { | 
|  | clickOnElement(id, resolve); | 
|  | }); | 
|  | } | 
|  |  | 
|  | async_test(function (t) { | 
|  | if (!window.LayoutShift) | 
|  | assert_unreached('LayoutShift entries are not supported'); | 
|  | const startTime = performance.now(); | 
|  | const observer = new PerformanceObserver( | 
|  | t.step_func_done(function(entryList) { | 
|  | const endTime = performance.now(); | 
|  | assert_equals(entryList.getEntries().length, 1); | 
|  | const entry = entryList.getEntries()[0]; | 
|  | assert_equals(entry.entryType, "layout-shift"); | 
|  | assert_equals(entry.name, ""); | 
|  | assert_greater_than_equal(entry.startTime, startTime) | 
|  | assert_less_than_equal(entry.startTime, endTime) | 
|  | assert_equals(entry.duration, 0.0); | 
|  | const maxDimension = Math.max(document.documentElement.clientWidth, | 
|  | document.documentElement.clientHeight); | 
|  | // The layout shift value should be: | 
|  | // 300 * (100 + 60) * (60 / maxDimension) / viewport size. | 
|  | assert_equals(entry.value, 300 * (100 + 60) * (60 / maxDimension) / | 
|  | (document.documentElement.clientWidth * document.documentElement.clientHeight)); | 
|  | }) | 
|  | ); | 
|  | observer.observe({entryTypes: ['layout-shift']}); | 
|  | window.onload = () => { | 
|  | // Modify the position of the div. | 
|  | document.getElementById('myDiv').style = "top: 60px"; | 
|  | }; | 
|  | }, 'Layout shift is observable via PerformanceObserver.'); | 
|  |  | 
|  | async_test(function (t) { | 
|  | const startTime = performance.now(); | 
|  | const observer = new PerformanceObserver( | 
|  | t.step_func_done(function(entryList) { | 
|  | const endTime = performance.now(); | 
|  | assert_equals(entryList.getEntries().length, 1); | 
|  | const entry = entryList.getEntries()[0]; | 
|  | assert_equals(entry.entryType, "layout-shift"); | 
|  | assert_equals(entry.name, ""); | 
|  | assert_greater_than_equal(entry.startTime, startTime) | 
|  | assert_less_than_equal(entry.startTime, endTime) | 
|  | assert_equals(entry.duration, 0.0); | 
|  | const maxDimension = Math.max(document.documentElement.clientWidth, | 
|  | document.documentElement.clientHeight); | 
|  | // The layout shift value should be: | 
|  | // 300 * (100 + 60) * (60 / maxDimension) / viewport size. | 
|  | assert_equals(entry.value, 300 * (100 + 60) * (60 / maxDimension) / | 
|  | (document.documentElement.clientWidth * document.documentElement.clientHeight)); | 
|  | // We should see that there was a click input entry | 
|  | assert_equals(entry.hadRecentInput, true); | 
|  | assert_greater_than_equal(timeAfterClick, entry.lastInputTime); | 
|  | }) | 
|  | ); | 
|  | observer.observe({entryTypes: ['layout-shift']}); | 
|  | window.onload = () => { | 
|  | // User input event | 
|  | clickAndBlockMain('button').then( () => { | 
|  | timeAfterClick = performance.now(); | 
|  | // Modify the position of the div. | 
|  | document.getElementById('myDiv').style = "top: 60px"; | 
|  | }); | 
|  | }; | 
|  | }, 'Layout shift within user input is observable via PerformanceObserver.'); | 
|  | </script> | 
|  |  | 
|  | </body> |