summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/page-visibility
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/page-visibility
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/page-visibility')
-rw-r--r--testing/web-platform/tests/page-visibility/META.yml4
-rw-r--r--testing/web-platform/tests/page-visibility/iframe-session-history.html65
-rw-r--r--testing/web-platform/tests/page-visibility/iframe-unload.html49
-rw-r--r--testing/web-platform/tests/page-visibility/minimize.html53
-rw-r--r--testing/web-platform/tests/page-visibility/onvisibilitychange.html16
-rw-r--r--testing/web-platform/tests/page-visibility/resources/blank_page_green.html10
-rw-r--r--testing/web-platform/tests/page-visibility/resources/iframe-post-load.html8
-rw-r--r--testing/web-platform/tests/page-visibility/resources/iframe-with-subframes.html6
-rw-r--r--testing/web-platform/tests/page-visibility/resources/iframe.html12
-rw-r--r--testing/web-platform/tests/page-visibility/resources/pagevistestharness.js112
-rw-r--r--testing/web-platform/tests/page-visibility/resources/unload-bubbles.html19
-rw-r--r--testing/web-platform/tests/page-visibility/resources/unload.html17
-rw-r--r--testing/web-platform/tests/page-visibility/resources/window_state_context.js45
-rw-r--r--testing/web-platform/tests/page-visibility/test_attributes_exist.html22
-rw-r--r--testing/web-platform/tests/page-visibility/test_child_document.html94
-rw-r--r--testing/web-platform/tests/page-visibility/test_default_view.html43
-rw-r--r--testing/web-platform/tests/page-visibility/test_minimize-manual.html189
-rw-r--r--testing/web-platform/tests/page-visibility/test_read_only.html37
-rw-r--r--testing/web-platform/tests/page-visibility/test_tab_state_change-manual.html187
-rw-r--r--testing/web-platform/tests/page-visibility/unload-bubbles.html22
-rw-r--r--testing/web-platform/tests/page-visibility/unload.html18
-rw-r--r--testing/web-platform/tests/page-visibility/visibility-state-entry.tentative.html70
22 files changed, 1098 insertions, 0 deletions
diff --git a/testing/web-platform/tests/page-visibility/META.yml b/testing/web-platform/tests/page-visibility/META.yml
new file mode 100644
index 0000000000..9b9aea8e8b
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/META.yml
@@ -0,0 +1,4 @@
+spec: https://w3c.github.io/page-visibility/
+suggested_reviewers:
+ - plehegar
+ - igrigorik
diff --git a/testing/web-platform/tests/page-visibility/iframe-session-history.html b/testing/web-platform/tests/page-visibility/iframe-session-history.html
new file mode 100644
index 0000000000..e023215316
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/iframe-session-history.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<title>Test the correct sequence of pagevisibility-related events in conjunction with history navigation</title>
+<link rel="author" title="Noam Rosenthal" href="mailto:noam@webkit.org">
+<link rel="help" href="https://www.w3.org/TR/page-visibility-2/">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+
+const iframePath1 = 'resources/iframe-post-load.html?v=1'
+const iframePath2 = 'resources/iframe-post-load.html?v=2'
+
+function waitForLoad(iframe) {
+ return new Promise(resolve => {
+ const callback = e => {
+ if (e.data === 'load') {
+ window.removeEventListener('message', callback)
+ resolve()
+ }
+ }
+
+ window.addEventListener('message', callback)
+ })
+}
+
+function assert_sequence(sequence) {
+ assert_equals(sequence.length, 2)
+ assert_equals(sequence[0].type, 'pagehide')
+ assert_equals(sequence[0].target, '#document')
+ assert_equals(sequence[0].visibilityState, 'visible')
+ assert_equals(sequence[0].cancelable, true)
+ assert_equals(sequence[1].type, 'visibilitychange')
+ assert_equals(sequence[1].target, '#document')
+ assert_equals(sequence[1].visibilityState, 'hidden')
+ assert_equals(sequence[1].cancelable, false)
+}
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ iframe.src = iframePath1;
+ document.body.appendChild(iframe);
+ let sequence = [];
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = iframePath1;
+ await waitForLoad(iframe);
+ const handler = t.step_func(event => sequence.push({
+ type: event.type,
+ target: event.target.nodeName,
+ cancelable: event.cancelable,
+ visibilityState: iframe.contentWindow.document.visibilityState
+ }));
+ iframe.contentWindow.document.addEventListener('visibilitychange', handler);
+ iframe.contentWindow.addEventListener('pagehide', handler);
+ iframe.contentWindow.location.href = iframePath2;
+ await waitForLoad(iframe);
+ assert_sequence(sequence);
+ sequence = [];
+ iframe.contentWindow.addEventListener('pagehide', handler);
+ iframe.contentWindow.document.addEventListener('visibilitychange', handler);
+ iframe.contentWindow.history.back();
+ await waitForLoad(iframe);
+ assert_sequence(sequence);
+}, "pagehide should be called before visibilitychange, and visibilityState should be set to hidden at the right time");
+</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/page-visibility/iframe-unload.html b/testing/web-platform/tests/page-visibility/iframe-unload.html
new file mode 100644
index 0000000000..6d049a846d
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/iframe-unload.html
@@ -0,0 +1,49 @@
+<html>
+<title>visibilitychange fires on unload with iframes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+var frameDocs = [];
+var docsLoaded = 0;
+var numFrames = 3;
+
+var ast = new async_test("visibilitychange fires on unload with iframes");
+
+function startTest() {
+ if (++docsLoaded < numFrames)
+ return;
+
+ ast.step(function () {
+ frameDocs.push(window[0].document);
+ frameDocs.push(window[0][0].document);
+ frameDocs.push(window[0][1].document);
+
+ for (var i = 0; i < frameDocs.length; ++i) {
+ frameDocs[i].addEventListener(
+ "visibilitychange",
+ onVisibilityChange.bind(null, i), false);
+ }
+
+ document.body.removeChild(document.getElementById("frame1"));
+ });
+}
+
+var checkedFrames = 0;
+
+function onVisibilityChange(i) {
+ ast.step(function () {
+ assert_equals(frameDocs[i].visibilityState, "hidden");
+ });
+ if (++checkedFrames >= numFrames) {
+ ast.done();
+ }
+}
+
+
+
+</script>
+<iframe id="frame1" src="resources/iframe-with-subframes.html"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/minimize.html b/testing/web-platform/tests/page-visibility/minimize.html
new file mode 100644
index 0000000000..ca834f74b6
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/minimize.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<title>Test different scenarios of how browser interactions are reflected by page visibility</title>
+<link rel="author" title="Noam Rosenthal" href="mailto:noam@webkit.org">
+<link rel="help" href="https://www.w3.org/TR/page-visibility-2/">
+<meta name="timeout" content="long">
+<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/window_state_context.js"></script>
+<script>
+
+const iframePath = 'resources/blank_page_green.html'
+
+promise_test(async t => {
+ const {minimize, restore} = window_state_context(t);
+ assert_equals(document.visibilityState, "visible");
+ assert_equals(document.hidden, false);
+ await minimize();
+ assert_equals(document.visibilityState, "hidden");
+ assert_equals(document.hidden, true);
+ await restore()
+ assert_equals(document.visibilityState, "visible");
+ assert_equals(document.hidden, false);
+}, "visibilityState & hidden should be affected by window being minimized/restored");
+
+promise_test(async t => {
+ const {minimize, restore} = window_state_context(t);
+ const events = [];
+ const callback = () => events.push(document.visibilityState);
+ document.addEventListener('visibilitychange', callback);
+ t.add_cleanup(() => document.removeEventListener('visibilitychange', callback));
+ await minimize();
+ await restore();
+ assert_array_equals(events, ['hidden', 'visible']);
+}, "visibilitychange event should be fired when minimized/restored")
+
+promise_test(async t => {
+ const {minimize, restore} = window_state_context(t);
+ const events = [];
+ const iframe = document.createElement('iframe');
+ iframe.src = iframePath;
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ await new Promise(resolve => iframe.onload = resolve);
+ const callback = () => events.push(iframe.contentWindow.document.visibilityState);
+ iframe.contentWindow.addEventListener('visibilitychange', callback);
+ await minimize();
+ await restore();
+ iframe.contentWindow.removeEventListener('visibilitychange', callback);
+ assert_array_equals(events, ['hidden', 'visible']);
+}, "iframe should receive visibility events when top level window is shown/hidden");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/page-visibility/onvisibilitychange.html b/testing/web-platform/tests/page-visibility/onvisibilitychange.html
new file mode 100644
index 0000000000..f854084280
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/onvisibilitychange.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>onvisibilitychange attribute is a proper event handler</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var ast = new async_test("onvisibilitychange attribute is a proper event handler");
+function startTest() {
+ var iframe1 = document.getElementById("frame1");
+ iframe1.contentWindow.document.onvisibilitychange = ast.step_func(function() {
+ ast.done();
+ });
+ frame1.parentNode.removeChild(frame1);
+}
+</script>
+<iframe id="frame1" src='resources/iframe.html'></iframe>
diff --git a/testing/web-platform/tests/page-visibility/resources/blank_page_green.html b/testing/web-platform/tests/page-visibility/resources/blank_page_green.html
new file mode 100644
index 0000000000..b8a1947b77
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/blank_page_green.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
+ <title>Green Test Page</title>
+ </head>
+ <body style="background-color:#00FF00;">
+ <h1>Placeholder</h1>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/resources/iframe-post-load.html b/testing/web-platform/tests/page-visibility/resources/iframe-post-load.html
new file mode 100644
index 0000000000..5c8f2e3a08
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/iframe-post-load.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+<head>
+<script>
+window.addEventListener('load', () => parent.postMessage('load', '*'))
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/resources/iframe-with-subframes.html b/testing/web-platform/tests/page-visibility/resources/iframe-with-subframes.html
new file mode 100644
index 0000000000..febb954369
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/iframe-with-subframes.html
@@ -0,0 +1,6 @@
+<html>
+<body onload="parent.startTest()">
+<iframe id="subIframe1" onload="parent.parent.startTest()"></iframe>
+<iframe id="subIframe2" onload="parent.parent.startTest()"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/resources/iframe.html b/testing/web-platform/tests/page-visibility/resources/iframe.html
new file mode 100644
index 0000000000..e08acb827a
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/iframe.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+<head><title>Document</title></head>
+<body>
+<h1>Document</h1>
+<script>
+onload = function() {
+ parent.startTest();
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/resources/pagevistestharness.js b/testing/web-platform/tests/page-visibility/resources/pagevistestharness.js
new file mode 100644
index 0000000000..d164d4603b
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/pagevistestharness.js
@@ -0,0 +1,112 @@
+//
+// Helper functions for Page Visibility tests
+//
+
+var VISIBILITY_STATES =
+{
+ HIDDEN: "hidden",
+ VISIBLE: "visible"
+};
+
+var feature_check = false;
+
+//
+// All test() functions in the WebPerf PageVis test suite should use pv_test() instead.
+//
+// pv_test() validates the document.hidden and document.visibilityState attributes
+// exist prior to running tests and immediately shows a failure if they do not.
+//
+
+function pv_test(func, msg, doc)
+{
+ if (!doc)
+ {
+ doc = document;
+ }
+
+ // only run the feature check once, unless func == null, in which case,
+ // this call is intended as a feature check
+ if (!feature_check)
+ {
+ feature_check = true;
+
+ var hiddenVal = doc.hidden;
+ var visStateVal = doc.visibilityState;
+
+ // show a single error that the Page Visibility feature is undefined
+ test(function()
+ {
+ assert_true(hiddenVal !== undefined && hiddenVal != null,
+ "document.hidden is defined and not null.");},
+ "document.hidden is defined and not null.");
+
+ test(function()
+ {
+ assert_true(visStateVal !== undefined && hiddenVal != null,
+ "document.visibilityState is defined and not null.");},
+ "document.visibilityState is defined and not null.");
+
+ }
+
+ if (func)
+ {
+ test(func, msg);
+ }
+}
+
+
+function test_feature_exists(doc, msg)
+{
+ if (!msg)
+ {
+ msg = "";
+ }
+ var hiddenMsg = "document.hidden is defined" + msg + ".";
+ var stateMsg = "document.visibilityState is defined" + msg + ".";
+ pv_test(function(){assert_not_equals(document.hidden, undefined, hiddenMsg);}, hiddenMsg, doc);
+ pv_test(function(){assert_not_equals(document.visibilityState, undefined, stateMsg);}, stateMsg, doc);
+}
+
+//
+// Common helper functions
+//
+
+function test_true(value, msg)
+{
+ pv_test(function() { assert_true(value, msg); }, msg);
+}
+
+function test_equals(value, equals, msg)
+{
+ pv_test(function() { assert_equals(value, equals, msg); }, msg);
+}
+
+//
+// asynchronous test helper functions
+//
+
+function add_async_result(test_obj, pass_state)
+{
+ // add assertion to manual test for the pass state
+ test_obj.step(function() { assert_true(pass_state) });
+
+ // end manual test
+ test_obj.done();
+}
+
+function add_async_result_assert(test_obj, func)
+{
+ // add assertion to manual test for the pass state
+ test_obj.step(func);
+
+ // end manual test
+ test_obj.done();
+}
+
+var open_link;
+function TabSwitch()
+{
+ //var open_link = window.open("http://www.bing.com");
+ open_link = window.open('', '_blank');
+ step_timeout(function() { open_link.close(); }, 2000);
+}
diff --git a/testing/web-platform/tests/page-visibility/resources/unload-bubbles.html b/testing/web-platform/tests/page-visibility/resources/unload-bubbles.html
new file mode 100644
index 0000000000..cc9e14f787
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/unload-bubbles.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<head><title>Document</title></head>
+<body>
+<h1>Document</h1>
+<script>
+window.addEventListener("load", function() {
+ window.addEventListener("visibilitychange", event => {
+ opener.postMessage({
+ target: event.target.nodeName,
+ state: document.visibilityState,
+ bubbles: event.bubbles
+ }, "*");
+ });
+ opener.postMessage("close", "*");
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/resources/unload.html b/testing/web-platform/tests/page-visibility/resources/unload.html
new file mode 100644
index 0000000000..b548518784
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/unload.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+<head><title>Document</title></head>
+<body>
+<h1>Document</h1>
+<script>
+onload = function() {
+ document.addEventListener("visibilitychange", onVisibilityChange, false);
+ opener.postMessage("close", "*");
+}
+
+function onVisibilityChange() {
+ opener.postMessage(document.visibilityState, "*");
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/resources/window_state_context.js b/testing/web-platform/tests/page-visibility/resources/window_state_context.js
new file mode 100644
index 0000000000..844c440812
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/resources/window_state_context.js
@@ -0,0 +1,45 @@
+function window_state_context(t) {
+ let rect = null;
+ let state = "restored";
+ t.add_cleanup(async () => {
+ if (state === "minimized") {
+ await restore();
+ }
+ });
+ async function restore() {
+ if (state !== "minimized") {
+ return;
+ }
+ state = "restoring";
+ await test_driver.set_window_rect(rect);
+ state = "restored";
+ }
+
+ async function minimize() {
+ state = "minimized";
+ rect = await test_driver.minimize_window();
+ }
+
+ function visibilityEventPromise() {
+ return new Promise((resolve) =>
+ new PerformanceObserver((entries, observer) => {
+ observer.disconnect();
+ resolve();
+ }).observe({ type: "visibility-state" })
+ );
+ }
+
+ async function minimizeAndWait() {
+ const promise = visibilityEventPromise();
+ await Promise.all([minimize(), promise]);
+ await new Promise((resolve) => t.step_timeout(resolve, 0));
+ }
+
+ async function restoreAndWait() {
+ const promise = visibilityEventPromise();
+ await Promise.all([restore(), promise]);
+ await new Promise((resolve) => t.step_timeout(resolve, 0));
+ }
+
+ return { minimize, restore, minimizeAndWait, restoreAndWait };
+}
diff --git a/testing/web-platform/tests/page-visibility/test_attributes_exist.html b/testing/web-platform/tests/page-visibility/test_attributes_exist.html
new file mode 100644
index 0000000000..748161fdf8
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/test_attributes_exist.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Page Visibility API Definition</title>
+
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="resources/pagevistestharness.js"></script>
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test validates that all of the attributes associated with the Page Visibility feature exist
+ (but does not validate that their values are correct).</p>
+
+ <div id="log"></div>
+
+ <script type="text/javascript" >
+ test_feature_exists();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/test_child_document.html b/testing/web-platform/tests/page-visibility/test_child_document.html
new file mode 100644
index 0000000000..77ec8f8fd2
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/test_child_document.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Page Visibility API Child Document Test</title>
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="resources/pagevistestharness.js"></script>
+
+ <style type="text/css">
+ iframe
+ {
+ width:250px;
+ height:250px;
+ margin-left:5px;
+ }
+
+ div.docs
+ {
+ position:relative;
+ float:left;
+ text-align:center;
+ margin:10px;
+ border:solid 1px black;
+ padding:3px;
+ }
+ </style>
+
+ <script type="text/javascript" >
+ setup({explicit_done: true});
+
+ function onload_test()
+ {
+ pv_test();
+
+ var frames = document.getElementsByTagName("iframe");
+ var doc, doc_name;
+
+ for (var i = 0; i < frames.length; i++)
+ {
+ doc = frames[i].contentDocument;
+ doc_name = "IFrame with " + frames[i].id;
+
+ pv_test(function()
+ {
+ test_feature_exists(doc, " for frame with " + frames[i].id);
+ });
+
+ test_equals(doc.visibilityState, VISIBILITY_STATES.VISIBLE,
+ "document.visibilityState for frame with " +
+ frames[i].id + " == " +
+ VISIBILITY_STATES.VISIBLE);
+ }
+
+ done();
+ }
+ </script>
+ </head>
+ <body onload="onload_test()">
+ <h1>Description</h1>
+ <p>This test validates that, within child documents, all of the Page Visibility API attributes exist,
+ are read-only, and match the value of the attributes within the parent document.</p>
+
+ <div id="log"></div>
+
+ <br/>
+
+ <div class="docs">
+ IFrame with no style attribute
+ <br/>
+ <iframe id="no style attribute" src="resources/blank_page_green.html">
+ iframes unsupported
+ </iframe>
+ </div>
+
+ <div class="docs">
+ IFrame with "display:none" style<br/>
+ <iframe id="'display:none' style" style="display:none"
+ src="resources/blank_page_green.html">
+ iframes unsupported
+ </iframe>
+ </div>
+
+ <div class="docs">
+ IFrame with "visibility:hidden" style
+ <br/>
+ <iframe id="'visibility:hidden' style" style="visibility:hidden"
+ src="resources/blank_page_green.html">
+ iframes unsupported
+ </iframe>
+ </div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/test_default_view.html b/testing/web-platform/tests/page-visibility/test_default_view.html
new file mode 100644
index 0000000000..6e2f97084f
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/test_default_view.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Page Visibility Null Default View Test</title>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="resources/pagevistestharness.js"></script>
+
+ <script type="text/javascript" >
+ setup({explicit_done: true});
+
+ function onload_test()
+ {
+ // inject a windowless subdocument as a child of the root document <html> element
+ var subDoc = document.implementation.createDocument('resources/blank_page_green.html', 'html', null);
+
+ // Test precondition: ensure subdocument has a null default view
+ test_true(subDoc.defaultView == null, "windowless subdocument generated for test has a null default view");
+
+ // check that feature exists within subdocument
+ test_feature_exists(subDoc, 'windowless subdocument');
+
+ // check that the subdocument has a hidden visibility state
+ test_true(subDoc.hidden,
+ "hidden == true for windowless subdocuments with a null default view");
+ test_equals(subDoc.visibilityState, VISIBILITY_STATES.HIDDEN,
+ "visibilityState == " + VISIBILITY_STATES.HIDDEN +
+ " for windowless subdocuments with a null default view");
+
+ done();
+ }
+ </script>
+ </head>
+ <body onload="onload_test()">
+ <h1>Description</h1>
+ <p>This test validates that document.hidden == false and
+ document.visibilityState == "visible" for windowless subdocuments.</p>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/test_minimize-manual.html b/testing/web-platform/tests/page-visibility/test_minimize-manual.html
new file mode 100644
index 0000000000..27fd0d7cef
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/test_minimize-manual.html
@@ -0,0 +1,189 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Page Visibility API Operation While Minimizing Browser Window</title>
+ <meta name="flags" content="interact" />
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="resources/pagevistestharness.js"></script>
+
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test validates that the page properly becomes hidden and visible due to minimizing, maximizing, and
+ restoring the browser window.</p>
+
+ <h1>Manual Test Steps:</h1>
+ <p>
+ <ol>
+ <li> Ensure this page is the foreground and click the "Start Test"</li>
+ <li> Minimize the browser</li>
+ <li> Restore or maximize the browser</li>
+ </ol>
+ Note: This test will automatically timeout and fail if not completed within 60 seconds.
+ </p>
+
+ <button onclick="start_test();">Start Test</button>
+
+ <div id="log"></div>
+
+ <br />
+ IFrame with default style:
+ <br />
+ <iframe id="childDocShown" src="resources/blank_page_green.html">
+ iframes unsupported
+ </iframe>
+ <hr />
+ IFrame with "display:none" style:
+ <br />
+ <iframe id="childDocHidden" src="resources/blank_page_green.html" style="display:none">
+ iframes unsupported
+ </iframe>
+
+ <script type="text/javascript" >
+ var child_doc_shown = null;
+ var child_doc_hidden = null;
+ var transition_mode;
+ var manual_test = null;
+
+ var expected_notification_count = 2;
+ var notification_count = new Array();
+ var page_docs = new Array();
+ var notification_handlers = new Array();
+ var two_notifications = false;
+
+ var PAGE_HIDDEN_VAL = "hidden";
+ var PAGE_VISIBLE_VAL = "visible";
+
+ setup({ explicit_done: true });
+
+ function test_transition_init()
+ {
+ child_doc_shown = document.getElementById("childDocShown").contentDocument;
+ child_doc_hidden = document.getElementById("childDocHidden").contentDocument;
+
+ // fill in data for page documents
+ page_docs.push([document, "document"]);
+ page_docs.push([child_doc_shown, "document.getElementById(\"childDocShown\").contentDocument"]);
+ page_docs.push([child_doc_hidden, "document.getElementById(\"childDocHidden\").contentDocument"]);
+
+ notification_handlers[0] = function(){ VerifyNotification(0); };
+ notification_handlers[1] = function(){ VerifyNotification(1); };
+ notification_handlers[2] = function(){ VerifyNotification(2); };
+
+ for (var i in page_docs)
+ {
+ notification_count[i] = 0;
+ page_docs[i][0].addEventListener("visibilitychange", notification_handlers[i]);
+ }
+
+ test_true(!document.hidden, "Page is visible on load.");
+ test_true((!child_doc_shown.hidden) && (!child_doc_hidden.hidden),
+ "All IFrame child documents are visible on load.");
+
+ document.addEventListener("visibilitychange", notification_handlers[0]);
+ document.addEventListener("visibilitychange", VerifyTwoNotifications);
+
+ manual_test = AddManual("Browser minimization has occurred.");
+ }
+
+ function VerifyNotification(doc_index)
+ {
+ doc = page_docs[doc_index][0];
+ docName = page_docs[doc_index][1];
+
+ notification_count[doc_index]++;
+ switch (notification_count[doc_index])
+ {
+ case 1:
+ // First step, check page visibility after tab deselection / minimization.
+ // hidden should change to true; visibilityState should change to "hidden"
+ test_true(doc.hidden, docName + ".hidden == true (after browser frame minimization)");
+ test_true(doc.visibilityState == PAGE_HIDDEN_VAL,
+ docName + ".visibilityState == \"hidden\" (after browser frame minimization)");
+
+ break;
+
+ case 2:
+ //Second step, check page visibility after tab reselection / maximization / restoration.
+ // hidden should change to false; visibilityState should change to "visible"
+ test_true(!doc.hidden,
+ docName + ".hidden == false (after browser frame maximization / restoration)");
+ test_true(doc.visibilityState == PAGE_VISIBLE_VAL,
+ docName + ".visibilityState == \"visible\" (after browser frame maximization / " +
+ "restoration)");
+
+ // perform tests specific to the main document
+ if (doc == document)
+ {
+ //Verify that a second registration to a different callback also occurred
+ test_true(two_notifications, "Two registrations (different callbacks) occurred.");
+
+ //Verify that a second registration to the same callback did not occur
+ test_equals(notification_count[doc_index],
+ expected_notification_count,
+ "Two registrations (same callback) did not occur.");
+
+ // pass the manual item associated with these tests
+ AddManualResult(manual_test, true);
+
+ document.removeEventListener("visibilitychange", VerifyTwoNotifications);
+
+ // schedule the rollup
+ setTimeout(VerifyAllNotifications, 200);
+ }
+
+ //Remove all event listeners and verify that the event does not fire
+ doc.removeEventListener("visibilitychange", notification_handlers[doc_index]);
+ break;
+ case 3:
+ //This step should not have occurred since the event handlers were cleared
+ test_true(false, "Event did not fire when event listener is removed.");
+
+ //On final step, schedule the rollup
+ setTimeout(done, 2000);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ function VerifyAllNotifications()
+ {
+ //On final step, schedule the rollup
+ setTimeout(done, 1000);
+ }
+
+ function VerifyTwoNotifications()
+ {
+ //This is a duplicate registration on visibilitychange and
+ //should never get fired. Check that duplicate_notification
+ //is false to verify that this never occurred.
+ two_notifications = true;
+ }
+
+ // Manual Test helper functions
+ function AddManual(test)
+ {
+ // add asynchronous test for manual tests
+ return async_test(test);
+ }
+
+ function AddManualResult(oManualTest, passState)
+ {
+ // add assertion to manual test for the pass state
+ oManualTest.step(function() {assert_true(passState)});
+
+ // end manual test
+ oManualTest.done();
+ }
+
+ function start_test()
+ {
+ test_transition_init();
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/test_read_only.html b/testing/web-platform/tests/page-visibility/test_read_only.html
new file mode 100644
index 0000000000..6d3702292d
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/test_read_only.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Page Visibility API is Read Only</title>
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="resources/pagevistestharness.js"></script>
+
+ <script type="text/javascript">
+ setup({ explicit_done: true });
+ function onload_test()
+ {
+ //check for feature definition first before attempting to overwrite
+ pv_test();
+
+ //Check document.hidden
+ document.hidden = "new value";
+ test_true(document.hidden !== "new value", 'document.hidden is read only.');
+
+ //Check document.visibilityState
+ document.visibilityState = "new value";
+ test_true(document.visibilityState !== "new value",
+ "document.visibilityState is read only.");
+
+ done();
+ }
+ </script>
+ </head>
+ <body onload="onload_test();">
+ <h1>Description</h1>
+ <p>This test validates that the Page Visibility attributes are read only.</p>
+
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/test_tab_state_change-manual.html b/testing/web-platform/tests/page-visibility/test_tab_state_change-manual.html
new file mode 100644
index 0000000000..75d1da6e93
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/test_tab_state_change-manual.html
@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Page Visibility API Operation While Changing Tabs</title>
+ <meta name="flags" content="interact" />
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="resources/pagevistestharness.js"></script>
+
+ </head>
+ <body>
+ <h1>Description</h1>
+ <p>This test validates that the page properly becomes hidden and visible due to switching tabs.</p>
+
+ <h1>Manual Test Steps:</h1>
+ <p>
+ <ol>
+ <li> Ensure this page is the foreground and click the "Start Test"</li>
+ <li> Switch to another tab</li>
+ <li> Return to the current tab containing this test page</li>
+ </ol>
+ Note: This test will automatically timeout and fail if not completed within 60 seconds.
+ </p>
+
+ <button onclick="start_test();">Start Test</button>
+
+ <div id="log"></div>
+
+ <br />
+ IFrame with default style:
+ <br />
+ <iframe id="childDocShown" src="resources/blank_page_green.html">
+ iframes unsupported
+ </iframe>
+ <hr />
+ IFrame with "display:none" style:
+ <br />
+ <iframe id="childDocHidden" src="resources/blank_page_green.html" style="display:none">
+ iframes unsupported
+ </iframe>
+
+ <script type="text/javascript" >
+ var child_doc_shown = null;
+ var child_doc_hidden = null;
+ var transition_mode;
+ var manual_test = null;
+
+ var expected_notification_count = 2;
+ var notification_count = new Array();
+ var page_docs = new Array();
+ var notification_handlers = new Array();
+ var two_notifications = false;
+
+ var PAGE_HIDDEN_VAL = "hidden";
+ var PAGE_VISIBLE_VAL = "visible";
+
+ setup({ explicit_done: true });
+
+ function test_transition_init()
+ {
+ child_doc_shown = document.getElementById("childDocShown").contentDocument;
+ child_doc_hidden = document.getElementById("childDocHidden").contentDocument;
+
+ // fill in data for page documents
+ page_docs.push([document, "document"]);
+ page_docs.push([child_doc_shown, "document.getElementById(\"childDocShown\").contentDocument"]);
+ page_docs.push([child_doc_hidden, "document.getElementById(\"childDocHidden\").contentDocument"]);
+
+ notification_handlers[0] = function(){ VerifyNotification(0); };
+ notification_handlers[1] = function(){ VerifyNotification(1); };
+ notification_handlers[2] = function(){ VerifyNotification(2); };
+
+ for (var i in page_docs)
+ {
+ notification_count[i] = 0;
+ page_docs[i][0].addEventListener("visibilitychange", notification_handlers[i]);
+ }
+
+ test_true(!document.hidden, "Page is visible on load.");
+ test_true((!child_doc_shown.hidden) && (!child_doc_hidden.hidden),
+ "All IFrame child documents are visible on load.");
+
+ document.addEventListener("visibilitychange", notification_handlers[0]);
+ document.addEventListener("visibilitychange", VerifyTwoNotifications);
+
+ manual_test = AddManual("Browser tab switch has occurred.");
+ }
+
+ function VerifyNotification(doc_index)
+ {
+ doc = page_docs[doc_index][0];
+ docName = page_docs[doc_index][1];
+
+ notification_count[doc_index]++;
+ switch (notification_count[doc_index])
+ {
+ case 1:
+ // First step, check page visibility after tab deselection / minimization.
+ // hidden should change to true; visibilityState should change to "hidden"
+ test_true(doc.hidden, docName + ".hidden == true (after tab switch)");
+ test_true(doc.visibilityState == PAGE_HIDDEN_VAL,
+ docName + ".visibilityState == \"hidden\" (after tab switch)");
+
+ break;
+
+ case 2:
+ //Second step, check page visibility after tab reselection / maximization / restoration.
+ // hidden should change to false; visibilityState should change to "visible"
+ test_true(!doc.hidden,
+ docName + ".hidden == false (after return to original tab)");
+ test_true(doc.visibilityState == PAGE_VISIBLE_VAL,
+ docName + ".visibilityState == \"visible\" (after return to original tab)");
+
+ // perform tests specific to the main document
+ if (doc == document)
+ {
+ //Verify that a second registration to a different callback also occurred
+ test_true(two_notifications, "Two registrations (different callbacks) occurred.");
+
+ //Verify that a second registration to the same callback did not occur
+ test_equals(notification_count[doc_index],
+ expected_notification_count,
+ "Two registrations (same callback) did not occur.");
+
+ // pass the manual item associated with these tests
+ AddManualResult(manual_test, true);
+
+ document.removeEventListener("visibilitychange", VerifyTwoNotifications);
+
+ // schedule the rollup
+ setTimeout(VerifyAllNotifications, 200);
+ }
+
+ //Remove all event listeners and verify that the event does not fire
+ doc.removeEventListener("visibilitychange", notification_handlers[doc_index]);
+ break;
+ case 3:
+ //This step should not have occurred since the event handlers were cleared
+ test_true(false, "Event did not fire when event listener is removed.");
+
+ //On final step, schedule the rollup
+ setTimeout(done, 2000);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ function VerifyAllNotifications()
+ {
+ //On final step, schedule the rollup
+ setTimeout(done, 1000);
+ }
+
+ function VerifyTwoNotifications()
+ {
+ //This is a duplicate registration on visibilitychange and
+ //should never get fired. Check that duplicate_notification
+ //is false to verify that this never occurred.
+ two_notifications = true;
+ }
+
+ // Manual Test helper functions
+ function AddManual(test)
+ {
+ // add asynchronous test for manual tests
+ return async_test(test);
+ }
+
+ function AddManualResult(oManualTest, passState)
+ {
+ // add assertion to manual test for the pass state
+ oManualTest.step(function() {assert_true(passState)});
+
+ // end manual test
+ oManualTest.done();
+ }
+
+ function start_test()
+ {
+ test_transition_init();
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/page-visibility/unload-bubbles.html b/testing/web-platform/tests/page-visibility/unload-bubbles.html
new file mode 100644
index 0000000000..19b169c5e6
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/unload-bubbles.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>visibilitychange event bubbles when fired on unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function(t) {
+ var w = window.open("resources/unload-bubbles.html");
+ window.onmessage = t.step_func(function(event) {
+ if (event.data === "close") {
+ w.close();
+ return;
+ }
+
+ const {state, target, bubbles} = event.data
+ assert_equals(state, "hidden");
+ assert_equals(target, "#document");
+ assert_equals(bubbles, true);
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/page-visibility/unload.html b/testing/web-platform/tests/page-visibility/unload.html
new file mode 100644
index 0000000000..9b7dcb8c66
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/unload.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>visibilitychange fires on unload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function(t) {
+ var w = window.open("resources/unload.html");
+ window.onmessage = t.step_func(function(event) {
+ if (event.data === "close") {
+ w.close();
+ return;
+ }
+ assert_equals(event.data, "hidden");
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/page-visibility/visibility-state-entry.tentative.html b/testing/web-platform/tests/page-visibility/visibility-state-entry.tentative.html
new file mode 100644
index 0000000000..0dbe634d25
--- /dev/null
+++ b/testing/web-platform/tests/page-visibility/visibility-state-entry.tentative.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<title>Test VisibleStateEntry</title>
+<link rel="author" title="Noam Rosenthal" href="mailto:nrosenthal@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#page-visibility">
+<meta name="timeout" content="long">
+<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/window_state_context.js"></script>
+<script>
+
+setup(() => {
+ assert_implements(window.VisibilityStateEntry, 'VisibilityStateEntry is not supported.');
+});
+
+promise_test(async t => {
+ const {minimizeAndWait, restoreAndWait} = window_state_context(t);
+ const all = performance.getEntries();
+ let entries = performance.getEntriesByType("visibility-state");
+ assert_equals(entries.length, 1);
+ assert_equals(entries[0].name, "visible");
+ assert_equals(entries[0].startTime, 0);
+ assert_equals(entries[0].duration, 0);
+ await minimizeAndWait();
+ entries = performance.getEntriesByType("visibility-state");
+ assert_equals(entries.length, 2);
+ assert_equals(entries[1].name, "hidden");
+ assert_equals(entries[1].duration, 0);
+ await restoreAndWait();
+ entries = performance.getEntriesByType("visibility-state");
+ assert_equals(entries.length, 3);
+ assert_equals(entries[2].name, "visible");
+ assert_equals(entries[2].duration, 0);
+}, "Hiding/showing the page should create visibility-state entries");
+
+promise_test(async t => {
+ const {minimizeAndWait} = window_state_context(t);
+ await minimizeAndWait();
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/blank_page_green.html";
+ const loaded = new Promise(resolve => iframe.addEventListener("load", resolve));
+ t.add_cleanup(() => iframe.remove());
+ document.body.appendChild(iframe);
+ await loaded;
+ const entries = iframe.contentWindow.performance.getEntriesByType("visibility-state");
+ assert_equals(entries[0].name, "hidden");
+ assert_equals(entries[0].startTime, 0);
+}, "If a page starts as hidden, the first visibility-state entry should be hidden");
+
+promise_test(async t => {
+ const {minimize, restore} = window_state_context(t);
+ const observed = new Promise(resolve => new PerformanceObserver(list => {
+ if (list.getEntries()[0].name === "visible")
+ resolve();
+ }).observe({entryTypes: ['visibility-state']}));
+ await minimize();
+ await restore();
+ await observed;
+}, "Visibility state entries should be queued to performance observers");
+
+promise_test(async t => {
+ const entry = await new Promise(resolve => new PerformanceObserver(
+ (list, observer) => {
+ observer.disconnect();
+ resolve(list.getEntries()[0]);
+ }).observe({type: "visibility-state", buffered: true}));
+ assert_equals(entry.name, "visible");
+}, "Visibility state observers should respect the buffered flag");
+</script>