summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/soft-navigation-heuristics
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/soft-navigation-heuristics
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/soft-navigation-heuristics')
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/back.tentative.html36
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/click-event-bubbles.tentative.html28
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/disabled.html31
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/dropped-entries.tentative.html42
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/first-interaction-not-softnav.tentative.html36
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/hash.tentative.html29
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection-second-softnav.tentative.html60
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection.tentative.html54
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-image-softnav-lcp.tentative.html32
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-text-softnav-lcp.tentative.html31
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-two-image-softnavs-lcp.tentative.html75
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/innertext.tentative.html32
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html76
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/load-classic-script-history-push.tentative.html31
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/load-module-script-history-push.tentative.html32
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/multiple-nested-events.tentative.html40
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/multiple-paint-entries-buffered.tentative.html43
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigate-child.html30
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-after-transition-commit.tentative.html28
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-back.tentative.html45
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-forward.tentative.html48
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-hash.tentative.html28
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html35
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-rejected.tentative.html28
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-traverseto.tentative.html49
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api-view-transition.tentative.html54
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/navigation-api.tentative.html26
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/popstate-multiple-backs.tentative.html64
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/popstate.tentative.html50
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/replacestate-null-then-push.tentative.html34
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/replacestate.tentative.html28
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/resources/empty.html2
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/resources/history_push.js1
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/resources/soft-navigation-helper.js356
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/second-interaction-not-softnav.tentative.html35
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-main-descendent.tentative.html30
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-non-main.tentative.html25
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-web-component-lifecycle.tentative.html46
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection.tentative.html26
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-no-url.tentative.html31
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint-larger-than-viewport.tentative.html70
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint.tentative.html42
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/softnav-before-lcp-paint.tentative.html52
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/softnav-between-lcp-render-and-paint.tentative.html36
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/supported-entry-types.tentative.html15
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection-second-softnav.tentative.html62
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection.tentative.html55
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html32
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-image-softnav-lcp.tentative.html31
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-text-softnav-lcp.tentative.html34
-rw-r--r--testing/web-platform/tests/soft-navigation-heuristics/visited-link.tentative.html48
51 files changed, 2284 insertions, 0 deletions
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/back.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/back.tentative.html
new file mode 100644
index 0000000000..349eaf465c
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/back.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ // Push state a couple of times
+ history.pushState({}, "", "foobar.html");
+ history.pushState({}, "", "anotherOne.html");
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ addTextToDivOnMain();
+ },
+ link: link,
+ pushState: url=>{history.back()},
+ test: "`history.back() properly works with SoftNavigationHeuristics"});
+ </script>
+</body>
+</html>
+
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/click-event-bubbles.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/click-event-bubbles.tentative.html
new file mode 100644
index 0000000000..ee9d1e1233
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/click-event-bubbles.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ // Adding a noop event that the "click" would bubble to.
+ document.getElementById("main").addEventListener("click", () => {});
+
+ testSoftNavigation({
+ addContent: () => {
+ addTextParagraphToMain("Lorem Ipsum");
+ },
+ link: link,
+ test: "Ensure event bubbling works well with soft navigations."});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/disabled.html b/testing/web-platform/tests/soft-navigation-heuristics/disabled.html
new file mode 100644
index 0000000000..b53c2f7012
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/disabled.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigationNotDetected({
+ eventHandler: url => {
+ addTextToDivOnMain();
+ history.pushState({}, '', 'foobar.html');
+ },
+ link: link,
+ eventName: "click",
+ eventTarget: link,
+ testName: "Test that a soft navigation is not detected when the feature "
+ + "is disabled"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/dropped-entries.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/dropped-entries.tentative.html
new file mode 100644
index 0000000000..d27ad452be
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/dropped-entries.tentative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+
+ testSoftNavigation({
+ addContent: async () => {
+ await addImageToMain('green-16x16.png');
+ },
+ link: link,
+ clicks: 52,
+ extraValidations: async (entries, options)=>{
+ if (!performance.softNavPaintMetricsSupported) {
+ return;
+ }
+ const [paint_entries, paint_options] = await new Promise(resolve => {
+ new PerformanceObserver((list, obs, options) =>
+ resolve([list.getEntries(), options])).observe(
+ {type: 'paint', buffered: true});
+ });
+ assert_equals(options['droppedEntriesCount'], 2);
+ assert_equals(paint_options['droppedEntriesCount'], 4);
+ },
+ test: "Test that a soft navigation entries get dropped when buffer limits"
+ + " get exceeded."});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/first-interaction-not-softnav.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/first-interaction-not-softnav.tentative.html
new file mode 100644
index 0000000000..e026202183
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/first-interaction-not-softnav.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <p><a id=firstlink>Click me!</a></p>
+ <p><a id=secondlink>Then click me!</a></p>
+ </main>
+ <script>
+ (async () => {
+ if (test_driver) {
+ const firstlink = document.getElementById("firstlink");
+ const clickPromise = new Promise(r => {
+ firstlink.addEventListener("click", r);
+ });
+ test_driver.click(firstlink);
+ await clickPromise;
+ }
+ })();
+ const secondlink = document.getElementById("secondlink");
+ testSoftNavigation({
+ addContent: () => {
+ addImageToMain();
+ },
+ link: secondlink,
+ test: "first interaction in the middle of a soft navigation"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/hash.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/hash.tentative.html
new file mode 100644
index 0000000000..3a8419c71d
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/hash.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect hashchange event.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ addTextToDivOnMain();
+ },
+ link: link,
+ pushState: (url)=>{location.hash=url;},
+ test: "Location hash changes properly works with SoftNavigationHeuristics"});
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection-second-softnav.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection-second-softnav.tentative.html
new file mode 100644
index 0000000000..4d26bb9269
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection-second-softnav.tentative.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ let first_lcp_painted;
+ let second_lcp_painted;
+ (async () => {
+ await new Promise(r => { first_lcp_painted = r; });
+ addImageToMain("lcp-133x106.png", "no_lcp");
+ (new PerformanceObserver(second_lcp_painted)).observe({type: "element"});
+ })();
+ testSoftNavigation({
+ pushState: null,
+ clicks: 2,
+ addContent: async () => {
+ // Add an LCP element.
+ await new Promise(resolve => {
+ addImageToMain("lcp-100x50.png", "first_lcp" + counter);
+ (new PerformanceObserver(resolve)).observe({type: "element"});
+ });
+ if(counter) {
+ first_lcp_painted();
+ // Wait for the unrelated LCP to be painted.
+ await new Promise(r => { second_lcp_painted = r; });
+ }
+ const url = URL + "?" + counter;
+ history.pushState({}, '', url);
+ },
+ link: link,
+ validate: async () => {
+ const lcps = await getLcpEntries();
+ const ref_counter = counter-1;
+ assert_equals(lcps.length, 2 + ref_counter, "Got 2 LCP entries");
+ assert_equals(lcps[lcps.length - 1].id, "first_lcp" + ref_counter,
+ "Got the first LCP");
+ },
+ test: "Second soft navigation image LCP discovered between user " +
+ "interaction and soft navigation detection are properly " +
+ "reported, while unrelated LCPs are ignored during that time. " +
+ "As a side effect, we also test element timing."});
+ </script>
+</body>
+</html>
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection.tentative.html
new file mode 100644
index 0000000000..0de675d372
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-before-detection.tentative.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ let first_lcp_painted;
+ let second_lcp_painted;
+ (async () => {
+ await new Promise(r => { first_lcp_painted = r; });
+ addImageToMain("lcp-133x106.png", "no_lcp");
+ (new PerformanceObserver(second_lcp_painted)).observe({type: "element"});
+ })();
+ testSoftNavigation({
+ pushState: null,
+ addContent: async () => {
+ // Add an LCP element.
+ await new Promise(resolve => {
+ addImageToMain("lcp-100x50.png", "first_lcp");
+ (new PerformanceObserver(resolve)).observe({type: "element"});
+ });
+ first_lcp_painted();
+ // Wait for the unrelated LCP to be painted.
+ await new Promise(r => { second_lcp_painted = r; });
+ const url = URL + "?" + counter;
+ history.pushState({}, '', url);
+ },
+ link: link,
+ validate: async () => {
+ const lcps = await getLcpEntries();
+ assert_equals(lcps.length, 2, "Got 2 LCP entries");
+ assert_equals(lcps[lcps.length - 1].id, "first_lcp", "Got the first LCP");
+ },
+ test: "Image LCP discovered between user interaction and soft " +
+ "navigation detection are properly reported, while unrelated " +
+ "LCPs are ignored during that time. As a side effect, we also " +
+ "test element timing."});
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-image-softnav-lcp.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-image-softnav-lcp.tentative.html
new file mode 100644
index 0000000000..7a2018d20e
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-image-softnav-lcp.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ const main = document.getElementById("main");
+ main.removeChild(document.getElementsByTagName("div")[0]);
+ await addImageToMain();
+ },
+ link: link,
+ test: "Test that an image LCP followup by a smaller soft navigation image LCP"
+ + " properly queues an LCP entry"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-text-softnav-lcp.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-text-softnav-lcp.tentative.html
new file mode 100644
index 0000000000..501d1c5fb6
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-text-softnav-lcp.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ addTextToDivOnMain();
+ },
+ link: link,
+ test: "Test that an image LCP followup by a smaller soft navigation text LCP"
+ + " properly queues an LCP entry"});
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-two-image-softnavs-lcp.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-two-image-softnavs-lcp.tentative.html
new file mode 100644
index 0000000000..66a9e5136f
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/image-lcp-followed-by-two-image-softnavs-lcp.tentative.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+
+ promise_test(async t => {
+ validatePaintEntries('first-contentful-paint', 1);
+ validatePaintEntries('first-paint', 1);
+ const preClickLcp = await getLcpEntries();
+ setEvent(t, link, /*pushState=*/url=>history.pushState({}, '', url),
+ /*addContent=*/async () => await addImageToMain(), /*pushUrl=*/true,
+ /*eventType=*/"click");
+
+ const first_click_paint_promise = waitOnPaintEntriesPromise();
+
+ interact(link);
+ await new Promise(resolve => {
+ (new PerformanceObserver(resolve)).observe({
+ type: 'soft-navigation'
+ });
+ });
+ assert_equals(
+ document.softNavigations, 1,
+ 'One Soft Navigation detected');
+
+ await first_click_paint_promise;
+ const postClickLcp = await getLcpEntries();
+ assert_greater_than(
+ postClickLcp.length, preClickLcp.length,
+ 'Soft navigation should have triggered at least an LCP entry');
+ assert_less_than(
+ postClickLcp[postClickLcp.length - 1].size,
+ preClickLcp[preClickLcp.length - 1].size,
+ 'Soft navigation LCP element should have a smaller size than the hard'
+ + ' navigation LCP element');
+
+ const second_click_paint_promise = waitOnPaintEntriesPromise();
+ const preClickLcp2 = await getLcpEntries();
+ interact(link);
+ await new Promise(resolve => {
+ (new PerformanceObserver(() => resolve())).observe({
+ type: 'soft-navigation'
+ });
+ });
+ assert_equals(
+ document.softNavigations, 2,
+ 'Two Soft Navigations detected');
+
+ await second_click_paint_promise;
+ const postClickLcp2 = await getLcpEntries();
+ assert_equals(postClickLcp2.length, 3, 'We expected 3 LCP entries at this point');
+ assert_greater_than(
+ postClickLcp2.length, preClickLcp2.length,
+ 'Soft navigation should have triggered at least an LCP entry');
+ }, "Multiple soft navigations get FP, FCP and LCP for each one");
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/innertext.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/innertext.tentative.html
new file mode 100644
index 0000000000..d40b604792
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/innertext.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ <!-- This test fails if the paragraph below already has content. The reason
+ is that secondary paints are not being properly recorded, and hence don't
+ count for soft navigation heuristics. -->
+ <p id="softnav-content"></p>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ document.getElementById("softnav-content").innerText =
+ "Lorem Ipsum dolor sit amet";
+ },
+ link: link,
+ test: "Soft navigation when only innerText was modified"});
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
new file mode 100644
index 0000000000..b587411991
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
+ <a id=not_nav><img src="/images/lcp-16x16.png"></a>
+ </div>
+ </main>
+ <script>
+ // Push state a couple of times
+ history.pushState({}, "", "foobar.html");
+ history.pushState({}, "", "anotherOne.html");
+
+ (async () => {
+ const link = document.getElementById("link");
+ // Trigger a user interaction that doesn't result in a soft navigation, but
+ // does paint.
+ await (async () => {
+ const not_nav = document.getElementById("not_nav");
+ let non_soft_nav_click;
+ const non_soft_nav_click_promise =
+ new Promise(r => { non_soft_nav_click = r; });
+ not_nav.addEventListener("click", () => {
+ addImageToMain("lcp-133x106.png", "not_soft_nav_image");
+ (new PerformanceObserver(non_soft_nav_click)).observe({type: "element"});
+ });
+ if (test_driver) {
+ test_driver.click(not_nav);
+ }
+ await non_soft_nav_click_promise;
+ })();
+ const url = URL + "?" + counter;
+ link.addEventListener("click", () => {
+ // Add an LCP element.
+ const img = new Image();
+ img.src = '/images/lcp-100x500.png' + "?" + Math.random();
+ document.getElementById("main").appendChild(img);
+ history.back();
+ });
+ promise_test(async t => {
+ if (test_driver) {
+ test_driver.click(link);
+ }
+ await waitOnSoftNav();
+ assert_equals(
+ document.softNavigations, 1,
+ 'Single Soft Navigation detected');
+ const [entries, options] = await new Promise(resolve => {
+ (new PerformanceObserver((list, obs, options) => resolve(
+ [list.getEntries(), options]))).observe(
+ {type: 'soft-navigation', buffered: true});
+ });
+
+ assert_equals(entries.length, 1,
+ "Performance observer got an entry");
+ }, "Ensure that soft navigation entry emitted through a synchronous " +
+ "event that modified DOM and committed a same document navigation, " +
+ "and that was preceded by a user intreaction that resulted in a " +
+ "contentful paint is properly detected.");
+ })();
+ </script>
+</body>
+</html>
+
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/load-classic-script-history-push.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/load-classic-script-history-push.tentative.html
new file mode 100644
index 0000000000..4e375b2164
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/load-classic-script-history-push.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect a soft navigation triggered by an external classic script.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ await addImageToMain();
+ },
+ link: link,
+ pushState: async (url) => {
+ const script = document.createElement("script");
+ script.src = "resources/history_push.js";
+ document.body.appendChild(script);
+ },
+ test: "Detect a soft navigation triggered from external classic script"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/load-module-script-history-push.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/load-module-script-history-push.tentative.html
new file mode 100644
index 0000000000..e82cd13f4a
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/load-module-script-history-push.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect a soft navigation triggered by an external module script.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ await addImageToMain();
+ },
+ link: link,
+ pushState: async (url) => {
+ const script = document.createElement("script");
+ script.src = "resources/history_push.js";
+ script.type = "module";
+ document.body.appendChild(script);
+ },
+ test: "Detect a soft navigation triggered from external module script"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/multiple-nested-events.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/multiple-nested-events.tentative.html
new file mode 100644
index 0000000000..e51841865d
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/multiple-nested-events.tentative.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ let should_navigate = true;
+ navigation.addEventListener("navigate", () => {
+ if (should_navigate) {
+ // It's the last sync navigation that determines the soft nav URL.
+ history.pushState({}, '', 'foobar.html');
+ should_navigate = false;
+ }
+ });
+ testSoftNavigation({
+ eventPrepWork: url => {
+ addTextToDivOnMain();
+ history.pushState({}, '', 'foobar1.html');
+ // Here we're bypassing the regular test's event logic, as this test is
+ // fully sync.
+ return false;
+ },
+ link: link,
+ eventName: "click",
+ eventTarget: link,
+ testName: "Test multiple nested navigate events"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/multiple-paint-entries-buffered.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/multiple-paint-entries-buffered.tentative.html
new file mode 100644
index 0000000000..dbb945a0a8
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/multiple-paint-entries-buffered.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Detect multiple soft navigations and ensure they buffer paint entries.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+
+ testSoftNavigation({
+ addContent: async () => {
+ await addImageToMain();
+ },
+ link: link,
+ clicks: 4,
+ extraValidations: async (entries, options)=>{
+ if (!performance.softNavPaintMetricsSupported) {
+ return;
+ }
+ const paint_entries = await new Promise(resolve => {
+ new PerformanceObserver(list => resolve(list.getEntries())).observe(
+ {type: 'paint', buffered: true,
+ includeSoftNavigationObservations: true});
+ });
+ assert_equals(paint_entries.length, 10);
+ },
+ test: "Test that multiple soft navigation buffer paint entries"});
+ </script>
+</body>
+</html>
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigate-child.html b/testing/web-platform/tests/soft-navigation-heuristics/navigate-child.html
new file mode 100644
index 0000000000..e3c17e2dba
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigate-child.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Navigate a child window.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a href="empty.html?2" rel=opener target=child id=link>Click me!</a>
+ </main>
+ <script>
+ promise_test(async t => {
+ const child = window.open("resources/empty.html?1", "child");
+ while (!child.document) {
+ await new Promise(r => t.step_timeout(r, 10));
+ }
+ const link = document.getElementById("link");
+ interact(link);
+ while (!child.location.href.includes("2")) {
+ await new Promise(r => t.step_timeout(r, 10));
+ }
+ }, "Test that a navigated child window doesn't crash");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-after-transition-commit.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-after-transition-commit.tentative.html
new file mode 100644
index 0000000000..ae17db7d59
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-after-transition-commit.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect intercepted navigate event with after-transition commit.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a href="foobar.html" id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testNavigationApi("Test soft navigation when navigate event intecepts with { commit: 'after-transition' }", e => {
+ e.intercept({commit: "after-transition", handler: async () => {
+ await addImageToMain();
+ e.commit();
+ }});
+ timestamps[counter]["eventEnd"] = performance.now();
+ }, link);
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-back.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-back.tentative.html
new file mode 100644
index 0000000000..cb96d9caf9
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-back.tentative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect navigation.back()</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ window.onload = async () => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ await new Promise(r => step_timeout(r, 0));
+
+ navigation.onnavigate = e => {
+ e.intercept();
+ };
+ // Push a couple of navigation entries, so that we'd have a navigation entry to go back to.
+ await navigation.navigate("foobar.html").finished;
+ await navigation.navigate("another.html").committed;
+
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ addTextToDivOnMain();
+ },
+ link: link,
+ pushState: async () =>{
+ await navigation.back().committed;
+ },
+ test: "`navigation.back()` properly works with SoftNavigationHeuristics"});
+ };
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-forward.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-forward.tentative.html
new file mode 100644
index 0000000000..f483ad376b
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-forward.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect navigation.forward()</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ window.onload = async () => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ await new Promise(r => step_timeout(r, 0));
+
+ navigation.onnavigate = e => {
+ e.intercept();
+ };
+ // Push a couple of navigation entries, then go back so that we'd have a
+ // navigation entry to go forward to.
+ await navigation.navigate("other.html").finished;
+ await navigation.navigate("foobar.html").finished;
+ await navigation.back().finished;
+
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ addTextToDivOnMain();
+ },
+ link: link,
+ pushState: async () =>{
+ await navigation.forward().committed;
+ },
+ test: "`navigation.forward()` properly works with SoftNavigationHeuristics"});
+ };
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-hash.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-hash.tentative.html
new file mode 100644
index 0000000000..e20578ea79
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-hash.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect intercepted navigate event.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a href="#foobar.html" id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testNavigationApi("Test soft navigation with the Navigation API", e => {
+ e.intercept({handler: async () => {
+ await addImageToMain();
+ main.appendChild(img);
+ }});
+ timestamps[counter]["eventEnd"] = performance.now();
+ }, link);
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html
new file mode 100644
index 0000000000..b7b2a24c94
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-preventDefault.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Don't detect a navigate event which got aborted as a soft navigation.
+</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a href="foobar.html" id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigationNotDetected({
+ testName: "Aborted navigate event is not a soft navigation",
+ eventHandler: e => {
+ e.intercept({handler: async () => {
+ await addImageToMain();
+ main.appendChild(img);
+ }});
+ e.preventDefault();
+ timestamps[counter]["eventEnd"] = performance.now();
+ },
+ eventTarget: navigation,
+ eventName: "navigate",
+ link: link});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-rejected.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-rejected.tentative.html
new file mode 100644
index 0000000000..693f876b6e
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-rejected.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect a rejected intercepted navigate event.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a href="foobar.html" id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testNavigationApi("Test intercepted and rejected navigate event", e => {
+ e.intercept({handler: async () => {
+ await addImageToMain();
+ throw new Error("This navigation handler rejected");
+ }});
+ timestamps[counter]["eventEnd"] = performance.now();
+ }, link);
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-traverseto.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-traverseto.tentative.html
new file mode 100644
index 0000000000..e4cabb095a
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-traverseto.tentative.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect navigation.traverseTo()</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ let key;
+ window.onload = async () => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ await new Promise(r => step_timeout(r, 0));
+
+ navigation.onnavigate = e => {
+ e.intercept();
+ };
+ // Push a couple of navigation entries, so that we'd have a navigation entry to traverse to.
+ await navigation.navigate("foobar.html").finished;
+ key = navigation.currentEntry.key;
+ await navigation.navigate("another.html").finished;
+
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ addTextToDivOnMain();
+ },
+ link: link,
+ pushState: async () =>{
+ await navigation.traverseTo(key).committed;
+ },
+ test: "`navigation.traverseTo()` properly works with SoftNavigationHeuristics"});
+ };
+ </script>
+</body>
+</html>
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-view-transition.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-view-transition.tentative.html
new file mode 100644
index 0000000000..4d88f3d0b7
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api-view-transition.tentative.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Navigation API + ViewTransition trigger a soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link href="foobar.html">Click me!</a>
+ </div>
+ </main>
+ <script>
+ let key;
+ window.onload = async () => {
+ // Wait for after the load event so that the navigation doesn't get
+ // converted into a replace navigation.
+ await new Promise(r => step_timeout(r, 0));
+
+ const navigate_callback = e => {
+ e.intercept({
+ async handler() {
+ const lcp_promise = new Promise(resolve => {
+ (new PerformanceObserver(list => resolve())).observe(
+ {type: 'largest-contentful-paint',
+ includeSoftNavigationObservations: true});
+ });
+ const transition = document.startViewTransition(async () => {
+ const main = document.getElementById('main');
+ main.innerHTML = '<img id="image" src="/images/blue.png?' +
+ Math.random() + '">';
+ const img = document.getElementById("image");
+ });
+ await transition.updateCallbackDone;
+ await lcp_promise;
+ }
+ });
+ timestamps[counter]["eventEnd"] = performance.now();
+ };
+
+ const link = document.getElementById("link");
+ testNavigationApi("Navigation API interception handler + " +
+ "startViewTransition properly detects soft navigations",
+ navigate_callback, link);
+ };
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/navigation-api.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api.tentative.html
new file mode 100644
index 0000000000..2d61736a48
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/navigation-api.tentative.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect intercepted navigate event.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a href="foobar.html" id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testNavigationApi("Test soft navigation with the Navigation API", e => {
+ e.intercept({handler: async () => {
+ await addImageToMain();
+ }});
+ timestamps[counter]["eventEnd"] = performance.now();
+ }, link);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/popstate-multiple-backs.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/popstate-multiple-backs.tentative.html
new file mode 100644
index 0000000000..fd87f5f03e
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/popstate-multiple-backs.tentative.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Soft navigation with multiple popstate calls.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ // Push state 4 times, as history.back() calls will trigger popstate
+ // events.
+ history.pushState({}, "", "foobar.html");
+ history.pushState({}, "", "another_one.html");
+ history.pushState({}, "", "and_another.html");
+ history.pushState({}, "", "and_yet_another.html");
+
+ // This function runs at the start of the popstate event.
+ const eventPrepWork = t => {
+ // If this is an event due to the first click, go back() twice more.
+ if (!t.popped) {
+ step_timeout(()=>history.back(), 0);
+ step_timeout(()=>history.back(), 0);
+ t.popped = 0;
+ }
+ ++t.popped;
+ // return true for the second time the event fires, which is the first
+ // back() triggered by the popstate event. The means that the first one
+ // of those back() navigations would trigger a soft navigation, but not
+ // the last one.
+ return t.popped == 2;
+ }
+ const link = document.getElementById("link");
+ link.addEventListener("click", () => {
+ history.back();
+ timestamps[counter]["eventEnd"] = performance.now();
+ });
+ testSoftNavigation({
+ addContent: () => {
+ // Add the content to the main element
+ const main = document.getElementById("main");
+ main.removeChild(document.getElementsByTagName("div")[0]);
+ const div = document.createElement("div");
+ const text = document.createTextNode("Lorem ipsum");
+ div.appendChild(text);
+ div.style="font-size: 3em";
+ main.appendChild(div);
+ },
+ link: link,
+ eventPrepWork: eventPrepWork,
+ testName: "A soft navigation that started from a back() call inside a "
+ + "popstate event is recognized by SoftNavigationHeuristics",
+ eventType: "popstate"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/popstate.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/popstate.tentative.html
new file mode 100644
index 0000000000..f89991d76b
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/popstate.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link>Click me!</a>
+ </div>
+ </main>
+ <script>
+ // Push state twice, so that history.back() will trigger a popstate event,
+ // when the first push state is restored.
+ history.pushState({}, "", "foobar.html");
+ history.pushState({}, "", "another_one.html");
+
+ const link = document.getElementById("link");
+ link.addEventListener("click", () => {
+ history.back();
+ timestamps[counter]["eventEnd"] = performance.now();
+ });
+ testSoftNavigation({
+ addContent: () => {
+ // Add the content to the main element
+ const main = document.getElementById("main");
+ main.removeChild(document.getElementsByTagName("div")[0]);
+ const div = document.createElement("div");
+ const text = document.createTextNode("Lorem ipsum");
+ div.appendChild(text);
+ div.style="font-size: 3em";
+ main.appendChild(div);
+ },
+ link: link,
+ testName: "A soft navigation that uses a same-document initiated popstate"
+ + " event is recognized by SoftNavigationHeuristics",
+ eventType: "popstate"});
+ </script>
+</body>
+</html>
+
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/replacestate-null-then-push.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/replacestate-null-then-push.tentative.html
new file mode 100644
index 0000000000..8a81c6be20
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/replacestate-null-then-push.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect soft navigation with replaceState that has a null URL, then
+ pushState with the URL.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async (url) => {
+ await addImageToMain();
+ history.pushState({}, '', url);
+ },
+ link: link,
+ pushState: async (url) =>{
+ history.replaceState({}, '');
+ },
+ test: "Detect soft navigation with replaceState that has a null URL," +
+ " then pushState with the URL"});
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/replacestate.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/replacestate.tentative.html
new file mode 100644
index 0000000000..42e9a71899
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/replacestate.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect soft navigation with replaceState.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ await addImageToMain();
+ },
+ link: link,
+ pushState: (url)=>{history.replaceState({}, '', url);},
+ test: "Detect soft navigation with replaceState"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/resources/empty.html b/testing/web-platform/tests/soft-navigation-heuristics/resources/empty.html
new file mode 100644
index 0000000000..5fa1cdf5e6
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/resources/empty.html
@@ -0,0 +1,2 @@
+<!DOCTYPE HTML>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/resources/history_push.js b/testing/web-platform/tests/soft-navigation-heuristics/resources/history_push.js
new file mode 100644
index 0000000000..6647dd740a
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/resources/history_push.js
@@ -0,0 +1 @@
+history.pushState({}, '', URL);
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/resources/soft-navigation-helper.js b/testing/web-platform/tests/soft-navigation-heuristics/resources/soft-navigation-helper.js
new file mode 100644
index 0000000000..d405adb4e7
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/resources/soft-navigation-helper.js
@@ -0,0 +1,356 @@
+var counter = 0;
+var interacted;
+var timestamps = []
+const MAX_CLICKS = 50;
+// Entries for one hard navigation + 50 soft navigations.
+const MAX_PAINT_ENTRIES = 51;
+const URL = "foobar.html";
+const readValue = (value, defaultValue) => {
+ return value !== undefined ? value : defaultValue;
+}
+const testSoftNavigation =
+ options => {
+ const addContent = options.addContent;
+ const link = options.link;
+ const pushState = readValue(options.pushState,
+ url=>{history.pushState({}, '', url)});
+ const clicks = readValue(options.clicks, 1);
+ const extraValidations = readValue(options.extraValidations,
+ () => {});
+ const testName = options.testName;
+ const pushUrl = readValue(options.pushUrl, true);
+ const eventType = readValue(options.eventType, "click");
+ const interactionFunc = options.interactionFunc;
+ const eventPrepWork = options.eventPrepWork;
+ promise_test(async t => {
+ await waitInitialLCP();
+ const preClickLcp = await getLcpEntries();
+ setEvent(t, link, pushState, addContent, pushUrl, eventType,
+ eventPrepWork);
+ let first_navigation_id;
+ for (let i = 0; i < clicks; ++i) {
+ const firstClick = (i === 0);
+ let paint_entries_promise =
+ waitOnPaintEntriesPromise(firstClick);
+ interacted = false;
+ interact(link, interactionFunc);
+
+ const navigation_id = await waitOnSoftNav();
+ if (!first_navigation_id) {
+ first_navigation_id = navigation_id;
+ }
+ // Ensure paint timing entries are fired before moving on to the next
+ // click.
+ await paint_entries_promise;
+ }
+ assert_equals(
+ document.softNavigations, clicks,
+ 'Soft Navigations detected are the same as the number of clicks');
+ await validateSoftNavigationEntry(
+ clicks, extraValidations, pushUrl);
+
+ await runEntryValidations(preClickLcp, first_navigation_id, clicks + 1, options.validate);
+ }, testName);
+ };
+
+const testNavigationApi = (testName, navigateEventHandler, link) => {
+ promise_test(async t => {
+ navigation.addEventListener('navigate', navigateEventHandler);
+ const navigated = new Promise(resolve => {
+ navigation.addEventListener('navigatesuccess', resolve);
+ navigation.addEventListener('navigateerror', resolve);
+ });
+ await waitInitialLCP();
+ const preClickLcp = await getLcpEntries();
+ let paint_entries_promise = waitOnPaintEntriesPromise();
+ interact(link);
+ const first_navigation_id = await waitOnSoftNav();
+ await navigated;
+ await paint_entries_promise;
+ assert_equals(document.softNavigations, 1, 'Soft Navigation detected');
+ await validateSoftNavigationEntry(1, () => {}, 'foobar.html');
+
+ await runEntryValidations(preClickLcp, first_navigation_id);
+ }, testName);
+};
+
+const testSoftNavigationNotDetected = options => {
+ promise_test(async t => {
+ const preClickLcp = await getLcpEntries();
+ options.eventTarget.addEventListener(options.eventName, options.eventHandler);
+ interact(options.link);
+ await new Promise((resolve, reject) => {
+ (new PerformanceObserver(() =>
+ reject("Soft navigation should not be triggered"))).observe({
+ type: 'soft-navigation',
+ buffered: true
+ });
+ t.step_timeout(resolve, 1000);
+ });
+ if (document.softNavigations) {
+ assert_equals(
+ document.softNavigations, 0, 'Soft Navigation not detected');
+ }
+ const postClickLcp = await getLcpEntries();
+ assert_equals(
+ preClickLcp.length, postClickLcp.length, 'No LCP entries accumulated');
+ }, options.testName);
+ };
+
+const runEntryValidations =
+ async (preClickLcp, first_navigation_id, entries_expected_number = 2,
+ validate = null) => {
+ await validatePaintEntries('first-contentful-paint', entries_expected_number,
+ first_navigation_id);
+ await validatePaintEntries('first-paint', entries_expected_number,
+ first_navigation_id);
+ const postClickLcp = await getLcpEntries();
+ const postClickLcpWithoutSoftNavs = await getLcpEntriesWithoutSoftNavs();
+ assert_greater_than(
+ postClickLcp.length, preClickLcp.length,
+ 'Soft navigation should have triggered at least an LCP entry');
+
+ if (validate) {
+ await validate();
+ }
+ assert_equals(
+ postClickLcpWithoutSoftNavs.length, preClickLcp.length,
+ 'Soft navigation should not have triggered an LCP entry when the ' +
+ 'observer did not opt in');
+ assert_not_equals(
+ postClickLcp[postClickLcp.length - 1].size,
+ preClickLcp[preClickLcp.length - 1].size,
+ 'Soft navigation LCP element should not have identical size to the hard ' +
+ 'navigation LCP element');
+ assert_equals(
+ postClickLcp[preClickLcp.length].navigationId,
+ first_navigation_id, 'Soft navigation LCP should have the same navigation ' +
+ 'ID as the last soft nav entry')
+};
+
+const interact =
+ (link, interactionFunc = undefined) => {
+ if (test_driver) {
+ if (interactionFunc) {
+ interactionFunc();
+ } else {
+ test_driver.click(link);
+ }
+ timestamps[counter] = {"syncPostInteraction": performance.now()};
+ }
+ }
+
+const setEvent = (t, button, pushState, addContent, pushUrl, eventType, prepWork) => {
+ const eventObject =
+ (eventType == 'click' || eventType.startsWith("key")) ? button : window;
+ eventObject.addEventListener(eventType, async e => {
+ let prepWorkFailed = false;
+ if (prepWork &&!prepWork(t)) {
+ prepWorkFailed = true;
+ }
+ // This is the end of the event's sync processing.
+ if (!timestamps[counter]["eventEnd"]) {
+ timestamps[counter]["eventEnd"] = performance.now();
+ }
+ if (prepWorkFailed) {
+ return;
+ }
+ // Jump through a task, to ensure task tracking is working properly.
+ await new Promise(r => t.step_timeout(r, 0));
+
+ const url = URL + "?" + counter;
+ if (pushState) {
+ // Change the URL
+ if (pushUrl) {
+ pushState(url);
+ } else {
+ pushState();
+ }
+ }
+
+ // Wait 10 ms to make sure the timestamps are correct.
+ await new Promise(r => t.step_timeout(r, 10));
+
+ await addContent(url);
+
+ interacted = true;
+ ++counter;
+ });
+};
+
+const validateSoftNavigationEntry = async (clicks, extraValidations,
+ pushUrl) => {
+ const [entries, options] = await new Promise(resolve => {
+ (new PerformanceObserver((list, obs, options) => resolve(
+ [list.getEntries(), options]))).observe(
+ {type: 'soft-navigation', buffered: true});
+ });
+ const expectedClicks = Math.min(clicks, MAX_CLICKS);
+
+ assert_equals(entries.length, expectedClicks,
+ "Performance observer got an entry");
+ for (let i = 0; i < entries.length; ++i) {
+ const entry = entries[i];
+ assert_true(entry.name.includes(pushUrl ? URL : document.location.href),
+ "The soft navigation name is properly set");
+ const entryTimestamp = entry.startTime;
+ assert_less_than_equal(timestamps[i]["syncPostInteraction"], entryTimestamp,
+ "Entry timestamp is lower than the post interaction one");
+ assert_greater_than_equal(
+ entryTimestamp, timestamps[i]['eventEnd'],
+ 'Event start timestamp matches');
+ assert_not_equals(entry.navigationId,
+ performance.getEntriesByType("navigation")[0].navigationId,
+ "The navigation ID was re-generated and different from the initial one.");
+ if (i > 0) {
+ assert_not_equals(entry.navigationId,
+ entries[i-1].navigationId,
+ "The navigation ID was re-generated between clicks");
+ }
+ }
+ assert_equals(performance.getEntriesByType("soft-navigation").length,
+ expectedClicks, "Performance timeline got an entry");
+ await extraValidations(entries, options);
+
+};
+
+const validatePaintEntries = async (type, entries_number, first_navigation_id) => {
+ if (!performance.softNavPaintMetricsSupported) {
+ return;
+ }
+ const expected_entries_number = Math.min(entries_number, MAX_PAINT_ENTRIES);
+ const entries = await new Promise(resolve => {
+ const entries = [];
+ (new PerformanceObserver(list => {
+ entries.push(...list.getEntriesByName(type));
+ if (entries.length >= expected_entries_number) {
+ resolve(entries);
+ }
+ })).observe(
+ {type: 'paint', buffered: true, includeSoftNavigationObservations: true});
+ });
+ const entries_without_softnavs = await new Promise(resolve => {
+ (new PerformanceObserver(list => resolve(
+ list.getEntriesByName(type)))).observe(
+ {type: 'paint', buffered: true});
+ });
+ assert_equals(entries.length, expected_entries_number,
+ `There are ${entries_number} entries for ${type}`);
+ assert_equals(entries_without_softnavs.length, 1,
+ `There is one non-softnav entry for ${type}`);
+ if (entries_number > 1) {
+ assert_not_equals(entries[0].startTime, entries[1].startTime,
+ "Entries have different timestamps for " + type);
+ }
+ if (expected_entries_number > entries_without_softnavs.length) {
+ assert_equals(entries[entries_without_softnavs.length].navigationId,
+ first_navigation_id,
+ "First paint entry should have the same navigation ID as the last soft " +
+ "navigation entry");
+ }
+};
+
+const waitInitialLCP = () => {
+ return new Promise(resolve => {
+ new PerformanceObserver(list => resolve()).observe({
+ type: 'largest-contentful-paint',
+ buffered: true
+ });
+ });
+}
+
+const waitOnSoftNav = () => {
+ return new Promise(resolve => {
+ (new PerformanceObserver(list => {
+ const entries = list.getEntries();
+ assert_equals(entries.length, 1,
+ "Only one soft navigation entry");
+ resolve(entries[0].navigationId);
+ })).observe({
+ type: 'soft-navigation'
+ });
+ });
+};
+
+const getLcpEntries = async () => {
+ const entries = await new Promise(resolve => {
+ (new PerformanceObserver(list => resolve(
+ list.getEntries()))).observe(
+ {type: 'largest-contentful-paint', buffered: true,
+ includeSoftNavigationObservations: true});
+ });
+ return entries;
+};
+
+const getLcpEntriesWithoutSoftNavs = async () => {
+ const entries = await new Promise(resolve => {
+ (new PerformanceObserver(list => resolve(
+ list.getEntries()))).observe(
+ {type: 'largest-contentful-paint', buffered: true});
+ });
+ return entries;
+};
+
+const addImage = async (element, url="blue.png", id = "imagelcp") => {
+ const img = new Image();
+ img.src = '/images/'+ url + "?" + Math.random();
+ img.id=id
+ img.setAttribute("elementtiming", id);
+ await img.decode();
+ element.appendChild(img);
+};
+const addImageToMain = async (url="blue.png", id = "imagelcp") => {
+ await addImage(document.getElementById('main'), url, id);
+};
+
+const addTextParagraphToMain = (text, element_timing = "") => {
+ const main = document.getElementById("main");
+ const p = document.createElement("p");
+ const textNode = document.createTextNode(text);
+ p.appendChild(textNode);
+ if (element_timing) {
+ p.setAttribute("elementtiming", element_timing);
+ }
+ p.style = "font-size: 3em";
+ main.appendChild(p);
+ return p;
+};
+const addTextToDivOnMain = () => {
+ const main = document.getElementById("main");
+ const prevDiv = document.getElementsByTagName("div")[0];
+ if (prevDiv) {
+ main.removeChild(prevDiv);
+ }
+ const div = document.createElement("div");
+ const text = document.createTextNode("Lorem Ipsum");
+ div.appendChild(text);
+ div.style = "font-size: 3em";
+ main.appendChild(div);
+}
+
+const waitOnPaintEntriesPromise = (expectLCP = true) => {
+ return new Promise((resolve, reject) => {
+ if (performance.softNavPaintMetricsSupported) {
+ const paint_entries = []
+ new PerformanceObserver(list => {
+ paint_entries.push(...list.getEntries());
+ if (paint_entries.length == 2) {
+ resolve();
+ } else if (paint_entries.length > 2) {
+ reject();
+ }
+ }).observe({type: 'paint', includeSoftNavigationObservations: true});
+ } else if (expectLCP) {
+ new PerformanceObserver(list => {
+ resolve();
+ }).observe({
+ type: 'largest-contentful-paint',
+ includeSoftNavigationObservations: true
+ });
+ } else {
+ step_timeout(
+ () => requestAnimationFrame(() => requestAnimationFrame(resolve)),
+ 100);
+ }
+ });
+};
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/second-interaction-not-softnav.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/second-interaction-not-softnav.tentative.html
new file mode 100644
index 0000000000..a9bb337060
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/second-interaction-not-softnav.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <p><a id=link>Click me!</a></p>
+ <p><a id=secondlink>Then click me!</a></p>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ if (test_driver) {
+ const secondlink = document.getElementById("secondlink");
+ const clickPromise = new Promise(r => {
+ secondlink.addEventListener("click", r);
+ });
+ test_driver.click(secondlink);
+ await clickPromise;
+ }
+ addImageToMain();
+ },
+ link: link,
+ test: "Second interaction in the middle of a soft navigation"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-main-descendent.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-main-descendent.tentative.html
new file mode 100644
index 0000000000..96ff55260c
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-main-descendent.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect soft navigation adding content to a main descendent.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ <div id=descendent></div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ const descendent= document.getElementById("descendent");
+ const content = document.createTextNode("Lorem Ipsum");
+ descendent.appendChild(content);
+ },
+ link: link,
+ test: "Test that a soft navigation is detected even when DOM change is "
+ + "done on a main descendent"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-non-main.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-non-main.tentative.html
new file mode 100644
index 0000000000..86da167c33
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-non-main.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <a id=link>Click me!</a>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ await addImage(document.body);
+ },
+ link: link,
+ test: "Test that a soft navigation is detected on a non-main element"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-web-component-lifecycle.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-web-component-lifecycle.tentative.html
new file mode 100644
index 0000000000..7e27b0073c
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection-web-component-lifecycle.tentative.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <script>
+ // Define a custom element
+ class SPAContent extends HTMLDivElement {
+ constructor() {
+ super();
+ }
+ connectedCallback() {
+ // Change the URL
+ history.pushState({}, '', "/foobar.html");
+ }
+ }
+ customElements.define("spa-content", SPAContent, { extends: "div"});
+
+ </script>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ const main = document.getElementById("main");
+ const spaContent = document.createElement("div", {is: "spa-content"});
+ const content = document.createTextNode("Lorem Ipsum");
+ spaContent.appendChild(content);
+ main.appendChild(spaContent);
+ },
+ link: link,
+ test: "Test that a soft navigation is detected when the click is done "
+ + "on a custom element."});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection.tentative.html
new file mode 100644
index 0000000000..618984d859
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-detection.tentative.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ await addImageToMain();
+ },
+ link: link,
+ test: "Test that a soft navigation is detected"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-no-url.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-no-url.tentative.html
new file mode 100644
index 0000000000..a0055c654c
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/soft-navigation-no-url.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigationNotDetected({
+ eventHandler: url => {
+ addTextToDivOnMain();
+ history.pushState({}, '');
+ },
+ link: link,
+ eventName: "click",
+ eventTarget: link,
+ testName: "Test that a soft navigation is not detected when a URL is not"
+ + " passed to the history API."});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint-larger-than-viewport.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint-larger-than-viewport.tentative.html
new file mode 100644
index 0000000000..3c930d8be4
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint-larger-than-viewport.tentative.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="/soft-navigation-heuristics/resources/soft-navigation-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img id="initial_image1" src="/images/lcp-256x256.png?1" style="width: 90vw; height: 90vh">
+ <div id=extra></div>
+ </a>
+ </div>
+ </main>
+ <script>
+ (async () => {
+ const link = document.getElementById("link");
+
+ // Inject a second image that takes a large part of the viewport.
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ document.getElementById("extra").innerHTML = `
+ <div style="position: absolute; bottom: 0; right: 0">
+ <img id="initial_image2" src="/images/lcp-256x256.png?2" style="width: 90vw;height: 90vh">
+ </div>`;
+ resolve();
+ }));
+ });
+ // Wait until the second image is rendered.
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ resolve();
+ }));
+ });
+ testSoftNavigation({
+ addContent: async () => {
+ // Remove the initial image, so that the text would be painted on screen.
+ document.getElementById("initial_image1").remove();
+ document.getElementById("initial_image2").remove();
+ let lcp_element_painted;
+ const lcp_element_paint_promise = new Promise((r) => { lcp_element_painted = r; });
+ // Add an LCP element, but have it be small enough to not trigger the
+ // Soft Navigation heuristics.
+ const p = addTextParagraphToMain(
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod",
+ /*element_timing=*/"lcp");
+ (new PerformanceObserver(list => {
+ // Once the first element is fully painted:
+ lcp_element_painted();
+ })).observe({type: "element", buffered: true});
+ await lcp_element_paint_promise;
+ // Add a smaller element that gets us over that threshold.
+ addTextParagraphToMain("dolore magna aliqua.");
+ },
+ link: link,
+ test: "Test that an image LCP followed by a smaller soft navigation LCP"
+ + " properly queues an LCP entry, even when the soft navigation is"
+ + " detected after the LCP, even when initial paints significantly"
+ + " exceed the viewport dimensions."});
+ })();
+ </script>
+</body>
+</html>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint.tentative.html
new file mode 100644
index 0000000000..cf9a9942ca
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/softnav-after-lcp-paint.tentative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ let lcp_element_painted;
+ const lcp_element_paint_promise = new Promise((r) => { lcp_element_painted = r; });
+ // Add an LCP element, but have it be small enough to not trigger the
+ // Soft Navigation heuristics.
+ const p = addTextParagraphToMain("Lorem Ipsu", /*element_timing=*/"lcp");
+ (new PerformanceObserver(list => {
+ // Once the first element is fully painted:
+ lcp_element_painted();
+ })).observe({type: "element", buffered: true});
+ await lcp_element_paint_promise;
+ // Add a smaller element that gets us over that threshold.
+ addTextParagraphToMain("m");
+ },
+ link: link,
+ test: "Test that an image LCP followed by a smaller soft navigation LCP"
+ + " properly queues an LCP entry, even when the soft navigation is"
+ + " detected after the LCP."});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/softnav-before-lcp-paint.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/softnav-before-lcp-paint.tentative.html
new file mode 100644
index 0000000000..d5dc4b74fb
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/softnav-before-lcp-paint.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ // Add an LCP element, large enough to trigger the Soft Navigation
+ // heuristics.
+ const p = addTextParagraphToMain("Lorem Ipsum", /*element_timing=*/"lcp");
+ p.id = "first_lcp";
+ // Once the first element is fully painted.
+ const observer = new PerformanceObserver(list => {
+ // Add a larger element to be the new LCP.
+ window.lcp_observer_promise = new Promise(resolve => {
+ (new PerformanceObserver(resolve)).observe({type: "element"});
+ });
+ const p2 = addTextParagraphToMain("LOREM IPSUMER", "real_lcp");
+ p2.id = "real_lcp";
+ observer.disconnect();
+ });
+ observer.observe({type: "element", buffered: true});
+ },
+ link: link,
+ validate: async () => {
+ await window.lcp_observer_promise;
+ const lcps = await getLcpEntries();
+ assert_greater_than_equal(lcps.length, 3, "Got at least 3 LCP entries");
+ assert_equals(lcps[lcps.length - 2].id, "first_lcp", "Got the first LCP");
+ assert_equals(lcps[lcps.length - 1].id, "real_lcp", "Got the real LCP");
+ },
+ test: "Test that an image LCP followed by 2 smaller soft navigation LCPs"
+ + " properly queues both LCP entries, even when the soft navigation"
+ + " is detected in between them."});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/softnav-between-lcp-render-and-paint.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/softnav-between-lcp-render-and-paint.tentative.html
new file mode 100644
index 0000000000..56c7073de6
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/softnav-between-lcp-render-and-paint.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ // Add an LCP element, but have it be small enough to not trigger the
+ // Soft Navigation heuristics.
+ const p = addTextParagraphToMain("Lorem Ipsu");
+ requestAnimationFrame(() => {
+ const p2 = addTextParagraphToMain("m");
+ });
+ },
+ link: link,
+ test: "Test that an image LCP followed by a smaller soft navigation LCP"
+ + " properly queues an LCP entry, even when the soft navigation is"
+ + " detected between the LCP's render and paint."});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/supported-entry-types.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/supported-entry-types.tentative.html
new file mode 100644
index 0000000000..4ab408e10b
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/supported-entry-types.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Soft navigations are a supported entry type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ promise_test(async () => {
+ assert_true(PerformanceObserver.supportedEntryTypes.includes(
+ "soft-navigation"));
+ }, "Soft navigations are a supported entry type");
+</script>
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection-second-softnav.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection-second-softnav.tentative.html
new file mode 100644
index 0000000000..bed27c3506
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection-second-softnav.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ let first_lcp_painted;
+ let second_lcp_painted;
+ (async () => {
+ await new Promise(r => { first_lcp_painted = r; });
+ addTextParagraphToMain("LOREM IPSUMR", "no lcp");
+ (new PerformanceObserver(second_lcp_painted)).observe({type: "element"});
+ })();
+ testSoftNavigation({
+ pushState: null,
+ clicks: 2,
+ addContent: async () => {
+ // Add an LCP element.
+ await new Promise(resolve => {
+ const p = addTextParagraphToMain("Lorem Ipsum", /*element_timing=*/"first_lcp" + counter);
+ p.id = "first_lcp" + counter;
+ (new PerformanceObserver(resolve)).observe({type: "element"});
+ });
+ if(counter) {
+ first_lcp_painted();
+ // Wait for the unrelated LCP to be painted.
+ await new Promise(r => { second_lcp_painted = r; });
+ }
+ const url = URL + "?" + counter;
+ history.pushState({}, '', url);
+ },
+ link: link,
+ validate: async () => {
+ const lcps = await getLcpEntries();
+ const ref_counter = counter-1;
+ assert_equals(lcps.length, 2 + ref_counter, "Got 2 LCP entries");
+ assert_equals(lcps[lcps.length - 1].id, "first_lcp" + ref_counter, "Got the first LCP");
+ },
+ test: "Second soft navigation text LCP discovered between user " +
+ "interaction and soft navigation detection are properly " +
+ "reported, while unrelated LCPs are ignored during that time. " +
+ "As a side effect, we also test element timing."});
+ </script>
+</body>
+</html>
+
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection.tentative.html
new file mode 100644
index 0000000000..11e82e539f
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-before-detection.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<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 src="resources/soft-navigation-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ let first_lcp_painted;
+ let second_lcp_painted;
+ (async () => {
+ await new Promise(r => { first_lcp_painted = r; });
+ addTextParagraphToMain("LOREM IPSUMR", "no lcp");
+ (new PerformanceObserver(second_lcp_painted)).observe({type: "element"});
+ })();
+ testSoftNavigation({
+ pushState: null,
+ addContent: async () => {
+ // Add an LCP element.
+ await new Promise(resolve => {
+ const p = addTextParagraphToMain("Lorem Ipsum", /*element_timing=*/"first_lcp");
+ p.id = "first_lcp";
+ (new PerformanceObserver(resolve)).observe({type: "element"});
+ });
+ first_lcp_painted();
+ // Wait for the unrelated LCP to be painted.
+ await new Promise(r => { second_lcp_painted = r; });
+ const url = URL + "?" + counter;
+ history.pushState({}, '', url);
+ },
+ link: link,
+ validate: async () => {
+ const lcps = await getLcpEntries();
+ assert_equals(lcps.length, 2, "Got 2 LCP entries");
+ assert_equals(lcps[lcps.length - 1].id, "first_lcp", "Got the first LCP");
+ },
+ test: "Text LCP discovered between user interaction and soft " +
+ "navigation detection are properly reported, while unrelated " +
+ "LCPs are ignored during that time. As a side effect, we also " +
+ "test element timing."});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html
new file mode 100644
index 0000000000..0615b513e6
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link style="font-size: 4em">Click me!</a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ const main = document.getElementById("main");
+ main.removeChild(document.getElementsByTagName("div")[0]);
+ await addImageToMain("anim-gr.png");
+ },
+ link: link,
+ test: "Test that a text LCP followup by a smaller soft navigation image"
+ + " LCP properly queues an LCP entry"});
+ </script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-image-softnav-lcp.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-image-softnav-lcp.tentative.html
new file mode 100644
index 0000000000..3a8398347c
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-image-softnav-lcp.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link style="font-size: 4em">Click me!</a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ const main = document.getElementById("main");
+ main.removeChild(document.getElementsByTagName("div")[0]);
+ await addImageToMain();
+ },
+ link: link,
+ test: "Test that a text LCP followup by a smaller soft navigation image"
+ + " LCP properly queues an LCP entry"});
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-text-softnav-lcp.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-text-softnav-lcp.tentative.html
new file mode 100644
index 0000000000..929f59f019
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/text-lcp-followed-by-text-softnav-lcp.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Detect simple soft navigation.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <div>
+ <a id=link style="font-size: 4em">Click me!</a>
+ </div>
+ </main>
+ <script>
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: () => {
+ const main = document.getElementById("main");
+ main.removeChild(document.getElementsByTagName("div")[0]);
+ addTextToDivOnMain();
+ },
+ link: link,
+ test: "Test that a text LCP followup by a smaller soft navigation text"
+ + " LCP properly queues an LCP entry"});
+ </script>
+</body>
+</html>
+
+
+
diff --git a/testing/web-platform/tests/soft-navigation-heuristics/visited-link.tentative.html b/testing/web-platform/tests/soft-navigation-heuristics/visited-link.tentative.html
new file mode 100644
index 0000000000..0bb149f00e
--- /dev/null
+++ b/testing/web-platform/tests/soft-navigation-heuristics/visited-link.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Soft navigation visited link paint tests.</title>
+<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 src="resources/soft-navigation-helper.js"></script>
+</head>
+<body>
+ <main id=main>
+ <a id=link>Click me!</a>
+ <a id=visited>link that is really long so it is the LCP</a>
+ </main>
+ <script>
+ const visited = document.getElementById("visited");
+ const fake_url = "./fake_" + Math.random();
+ visited.href = fake_url;
+ const visitFakeURLAndAddInvisibleText = () => {
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ history.replaceState({}, "", fake_url);
+ }));
+ const main = document.getElementById("main");
+ const div = document.createElement("div");
+ const text = document.createTextNode("Lorem Ipsum");
+ div.appendChild(text);
+ main.appendChild(div);
+ }
+ const link = document.getElementById("link");
+ testSoftNavigation({
+ addContent: async () => {
+ await visitFakeURLAndAddInvisibleText();
+ },
+ link: link,
+ validate: async () => {
+ await new Promise(r => step_timeout(r, 100));
+ const postVisitedLcp = await getLcpEntries();
+ assert_not_equals(postVisitedLcp[postVisitedLcp.length - 1].id,
+ "visited",
+ "Soft Nav LCP ID should not be visited");
+ },
+ test: "Test that a visited link doesn't trigger LCP after a soft " +
+ "navigation is detected"});
+ </script>
+</body>
+</html>