summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/visual-viewport
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/visual-viewport
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/visual-viewport')
-rw-r--r--testing/web-platform/tests/visual-viewport/META.yml3
-rw-r--r--testing/web-platform/tests/visual-viewport/helper-resize-event-on-load-overflowing-page.html47
-rw-r--r--testing/web-platform/tests/visual-viewport/page-and-offset-in-iframe.html86
-rw-r--r--testing/web-platform/tests/visual-viewport/resize-event-order.html72
-rw-r--r--testing/web-platform/tests/visual-viewport/scroll-event-order.html98
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-attribute-event-handlers-manual.html75
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-dimensions-custom-scrollbars-manual.html158
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-dimensions-scrollbars-manual.html146
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-no-resize-event-on-overflow-recalc.html50
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-offset-manual.html151
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-page-manual.html108
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-read-size-causes-layout.html35
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-read-size-in-iframe-causes-layout.html36
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-resize-event-manual.html92
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-show.html43
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-size-change.html43
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-resize-event-on-load-overflowing-page.html32
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-scale-iframe-manual.html77
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-scale-manual.html81
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-scroll-event-manual.html182
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-scrollbars-cause-resize.html74
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-segments.tentative.html15
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-type.html31
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-unscaled-scale-iframe.html33
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-unscaled-scale.html26
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll-iframe.html54
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll.html45
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-unscaled-size-iframe.html65
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-unscaled-size.html51
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport-url-bar-changes-height-manual.html87
-rw-r--r--testing/web-platform/tests/visual-viewport/viewport_support.js180
31 files changed, 2276 insertions, 0 deletions
diff --git a/testing/web-platform/tests/visual-viewport/META.yml b/testing/web-platform/tests/visual-viewport/META.yml
new file mode 100644
index 0000000000..5499f77335
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/META.yml
@@ -0,0 +1,3 @@
+spec: https://wicg.github.io/visual-viewport/
+suggested_reviewers:
+ - bokand
diff --git a/testing/web-platform/tests/visual-viewport/helper-resize-event-on-load-overflowing-page.html b/testing/web-platform/tests/visual-viewport/helper-resize-event-on-load-overflowing-page.html
new file mode 100644
index 0000000000..c036b16ec8
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/helper-resize-event-on-load-overflowing-page.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Resize Event On Load Overflowing Page</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script>
+ var opener = window.opener;
+ var numViewResizes = 0;
+ window.visualViewport.addEventListener('resize', function() {
+ numViewResizes++;
+ });
+
+ window.addEventListener('load', function() {
+ requestAnimationFrame(function() {
+ requestAnimationFrame(
+ opener.t.step_func_done(function() {
+ var isOverlay = opener.calculateScrollbarThickness() == 0;
+ opener.assert_equals(numViewResizes, isOverlay ? 0 : 1);
+ }));
+ });
+ });
+ </script>
+ <style>
+ html {
+ height: 100%;
+ }
+ body {
+ /* Ensure overflow */
+ height: 200%;
+ }
+ #log {
+ overflow: auto;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Resize Event On Load Overflowing Page</h1>
+ <h4>
+ Test Description: This test ensures that we fire a resize event against
+ window.visualViewport if the page has overflow (since this creates a scrollbar
+ and thus changes the viewport size).
+ </h4>
+ <div id="log"></div>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/visual-viewport/page-and-offset-in-iframe.html b/testing/web-platform/tests/visual-viewport/page-and-offset-in-iframe.html
new file mode 100644
index 0000000000..086c816956
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/page-and-offset-in-iframe.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: page and offset values in iframe</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-actions.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="viewport_support.js"></script>
+ <style>
+ iframe {
+ width: 200px;
+ height: 300px;
+ border: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Page and offset values in iframe</h1>
+ <h4>
+ Test Description: This test checks for correct visualViewport values in
+ an iframe.
+ </h4>
+ <iframe></iframe>
+ </body>
+ <script>
+ var iframe = frames[0].window;
+
+ // Add overflow to the iframe so it can scroll.
+ iframe.document.body.style.width = "2000px";
+ iframe.document.body.style.height = "2000px";
+ iframe.scrollTo(1000, 1200);
+
+ promise_test(async t => {
+ assert_equals(visualViewport.scale, 1,
+ "[PRECONDITION] Start off unzoomed");
+ assert_equals(visualViewport.offsetLeft, 0,
+ "[PRECONDITION] No offsetLeft to start");
+ assert_equals(visualViewport.offsetTop, 0,
+ "[PRECONDITION] No offsetTop to start");
+
+ // The iframe's visual viewport isn't yet offset
+ assert_equals(iframe.visualViewport.offsetLeft, 0,
+ "offsetLeft must be 0");
+ assert_equals(iframe.visualViewport.offsetTop, 0,
+ "offsetTop must be 0");
+
+ // However, page values should reflect layout viewport scrolling.
+ assert_equals(iframe.visualViewport.pageLeft, 1000,
+ "pageLeft must reflect location in document.");
+ assert_equals(iframe.visualViewport.pageTop, 1200,
+ "pageTop must reflect location in document.");
+
+ // Zoom in and pan the viewport to ensure the iframe is independent
+ // of the root frame.
+ await pinchZoomIn();
+
+ // These values are arbitrary since the amount of pinch-zoom caused
+ // by pinchZoomIn will differ but we only care that the iframe's
+ // values aren't changed.
+ assert_greater_than(visualViewport.scale, 1.2,
+ "Pinch zoom must have increased scale");
+ assert_greater_than(visualViewport.offsetLeft, 10,
+ "Pinch zoom must have offsetLeft visualViewport");
+ assert_greater_than(visualViewport.offsetTop, 10,
+ "Pinch zoom must have offsetTop visualViewport");
+
+ // The iframe's visualViewport is independent of the root frame's so
+ // none of the values should have changed.
+ assert_equals(iframe.visualViewport.offsetLeft, 0,
+ "After zooming, offsetLeft must remain 0");
+ assert_equals(iframe.visualViewport.offsetTop, 0,
+ "After zooming, offsetTop must remain 0");
+ assert_equals(iframe.visualViewport.pageLeft, 1000,
+ "After zooming, pageLeft must reflect location in document.");
+ assert_equals(iframe.visualViewport.pageTop, 1200,
+ "After zooming, pageTop must reflect location in document.");
+
+ assert_equals(iframe.visualViewport.scale, 1,
+ "Iframe's visualViewport must not be scaled");
+ }, "VisualViewport page and offset values in iframe");
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/resize-event-order.html b/testing/web-platform/tests/visual-viewport/resize-event-order.html
new file mode 100644
index 0000000000..41a13728bc
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/resize-event-order.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<title>Visual Viewport Resize Event Order</title>
+<meta charset=utf-8>
+<link rel="help" href="https://wicg.github.io/visual-viewport/index.html">
+<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>
+<style>
+ iframe {
+ width: 300px;
+ height: 300px;
+ }
+</style>
+
+<body>
+ <iframe srcdoc="<!DOCTYPE html>"></iframe>
+</body>
+
+<script>
+
+async function oneRaf(win) {
+ return new Promise((resolve) => {
+ win.requestAnimationFrame(resolve);
+ });
+}
+
+// Runs the test on the given window object, asserts that event handlers on the
+// DOMWindow object are fired before those on the VisualViewport object.
+// `resizeFunc` is used to perform the resize.
+async function runTest(win, resizeFunc) {
+ const resize_events = [];
+
+ win.onresize = () => { resize_events.push('window-attribute'); }
+ win.addEventListener('resize', () => { resize_events.push('window-addEventListener'); });
+ win.visualViewport.onresize = () => { resize_events.push('visualViewport-attribute'); }
+ win.visualViewport.addEventListener('resize', () => {
+ resize_events.push('visualViewport-addEventListener'); });
+
+ assert_equals(resize_events.toString(), '', 'PRECONDITION');
+ resizeFunc(500, 600);
+
+ await oneRaf(win);
+
+ assert_equals(resize_events.toString(),
+ 'window-attribute,window-addEventListener,' +
+ 'visualViewport-attribute,visualViewport-addEventListener');
+}
+
+onload = () => {
+ // Test the event order in a top-level window which we will programmatically
+ // resize.
+ promise_test(async t => {
+ let popup = null;
+ test_driver.bless('Open a popup in a new window', () => {
+ popup = window.open('about:blank', 'newwindow', 'width=300,height=300');
+ });
+ await t.step_wait(() => popup != null, "Opened popup window");
+
+ await runTest(popup, (x, y) => {popup.resizeTo(x, y);});
+ }, 'Popup: DOMWindow resize fired before VisualViewport.');
+
+ // Also test the resize resulting from an iframe's size change.
+ promise_test(async t => {
+ const iframe = frames[0];
+ await runTest(iframe, (x, y) => {iframe.frameElement.style.width = x + 'px';
+ iframe.frameElement.style.height = y + 'px';
+ iframe.frameElement.offsetWidth; /* force reflow */});
+ }, 'iframe: DOMWindow resize fired before VisualViewport.');
+}
+
+</script>
diff --git a/testing/web-platform/tests/visual-viewport/scroll-event-order.html b/testing/web-platform/tests/visual-viewport/scroll-event-order.html
new file mode 100644
index 0000000000..da5b77c0e2
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/scroll-event-order.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<title>Visual Viewport Scroll Event Order</title>
+<meta charset=utf-8>
+<link rel="help" href="https://wicg.github.io/visual-viewport/index.html">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="viewport_support.js"></script>
+<style>
+ #target {
+ width: 100px;
+ height: 100px;
+ background-color: red;
+ position: absolute;
+ top: 300vh;
+ }
+
+ #scroller1,#scroller2 {
+ width: 200px;
+ height: 200px;
+ overflow: auto;
+ }
+
+ #spacer {
+ width: 80px;
+ height: 1000px;
+ background-image: linear-gradient(45deg, #808080 25%, transparent 25%),
+ linear-gradient(-45deg, #808080 25%, transparent 25%),
+ linear-gradient(45deg, transparent 75%, #808080 75%),
+ linear-gradient(-45deg, transparent 75%, #808080 75%);
+ background-size: 40px 40px;
+ background-position: 0 0, 0 20px, 20px -20px, -20px 0px;
+ }
+</style>
+
+<body>
+ <div id="target"></div>
+ <div id="scroller1"><div id="spacer"></div></div>
+ <div id="scroller2"><div id="spacer"></div></div>
+</body>
+
+<script>
+
+async function oneRaf() {
+ return new Promise((resolve) => {
+ window.requestAnimationFrame(resolve);
+ });
+}
+
+const scroller1 = document.getElementById('scroller1');
+const scroller2 = document.getElementById('scroller2');
+
+promise_test(async t => {
+ // Pinch-zoom in so that the scrollIntoView call below causes scrolling in
+ // both the layout and visual viewports within the same rAF.
+ await pinchZoomIn();
+ assert_greater_than(window.visualViewport.scale, 1, 'Must have zoomed in');
+
+ await oneRaf();
+
+ const scroll_events = [];
+
+ // Register the scroll handlers on the window, visualViewport, and both
+ // <div> scrollers.
+ {
+ window.onscroll = () => { scroll_events.push('window-attribute'); }
+ window.addEventListener('scroll', () => { scroll_events.push('window-addEventListener'); });
+ window.visualViewport.onscroll = () => { scroll_events.push('visualViewport-attribute'); }
+ window.visualViewport.addEventListener('scroll', () => {
+ scroll_events.push('visualViewport-addEventListener'); });
+ scroller1.addEventListener('scroll',
+ () => { scroll_events.push('scroller1'); });
+ scroller2.addEventListener('scroll',
+ () => { scroll_events.push('scroller2'); });
+ }
+
+ // Cause scrolling in each scroller and scrollIntoView so that the layout
+ // and visual viewports both scroll.
+ scroller1.scrollTop = 200;
+ document.getElementById('target').scrollIntoView();
+ scroller2.scrollTop = 200;
+
+ // Wait a rAF since scroll events are delievered as part of the event loop.
+ await oneRaf();
+
+ // The scroll events must be delivered in the order they were executed,
+ // scroller1 first, then the viewport (window then visualViewport), then
+ // scroller2.
+ assert_equals(scroll_events.toString(),
+ 'scroller1,' +
+ 'window-attribute,window-addEventListener,' +
+ 'visualViewport-attribute,visualViewport-addEventListener,' +
+ 'scroller2');
+}, "Scroll event ordering");
+
+</script>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-attribute-event-handlers-manual.html b/testing/web-platform/tests/visual-viewport/viewport-attribute-event-handlers-manual.html
new file mode 100644
index 0000000000..896f6cd98b
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-attribute-event-handlers-manual.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Attribute event handlers test</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_timeout: true, explicit_done: true})
+ </script>
+ </head>
+ <body>
+ <h1>Viewport: Attribute event handlers test</h1>
+ <h4>
+ Test Description: This test checks that attribute event handlers function correctly.
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ var didFireResize = false;
+ var didFireScroll = false;
+
+ addManualTestStep(
+ function() {
+ test(() => {
+ assert_not_equals(typeof(window.visualViewport.onresize),
+ 'undefined',
+ 'onresize is defined');
+ assert_not_equals(typeof(window.visualViewport.onscroll),
+ 'undefined',
+ 'onscroll is defined');
+ }, 'Attribute event handlers defined');
+
+ window.visualViewport.onresize = function(e) {
+ didFireResize = true;
+ };
+ window.visualViewport.onscroll = function(e) {
+ didFireScroll = true;
+ };
+ },
+ null,
+ '1. Pinch-zoom into the screen anywhere by any amount.');
+
+ addManualTestStep(
+ function() {
+ test(() => {
+ assert_true(didFireResize, 'Resize event fired');
+ assert_true(didFireScroll, 'Scroll event fired');
+ }, 'Event handler functionality');
+ continueBtn.remove();
+ },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-dimensions-custom-scrollbars-manual.html b/testing/web-platform/tests/visual-viewport/viewport-dimensions-custom-scrollbars-manual.html
new file mode 100644
index 0000000000..8fcbfe1391
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-dimensions-custom-scrollbars-manual.html
@@ -0,0 +1,158 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Dimensions with custom scrollbars</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_done: true, explicit_timeout: true});
+ </script>
+ <style>
+ #spacer {
+ width: 10000px;
+ height: 10000px;
+ position: absolute;
+ visibility: hidden;
+ }
+ ::-webkit-scrollbar {
+ width: 20px;
+ height: 25px;
+ }
+
+ ::-webkit-scrollbar-track {
+ background-color: #b46868;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background-color: rgba(0, 0, 0, 0.2);
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Dimensions with custom scrollbars</h1>
+ <h4>
+ Test Description: Tests the viewport dimensions correctly account for
+ custom scrollbars
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip</button>
+ <p>
+ Skip this test if your browser doesn't support custom scrollbars or
+ browser-zoom (Ctrl+/-).
+ </p>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ <div id="spacer"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ var originalWidth = 0;
+ var originalHeight = 0;
+ var originalInnerWidth = 0;
+ var originalInnerHeight = 0;
+
+ addManualTestStep(
+ function() {},
+ null,
+ '1. Ensure the browser is at the default pinch and browser zoom ' +
+ 'levels (100%). Most browsers: ctrl+0');
+
+ addManualTestStep(
+ function() {
+ originalWidth = window.visualViewport.width;
+ originalHeight = window.visualViewport.height;
+ // Remember the inner dimensions here for the next test to
+ // address an edge case where originalInnerWidth is an odd
+ // number of pixels. The test expects that at 2x browser-zoom,
+ // visualViewport.width = innerWidth - customScrollbarThickness
+ // The equality only holds if originalInnerWidth / innerWidth is
+ // exactly 2, which is not the case in the aforementioned
+ // scenario because innerWidth always has to be an integer
+ // number of CSS pixels. Ditto for the height computation.
+ originalInnerWidth = window.innerWidth;
+ originalInnerHeight = window.innerHeight;
+
+ assert_equals(
+ window.visualViewport.width,
+ window.innerWidth - 20,
+ "Custom scrollbar width subtracted from viewport.");
+ assert_equals(
+ window.visualViewport.height,
+ window.innerHeight - 25,
+ "Custom scrollbar height subtracted from viewport.");
+ },
+ 'No zoom or scale applied',
+ '2. Browser-zoom into 200% (ctrl +)');
+
+ addManualTestStep(
+ function() {
+ // Ensure we zoomed in to about what we expect.
+ assert_approx_equals(
+ originalInnerWidth / window.innerWidth,
+ 2.0,
+ 0.1,
+ "Browser zoom to correct level");
+
+ // The custom scrollbars are also 2x larger on the screen, so
+ // the viewport is smaller than half of the original size.
+ assert_equals(
+ window.visualViewport.width,
+ originalInnerWidth / 2 - 20,
+ "Custom scrollbar width subtracted from viewport.");
+ assert_equals(
+ window.visualViewport.height,
+ originalInnerHeight / 2 - 25,
+ "Custom scrollbar height subtracted from viewport.");
+ },
+ 'With 200% browser zoom',
+ '3. Reset browser zoom (ctrl+0).');
+
+ addManualTestStep(
+ showPinchWidget.bind(null, 2.0, 0, 0, continueTest),
+ null,
+ 'Pinch-zoom dialog in progress');
+
+ addManualTestStep(
+ function() {
+ assert_approx_equals(
+ window.visualViewport.scale, 2, 0.2, "Pinch zoom to correct scale");
+
+ // Scrollbars do not grow with pinch-zoom so they take up fewer
+ // CSS pixels as you zoom in.
+ assert_approx_equals(
+ window.visualViewport.width,
+ originalWidth / window.visualViewport.scale,
+ 1,
+ "Custom scrollbar width subtracted from viewport.");
+ assert_approx_equals(
+ window.visualViewport.height,
+ originalHeight / window.visualViewport.scale,
+ 1,
+ "Custom scrollbar width subtracted from viewport.");
+ },
+ 'With ~200% pinch zoom',
+ '4. Pinch-zoom out.');
+
+ addManualTestStep(
+ function() { continueBtn.remove(); },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-dimensions-scrollbars-manual.html b/testing/web-platform/tests/visual-viewport/viewport-dimensions-scrollbars-manual.html
new file mode 100644
index 0000000000..493de254dc
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-dimensions-scrollbars-manual.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Dimensions with scrollbars</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_done: true, explicit_timeout: true});
+ </script>
+ <style>
+ #spacer {
+ width: 10000px;
+ height: 10000px;
+ position: absolute;
+ visibility: hidden;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Dimensions with scrollbars</h1>
+ <h4>
+ Test Description: Tests the viewport dimensions correctly account for
+ scrollbars
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ <p>
+ If your browser doesn't support browser-zoom (Ctrl+/-, e.g. Mobile
+ Browsers) please skip.
+ </p>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ <div id="spacer"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+ var scrollbarThickness = calculateScrollbarThickness();
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ var originalWidth = 0;
+ var originalHeight = 0;
+ var originalInnerWidth = 0;
+ var originalVisualViewportWidthExpectation = 0;
+ var originalVisualViewportHeightExpectation = 0;
+
+ addManualTestStep(
+ function() {},
+ null,
+ '1. Ensure the browser is at the default pinch and browser zoom ' +
+ 'levels (100%). Most browsers: ctrl+0');
+
+ addManualTestStep(
+ function() {
+ originalWidth = window.visualViewport.width;
+ originalHeight = window.visualViewport.height;
+ originalInnerWidth = window.innerWidth;
+ // Remember the visualViewport size here for the next test to
+ // address an edge case where originalInnerWidth is an odd
+ // number of pixels. The test expects that at 2x browser-zoom,
+ // visualViewport.width = innerWidth - scrollbarThickness / 2.0
+ // The equality only holds if originalInnerWidth / innerWidth is
+ // exactly 2, which is not the case in the aforementioned
+ // scenario because innerWidth always has to be an integer
+ // number of CSS pixels. Ditto for the height computation.
+ originalVisualViewportWidthExpectation =
+ window.innerWidth - scrollbarThickness;
+ originalVisualViewportHeightExpectation =
+ window.innerHeight - scrollbarThickness;
+
+ assert_equals(
+ window.visualViewport.width,
+ originalVisualViewportWidthExpectation,
+ "Scrollbar width subtracted from viewport.");
+ assert_equals(
+ window.visualViewport.height,
+ originalVisualViewportHeightExpectation,
+ "Scrollbar height subtracted from viewport.");
+ },
+ 'No zoom or scale applied',
+ '2. Browser-zoom into 200% (ctrl +)');
+
+ addManualTestStep(
+ function() {
+ assert_approx_equals(
+ originalInnerWidth / window.innerWidth,
+ 2.0,
+ 0.1,
+ "Browser zoom to correct level");
+
+ // Scrollbars on the window don't grow with browser-zoom so
+ // they'll be fewer CSS pixels as the user zooms in.
+ assert_equals(
+ window.visualViewport.width,
+ originalVisualViewportWidthExpectation / 2,
+ "Scrollbar width subtracted from viewport.");
+ assert_equals(
+ window.visualViewport.height,
+ originalVisualViewportHeightExpectation / 2,
+ "Scrollbar height subtracted from viewport.");
+ },
+ 'With 200% browser zoom',
+ '3. Reset browser zoom (ctrl+0).');
+
+ addManualTestStep(
+ showPinchWidget.bind(null, 2.0, 0, 0, continueTest),
+ null,
+ 'Pinch-zoom dialog in progress');
+
+ addManualTestStep(
+ function() {
+ assert_approx_equals(
+ window.visualViewport.scale, 2, 0.2, "Pinch zoom to correct scale");
+
+ assert_approx_equals(window.visualViewport.width,
+ originalWidth / window.visualViewport.scale,
+ 1,
+ "Scrollbar width subtracted from viewport.");
+ assert_approx_equals(window.visualViewport.height,
+ originalHeight / window.visualViewport.scale,
+ 1,
+ "Scrollbar width subtracted from viewport.");
+ },
+ 'With ~200% pinch zoom',
+ '4. Pinch-zoom out.');
+
+ addManualTestStep(
+ function() { continueBtn.remove(); },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-no-resize-event-on-overflow-recalc.html b/testing/web-platform/tests/visual-viewport/viewport-no-resize-event-on-overflow-recalc.html
new file mode 100644
index 0000000000..dcb9432da8
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-no-resize-event-on-overflow-recalc.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: No Resize Event Fired on Overflow Recalc</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ html {
+ height: 100%;
+ }
+ body {
+ /* Ensure overflow to start */
+ height: 200%;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>No Resize Event Fired on Overflow Recalc</h1>
+ <h4>
+ Test Description: This test ensures we don't fire spurrious resize
+ events when overflow is recalculated.
+ </h4>
+ <script>
+ function runTest() {
+ var t = async_test(
+ "Resize event not fired at window.visualViewport when content is added");
+ var viewResized = false;
+ window.visualViewport.addEventListener('resize', function() {
+ viewResized = true;
+ });
+
+ requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(viewResized, false);
+ }));
+
+ document.body.style.height = "400%";
+ }
+
+ // Run the test after load to make sure any resize from load doesn't
+ // interfere.
+ window.onload = requestAnimationFrame(function() {
+ requestAnimationFrame(runTest);
+ });
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/visual-viewport/viewport-offset-manual.html b/testing/web-platform/tests/visual-viewport/viewport-offset-manual.html
new file mode 100644
index 0000000000..c216e405a3
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-offset-manual.html
@@ -0,0 +1,151 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Offset</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_done: true, explicit_timeout: true});
+ </script>
+ <style>
+ html {
+ width: 100%;
+ height: 100%;
+ }
+ #fullscreenBox {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ visibility: hidden;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Offset</h1>
+ <h4>
+ Test Description: Tests the offset scrolling properties on an
+ unscrollable page.
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <h4>Instruction</h4>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ <div id="fullscreenBox">
+ <!-- Invisible but needed to get maximum scrollable extents in the
+ presence of a collapsible URL bar. -->
+ </div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ // Prevent scrolling (though there should be no overflow) so that all
+ // scrolling must happen as panning the visual viewport within the
+ // layout viewport.
+ document.documentElement.style.overflow = "hidden";
+
+ addManualTestStep(
+ function() {},
+ null,
+ '1. Ensure the browser is at the default pinch and browser zoom ' +
+ 'levels (100%). Most browsers: ctrl+0');
+
+ var scale = 3.0;
+ var xTarget = 2 * window.innerWidth / scale;
+ var yTarget = 2 * window.innerHeight / scale;
+ addManualTestStep(
+ showPinchWidget.bind(null, scale, xTarget, yTarget, continueTest),
+ null,
+ '2.Follow instructions on pinch zoom dialog.');
+
+ addManualTestStep(
+ function() {
+ var actualScale = window.visualViewport.scale;
+ var actualOffsetLeft = window.visualViewport.offsetLeft;
+ var actualOffsetTop = window.visualViewport.offsetTop;
+
+ // This needs to happen before assertions in case they fail. A
+ // failed assertion stops running this function.
+ window.scrollTo(0, 0);
+
+ // Ensure we zoomed in to about what we expect.
+ assert_approx_equals(actualScale, scale, 0.2,
+ "window.visualViewport.scale reflects pinch-zoom level");
+ assert_approx_equals(actualOffsetLeft, xTarget, 5,
+ "offsetLeft value is correct.");
+ assert_approx_equals(actualOffsetTop, yTarget, 5,
+ "offsetTop value is correct.");
+ },
+ 'With ~300% pinch-zoom',
+ '3. Pinch-zoom back out to the minimum scale');
+
+ addManualTestStep(
+ showPinchWidget.bind(null, 2, 0, 0, continueTest),
+ null,
+ '4.Follow instructions on pinch zoom dialog.');
+
+ addManualTestStep(
+ function() {
+ document.documentElement.style.overflow = "";
+ continueBtn.style.position = "absolute";
+ continueBtn.style.left = "150%";
+ continueBtn.style.top = "150%";
+
+ assert_approx_equals(window.visualViewport.scale, 2, 0.2,
+ "window.visualViewport.scale reflects pinch-zoom level");
+ },
+ 'Tester pinch zoomed in correctly',
+ '5. Scroll fully to the bottom right. Click the continue button ' +
+ 'there.');
+
+ addManualTestStep(
+ function() {
+ var fullscreenBox = document.getElementById('fullscreenBox');
+ var expectedLeft = fullscreenBox.clientWidth / 2;
+ var expectedTop = fullscreenBox.clientHeight / 2;
+ var viewOffsetLeft = window.visualViewport.offsetLeft;
+ var viewOffsetTop = window.visualViewport.offsetTop;
+
+ // This needs to happen before assertions in case they fail. A
+ // failed assertion stops running this function.
+ continueBtn.style.position = "";
+ continueBtn.style.left = "";
+ continueBtn.style.top = "";
+
+ window.scrollTo(0, 0);
+
+ assert_approx_equals(viewOffsetLeft, expectedLeft, 10,
+ "OffsetLeft is correct");
+ assert_approx_equals(viewOffsetTop, expectedTop, 10,
+ "OffsetTop");
+ },
+ 'OffsetLeft and OffsetTop correct when there\'s some layout ' +
+ 'viewport scrolling as well.',
+ '6. Pinch-zoom out fully');
+
+ addManualTestStep(
+ function() {
+ continueBtn.remove();
+ },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-page-manual.html b/testing/web-platform/tests/visual-viewport/viewport-page-manual.html
new file mode 100644
index 0000000000..9fb75ca0a2
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-page-manual.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Page</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_done: true, explicit_timeout: true});
+ </script>
+ <style>
+ html {
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Page</h1>
+ <h4>
+ Test Description: Tests the page scrolling properties.
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <h4>Instruction</h4>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+ var icbWidth = 0;
+ var icbHeight = 0;
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ addManualTestStep(
+ function() {},
+ null,
+ '1. Ensure the browser is at the default pinch and browser zoom ' +
+ 'levels (100%). Most browsers: ctrl+0');
+
+ addManualTestStep(
+ showPinchWidget.bind(null, 1.5, 0, 0, continueTest),
+ null,
+ '2.Follow instructions on pinch zoom dialog.');
+
+ addManualTestStep(
+ function() {
+ continueBtn.style.position = "absolute";
+ continueBtn.style.left = "400%";
+ continueBtn.style.top = "400%";
+
+ assert_approx_equals(window.visualViewport.scale, 1.5, 0.2,
+ "window.visualViewport.scale reflects pinch-zoom level");
+ },
+ 'Tester pinch zoomed in correctly',
+ '3. Scroll fully to the bottom right. Click the continue button there.');
+
+ addManualTestStep(
+ function() {
+ var expectedLeft =
+ document.documentElement.clientWidth * 4 +
+ continueBtn.clientWidth -
+ window.visualViewport.width;
+ var expectedTop =
+ document.documentElement.clientHeight * 4 +
+ continueBtn.clientHeight -
+ window.visualViewport.height;
+ var viewPageLeft = window.visualViewport.pageLeft;
+ var viewPageTop = window.visualViewport.pageTop;
+
+ // This needs to happen before assertions in case they fail. A
+ // failed assertion stops running this function.
+ continueBtn.style.position = "";
+ continueBtn.style.left = "";
+ continueBtn.style.top = "";
+
+ window.scrollTo(0, 0);
+
+ assert_approx_equals(viewPageLeft, expectedLeft, 10,
+ "window.visualViewport.scale reflects pinch-zoom level");
+ assert_approx_equals(viewPageTop, expectedTop, 10,
+ "window.visualViewport.scale reflects pinch-zoom level");
+ },
+ 'PageLeft and PageTop correct when scrolled',
+ '4. Pinch-zoom out fully');
+
+ addManualTestStep(
+ function() {
+ continueBtn.remove();
+ },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-read-size-causes-layout.html b/testing/web-platform/tests/visual-viewport/viewport-read-size-causes-layout.html
new file mode 100644
index 0000000000..0520a7cf80
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-read-size-causes-layout.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, minimum-scale=1">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ html {
+ height: 100%;
+ }
+</style>
+
+<h4>This test checks that requesting the viewport size causes any pending layout to occur.</h4>
+<script>
+ async_test(function(t) {
+ window.onload = t.step_func(function() {
+ assert_equals(window.visualViewport.width, document.documentElement.clientWidth,
+ "window.visualViewport.width should match the window width.");
+ assert_equals(visualViewport.height, document.documentElement.clientHeight,
+ "window.visualViewport.height should match the window height.");
+
+ // Add overflow so scrollbars appear.
+ document.body.style.width = "2000px";
+ document.body.style.height = "2000px";
+
+ var viewportWidth = window.visualViewport.width;
+ var viewportHeight = window.visualViewport.height;
+
+ assert_equals(viewportWidth, document.documentElement.clientWidth,
+ "Reading viewport width should cause a layout and exclude the new scrollbar.");
+ assert_equals(viewportHeight, document.documentElement.clientHeight,
+ "Reading viewport height should cause a layout and exclude the new scrollbar.");
+ t.done();
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-read-size-in-iframe-causes-layout.html b/testing/web-platform/tests/visual-viewport/viewport-read-size-in-iframe-causes-layout.html
new file mode 100644
index 0000000000..a08f7e876f
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-read-size-in-iframe-causes-layout.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, minimum-scale=1">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ iframe {
+ width: 200px;
+ height: 300px;
+ }
+</style>
+
+<h4>This test checks that requesting the viewport size in an iframe causes any pending layout to occur.</h4>
+<iframe srcdoc="<!DOCTYPE html><style>html{height:100%}</style>"></iframe>
+<script>
+ async_test(function(t) {
+ window.onload = t.step_func(function() {
+ assert_equals(frames[0].window.visualViewport.width, 200,
+ "Reading width of iframe viewport should match iframe width.");
+ assert_equals(frames[0].window.visualViewport.height, 300,
+ "Reading height of iframe viewport should match iframe height.");
+
+ // Add overflow so scrollbars appear.
+ window.frames[0].window.document.body.style.width = "2000px";
+ window.frames[0].window.document.body.style.height = "2000px";
+
+ var viewportWidth = frames[0].window.visualViewport.width;
+ var viewportHeight = frames[0].window.visualViewport.height;
+
+ assert_equals(viewportWidth, frames[0].window.document.documentElement.clientWidth,
+ "Reading width of iframe viewport should cause a layout and exclude the new scrollbar.");
+ assert_equals(viewportHeight, frames[0].window.document.documentElement.clientHeight,
+ "Reading height of iframe viewport should cause a layout and exclude the new scrollbar.");
+ t.done();
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-resize-event-manual.html b/testing/web-platform/tests/visual-viewport/viewport-resize-event-manual.html
new file mode 100644
index 0000000000..9088d1589b
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-resize-event-manual.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Window Resize Fires Event</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_timeout: true, explicit_done: true})
+ </script>
+ </head>
+ <body>
+ <h1>Viewport: Window Resize Fires Event</h1>
+ <h4>
+ Test Description: This test checks that a resize event is fired against
+ the window.visualViewport object when the browser window is resized.
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ var didResizeView;
+ var cancelable;
+ var bubbles;
+
+ function resetValues() {
+ didResizeView = false;
+ cancelable = undefined;
+ bubbles = undefined;
+ }
+
+ addManualTestStep(
+ function() {
+ resetValues();
+ window.visualViewport.addEventListener('resize', function(e) {
+ didResizeView = true;
+ cancelable = e.cancelable;
+ bubbles = e.bubbles;
+ });
+ },
+ null,
+ '1. Resize the browser window (if on mobile, rotate the device)');
+
+
+ addManualTestStep(
+ function() {
+ assert_true(didResizeView);
+ assert_false(cancelable);
+ assert_false(bubbles);
+ },
+ 'Resize event fired at window.visualViewport after window resized',
+ '2. Unrotate the device or reset window size if needed.');
+
+ addManualTestStep(
+ resetValues,
+ null,
+ '3. Pinch-zoom anywhere on the page by any amount.');
+
+ addManualTestStep(
+ function() {
+ assert_true(didResizeView);
+ assert_false(cancelable);
+ assert_false(bubbles);
+ },
+ 'Pinch-zooming fires a resize event',
+ '4. Pinch-zoom back out');
+
+ addManualTestStep(
+ function() { continueBtn.remove(); },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-show.html b/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-show.html
new file mode 100644
index 0000000000..688148a88f
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-show.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Resize Event On Showing Iframe</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ async_test(t => {
+ document.addEventListener("DOMContentLoaded", () => {
+ let iframe = document.querySelector("iframe");
+ onload = () => {
+ requestAnimationFrame(() => { requestAnimationFrame(() => {
+ // First full rendering update is complete.
+ let resize_event_count = 0;
+ iframe.contentWindow.visualViewport.addEventListener("resize", () => {
+ resize_event_count++;
+ });
+ iframe.style.display = "";
+ iframe.clientWidth;
+ requestAnimationFrame(() => {
+ t.step(() => {
+ assert_equals(resize_event_count, 1);
+ t.done();
+ });
+ }) }); // 2x requestAnimationFrame
+ })
+ };
+ });
+ }, "Resize event fired when an iframe is shown.");
+ </script>
+ </head>
+ <body>
+ <h1>Viewport: Resize Event On Iframe Size Change</h1>
+ <h4>
+ Test Description: This test ensures that we fire a resize event when
+ an iframe is shown.
+ </h4>
+ <iframe style="display: none;" srcdoc="<p>Hello, world!</p>"></iframe>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-size-change.html b/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-size-change.html
new file mode 100644
index 0000000000..802fee7879
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-iframe-size-change.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Resize Event On Iframe Size Change</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ async_test(t => {
+ document.addEventListener("DOMContentLoaded", () => {
+ let iframe = document.querySelector("iframe");
+ onload = () => {
+ requestAnimationFrame(() => { requestAnimationFrame(() => {
+ // First full rendering update is complete.
+ let resize_event_count = 0;
+ iframe.contentWindow.visualViewport.addEventListener("resize", () => {
+ resize_event_count++;
+ });
+ iframe.style.width = "200px";
+ iframe.clientWidth;
+ requestAnimationFrame(() => {
+ t.step(() => {
+ assert_equals(resize_event_count, 1);
+ t.done();
+ });
+ }) }); // 2x requestAnimationFrame
+ })
+ };
+ });
+ }, "Resize event fired when an iframe is resized.");
+ </script>
+ </head>
+ <body>
+ <h1>Viewport: Resize Event On Iframe Size Change</h1>
+ <h4>
+ Test Description: This test ensures that we fire a resize event when
+ the size of an iframe changes.
+ </h4>
+ <iframe srcdoc="<p>Hello, world!</p>"></iframe>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-load-overflowing-page.html b/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-load-overflowing-page.html
new file mode 100644
index 0000000000..d5dc1deae7
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-resize-event-on-load-overflowing-page.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Resize Event On Load Overflowing Page</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ var t = async_test(
+ "Resize event fired exactly once against window.visualViewport if " +
+ "scrollbars affect layout.");
+ t.step(() => {
+ // Run the test in a new window to make sure we don't pick up
+ // a resize event due to a previous page having a different scale.
+ var win = window.open("helper-resize-event-on-load-overflowing-page.html");
+ t.add_cleanup(() => win.close());
+ });
+ </script>
+ </head>
+ <body>
+ <h1>Viewport: Resize Event On Load Overflowing Page</h1>
+ <h4>
+ Test Description: This test ensures that we fire a resize event against
+ window.visualViewport if the page has overflow (since this creates a scrollbar
+ and thus changes the viewport size).
+ </h4>
+ <div id="log"></div>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/visual-viewport/viewport-scale-iframe-manual.html b/testing/web-platform/tests/visual-viewport/viewport-scale-iframe-manual.html
new file mode 100644
index 0000000000..1c27ab71e1
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-scale-iframe-manual.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Scale iframe</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_done: true, explicit_timeout: true});
+ </script>
+ <style>
+ iframe {
+ width: 200px;
+ height: 200px;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Scale iframe</h1>
+ <h4>Test Description: Tests the visualViewport.scale property inside an iframe</h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <p>
+ <iframe srcdoc="<!DOCTYPE html><style>html {background-color: coral;}</style>"></iframe>
+ </p>
+ <div id="log"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ addManualTestStep(
+ function() {},
+ null,
+ '1. Ensure the browser is at the default pinch and browser zoom ' +
+ 'levels (100%). Most browsers: ctrl+0');
+
+ addManualTestStep(
+ showPinchWidget.bind(null, 2.0, 0, 0, continueTest),
+ null,
+ '2.Follow instructions on pinch zoom dialog.');
+
+ addManualTestStep(
+ function() {
+ // Ensure we zoomed in to about what we expect.
+ assert_approx_equals(window.visualViewport.scale, 2.0, 0.2,
+ "Main window.visualViewport.scale reflects pinch-zoom level");
+
+ assert_equals(frames[0].window.visualViewport.scale, 1.0,
+ "Iframe scale unchanged even when pinch-zoomed");
+ },
+ 'Check iframe scale is unchanged when page is pinch-zoomed',
+ '3. Pinch-zoom back out to the minimum scale');
+
+ addManualTestStep(
+ function() {
+ continueBtn.remove();
+ },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-scale-manual.html b/testing/web-platform/tests/visual-viewport/viewport-scale-manual.html
new file mode 100644
index 0000000000..82bf418e1c
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-scale-manual.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Scale</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_done: true, explicit_timeout: true});
+ </script>
+ </head>
+ <body>
+ <h1>Viewport: Scale</h1>
+ <h4>Test Description: Tests the visualViewport.scale property</h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ <div id="spacer"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ addManualTestStep(
+ function() {},
+ null,
+ '1. Ensure the browser is at the default pinch and browser zoom ' +
+ 'levels (100%). Most browsers: ctrl+0');
+
+ addManualTestStep(
+ showPinchWidget.bind(null, 2.0, 0, 0, continueTest),
+ null,
+ '2.Follow instructions on pinch zoom dialog.');
+
+ addManualTestStep(
+ function() {
+ // Ensure we zoomed in to about what we expect.
+ assert_approx_equals(window.visualViewport.scale, 2.0, 0.2,
+ "window.visualViewport.scale reflects pinch-zoom level");
+ },
+ 'With ~200% pinch-zoom',
+ '3. Pinch-zoom back out to the minimum scale');
+
+ addManualTestStep(
+ function() {
+ assert_equals(window.visualViewport.scale, 1);
+ },
+ 'Fully pinch-zoomed out',
+ '4. If your browser supports it, browser-zoom in (using ctrl-+). ' +
+ 'Otherwise just click continue.');
+
+ addManualTestStep(
+ function() {
+ assert_equals(window.visualViewport.scale, 1);
+ },
+ 'Browser zoom doesn\'t change visualViewport.scale',
+ '5. Reset browser zoom to default (ctrl-0)');
+
+ addManualTestStep(
+ function() {
+ continueBtn.remove();
+ },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-scroll-event-manual.html b/testing/web-platform/tests/visual-viewport/viewport-scroll-event-manual.html
new file mode 100644
index 0000000000..3fcce10144
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-scroll-event-manual.html
@@ -0,0 +1,182 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Scroll Event</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_timeout: true, explicit_done: true})
+ </script>
+ <style>
+ html {
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Scroll Event</h1>
+ <h4>
+ Test Description: This test checks that a scroll event is fired against
+ the window.visualViewport object when the viewport is scrolled.
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <h4>Instruction</h4>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ var didGetScrollEvent = false;
+ var cancelable = undefined;
+ var bubbles = undefined;
+
+ function resetValues() {
+ didGetScrollEvent = false;
+ cancelable = undefined;
+ bubbles = undefined;
+ }
+
+ addManualTestStep(
+ function() {
+ window.visualViewport.addEventListener('scroll', function(e) {
+ didGetScrollEvent = true;
+ cancelable = e.cancelable;
+ bubbles = e.bubbles;
+ });
+ document.documentElement.style.overflow = "hidden";
+ },
+ null,
+ '1. Pinch-zoom a little near the "Continue" button but don\'t ' +
+ 'perform any scrolling.');
+
+ addManualTestStep(
+ function() {
+ requestAnimationFrame(continueTest);
+ assert_true(didGetScrollEvent, "Got event");
+ assert_false(cancelable, "Event is not cancelable");
+ assert_false(bubbles, "Event does not bubble");
+ },
+ 'Got scroll event while pinch-zooming',
+ '');
+
+ addManualTestStep(
+ resetValues,
+ null,
+ '2. Scroll in any direction.');
+
+ addManualTestStep(
+ function() {
+ requestAnimationFrame(continueTest);
+ assert_true(didGetScrollEvent, "Got event");
+ assert_false(cancelable, "Event is not cancelable");
+ assert_false(bubbles, "Event does not bubble");
+ },
+ 'Panning viewport fires a scroll event',
+ '');
+
+ addManualTestStep(
+ function() {
+ continueBtn.style.position = "absolute";
+ continueBtn.style.right = "10px";
+ continueBtn.style.bottom = "10px";
+ },
+ null,
+ '3. Scroll fully to the bottom right and click the continue ' +
+ 'button.');
+
+ var offsetLeft;
+ var offsetTop;
+ addManualTestStep(
+ function() {
+ resetValues();
+ document.documentElement.style.overflow = "";
+ document.body.style.width = "500%";
+ document.body.style.height = "500%";
+ continueBtn.style.position = "";
+ continueBtn.style.left = "";
+ continueBtn.style.top = "";
+
+ offsetLeft = window.visualViewport.offsetLeft;
+ offsetTop = window.visualViewport.offsetTop;
+
+ // The visual viewport should be fully scrolled so even if
+ // scrollTo does normally "push" the layout viewport with the
+ // visual, there should be no change to either offsetValue
+ window.scrollTo(10000, 10000);
+
+ requestAnimationFrame(continueTest);
+ assert_equals(window.visualViewport.offsetLeft, offsetLeft,
+ "OffsetLeft Unchanged");
+ assert_equals(window.visualViewport.offsetTop, offsetTop,
+ "OffsetTop Unchanged");
+ assert_false(didGetScrollEvent,
+ "Should not get view scroll event");
+ },
+ 'scrollTo down and right on a fully scrolled visual viewport ' +
+ 'shouldn\'t change offsets',
+ '');
+
+ addManualTestStep(
+ function() {
+ requestAnimationFrame(continueTest);
+ assert_false(didGetScrollEvent,
+ "Should not get view scroll event");
+ resetValues();
+ },
+ 'scrollTo without changing offsets shouldn\'t fire scroll event ' +
+ 'on view',
+ '');
+
+ addManualTestStep(
+ function() {
+ requestAnimationFrame(continueTest);
+ resetValues();
+ window.scrollTo(0, 0);
+ },
+ null,
+ '');
+
+ addManualTestStep(
+ function() {
+ // How scrollTo behaves in this case isn't fully spec'd but
+ // make sure it's at least rational if it does change the
+ // offset values.
+ var scrollChangedOffset =
+ offsetLeft != window.visualViewport.offsetLeft ||
+ offsetTop != window.visualViewport.offsetTop;
+
+ document.body.style.width = "";
+ document.body.style.height = "";
+
+ assert_equals(didGetScrollEvent, scrollChangedOffset,
+ 'If the scrollTo changed offsets it must have fired a ' +
+ 'scroll event');
+ },
+ 'scrollTo must fire scroll event if it changes visualViewport.offsetLeft|Top',
+ '6. Pinch-zoom out fully');
+
+ addManualTestStep(
+ function() { continueBtn.remove(); },
+ null,
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-scrollbars-cause-resize.html b/testing/web-platform/tests/visual-viewport/viewport-scrollbars-cause-resize.html
new file mode 100644
index 0000000000..086e8d92b0
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-scrollbars-cause-resize.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Scrollbars Cause Resize</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ </head>
+ <body>
+ <h1>Viewport: Scrollbars Cause Resize</h1>
+ <h4>
+ Test Description: This test checks that the appearance of classic
+ scrollbars will cause a resize event to be fired at window.visualViewport.
+ </h4>
+ <script>
+ setup({ explicit_done: true });
+
+ function runTest() {
+ var scrollbarThickness = calculateScrollbarThickness();
+
+ document.documentElement.style.overflow = "hidden";
+ var initialWidth = visualViewport.width;
+ var initialHeight = visualViewport.height;
+
+ test(function() {
+ assert_equals(window.visualViewport.width, window.innerWidth);
+ assert_equals(window.visualViewport.height, window.innerHeight);
+ }, "view size initially matches window size");
+
+
+ var t = async_test(
+ "Resize event was fired at window.visualViewport if, and only if, " +
+ "scrollbars are classic (i.e. affect flow)");
+ var viewResized = false;
+ window.visualViewport.addEventListener('resize', function() {
+ viewResized = true;
+ });
+
+ requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(viewResized, scrollbarThickness > 0);
+ }));
+
+ document.documentElement.style.overflow = "";
+ document.body.style.width = "10000px";
+ document.body.style.height = "10000px";
+
+ var expectedWidth = initialWidth - scrollbarThickness;
+ var expectedHeight = initialHeight - scrollbarThickness;
+
+ test(function() {
+ assert_equals(window.visualViewport.width, expectedWidth);
+ assert_equals(window.visualViewport.height, expectedHeight);
+ }, "view size reflects appearance of classic scrollbars");
+
+
+ document.body.style.width = "";
+ document.body.style.height = "";
+ }
+
+ // Run the test after load to make sure any resize from a previous test
+ // or from the load doesn't interfere.
+ window.onload = requestAnimationFrame(function() {
+ try {
+ runTest();
+ } finally {
+ done();
+ }
+ });
+ </script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-segments.tentative.html b/testing/web-platform/tests/visual-viewport/viewport-segments.tentative.html
new file mode 100644
index 0000000000..c90ed29d73
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-segments.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Viewport Segments: visualViewport.segments tentative</title>
+<link rel="help" href="https://wicg.github.io/visual-viewport/"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+
+test(() => {
+ assert_true('segments' in visualViewport, "`segments` must be a property of visualViewport.");
+ assert_equals(visualViewport.segments, null, "For a viewport not segmented, no segments must be exposed.");
+});
+</script>
+
diff --git a/testing/web-platform/tests/visual-viewport/viewport-type.html b/testing/web-platform/tests/visual-viewport/viewport-type.html
new file mode 100644
index 0000000000..7a9ca425c0
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-type.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: window.visualViewport type</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <h1>Viewport: window.visualViewport type</h1>
+ <h4>Test Description: This test checks that window.visualViewport is an object of type VisualViewport.</h4>
+ <script>
+ test(function() {
+ assert_equals(typeof(window.visualViewport), "object");
+ }, "window.visualViewport is an object");
+
+ test(function() {
+ assert_equals(window.visualViewport.toString(), "[object VisualViewport]");
+ }, "window.visualViewport has type `VisualViewport`");
+
+ addEventListener("load", function() {
+ document.getElementById("viewtype-log").innerText = typeof(window.visualViewport);
+ });
+ </script>
+ <div id="complete-notice">
+ <p>window.visualViewport is of type: <span id="viewtype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-unscaled-scale-iframe.html b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scale-iframe.html
new file mode 100644
index 0000000000..30d9bfbe89
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scale-iframe.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Unscaled scale iframe</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <style>
+ iframe {
+ width: 200px;
+ height: 300px;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Unscaled scale iframe</h1>
+ <h4>Test Description: This test checks that the default value for scale inside an iframe is 1.</h4>
+ <iframe></iframe>
+ <div id="complete-notice">
+ <p>iframe's window.visualViewport.scale is <span id="view-scale-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ test(function() {
+ assert_equals(frames[0].window.visualViewport.scale, 1);
+ }, "iframe's visualViewport.scale default value.");
+
+ document.getElementById("view-scale-log").innerText = frames[0].window.visualViewport.scale;
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-unscaled-scale.html b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scale.html
new file mode 100644
index 0000000000..b83015d8d1
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scale.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Unscaled scale</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ </head>
+ <body>
+ <h1>Viewport: Unscaled scale</h1>
+ <h4>Test Description: This test checks that the default value for scale is 1.</h4>
+ <div id="complete-notice">
+ <p>window.visualViewport.scale is <span id="view-scale-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ test(function() {
+ assert_equals(window.visualViewport.scale, 1);
+ }, "visualViewport.scale default value.");
+
+ document.getElementById("view-scale-log").innerText = window.visualViewport.scale;
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll-iframe.html b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll-iframe.html
new file mode 100644
index 0000000000..f9e3023ab7
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll-iframe.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Scroll in iframe - no page scale</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <style>
+ iframe {
+ width: 200px;
+ height: 300px;
+ border: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Scroll in iframe - no page scale</h1>
+ <h4>Test Description: This test checks that window.visualViewport returns correct offset and scroll values without any pinch-zoom page scale applied.</h4>
+ <iframe></iframe>
+ <div id="complete-notice">
+ <p>frames[0].window.visualViewport's offsetLeft and offsetTop is (<span id="scroll-offset-log"></span>).</p>
+ <p>frames[0].window.visualViewport's pageLeft and pageTop is (<span id="scroll-page-log"></span>).</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ var iframe = frames[0].window;
+
+ // Add overflow we can scroll.
+ iframe.document.body.style.width = "2000px";
+ iframe.document.body.style.height = "2000px";
+
+ iframe.scrollTo(1000, 1200);
+
+
+ test(function() {
+ assert_equals(iframe.visualViewport.offsetLeft, 0);
+ }, "offsetLeft must be 0.");
+ test(function() {
+ assert_equals(iframe.visualViewport.offsetTop, 0);
+ }, "offsetTop must be 0.");
+ test(function() {
+ assert_equals(iframe.visualViewport.pageLeft, 1000);
+ }, "pageLeft must reflect location of viewport in document.");
+ test(function() {
+ assert_equals(iframe.visualViewport.pageTop, 1200);
+ }, "pageTop must reflect location of viewport in document.");
+
+ document.getElementById("scroll-offset-log").innerText = iframe.visualViewport.offsetLeft+ ", " + iframe.visualViewport.offsetTop;
+ document.getElementById("scroll-page-log").innerText = iframe.visualViewport.pageLeft + ", " + iframe.visualViewport.pageTop;
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll.html b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll.html
new file mode 100644
index 0000000000..93623d1962
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-unscaled-scroll.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Scroll - no page scale</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ </head>
+ <body>
+ <h1>Viewport: Scroll - no page scale</h1>
+ <h4>Test Description: This test checks that window.visualViewport returns correct offset and scroll values without any pinch-zoom page scale applied.</h4>
+ <div id="complete-notice">
+ <p>window.visualViewport's offsetLeft and offsetTop is (<span id="scroll-offset-log"></span>).</p>
+ <p>window.visualViewport's pageLeft and pageTop is (<span id="scroll-page-log"></span>).</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ // Add overflow we can scroll.
+ document.body.style.width = "5000px";
+ document.body.style.height = "5000px";
+
+ scrollTo(1000, 1200);
+
+ test(function() {
+ assert_equals(window.visualViewport.offsetLeft, 0);
+ }, "offsetLeft must be 0.");
+ test(function() {
+ assert_equals(window.visualViewport.offsetTop, 0);
+ }, "offsetTop must be 0.");
+ test(function() {
+ assert_equals(window.visualViewport.pageLeft, 1000);
+ }, "pageLeft must reflect location of viewport in document.");
+ test(function() {
+ assert_equals(window.visualViewport.pageTop, 1200);
+ }, "pageTop must reflect location of viewport in document.");
+
+ document.getElementById("scroll-offset-log").innerText = window.visualViewport.offsetLeft+ ", " + window.visualViewport.offsetTop;
+ document.getElementById("scroll-page-log").innerText = window.visualViewport.pageLeft + ", " + window.visualViewport.pageTop;
+
+ scrollTo(0, 0);
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-unscaled-size-iframe.html b/testing/web-platform/tests/visual-viewport/viewport-unscaled-size-iframe.html
new file mode 100644
index 0000000000..31df6a05fd
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-unscaled-size-iframe.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Size in iframe - no page scale</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <style>
+ iframe {
+ width: 200px;
+ height: 300px;
+ border: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Size in iframe - no page scale</h1>
+ <h4>Test Description: This test checks that window.visualViewport returns correct sizes without any pinch-zoom page scale applied but with scrollbars.</h4>
+ <iframe></iframe>
+ <div id="complete-notice">
+ <p>frames[0].window.visualViewport width and height is (<span id="size-log"></span>).</p>
+ <p>frames[0].window.visualViewport width and height when scrollbars are present is (<span id="size-scrollbars-log"></span>).</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ setup({ explicit_done: true });
+
+ function runTest() {
+ var scrollbarThickness = calculateScrollbarThickness();
+
+ test(function() {
+ assert_equals(frames[0].window.visualViewport.width, 200);
+ }, "window.visualViewport.width of iframe viewport should match iframe width.");
+ test(function() {
+ assert_equals(frames[0].window.visualViewport.height, 300);
+ }, "window.visualViewport.height of iframe viewport should match iframe height.");
+
+ document.getElementById("size-log").innerText = frames[0].window.visualViewport.width + ", " + frames[0].window.visualViewport.height;
+
+ // Add overflow so scrollbars appear.
+ window.frames[0].window.document.body.style.width = "2000px";
+ window.frames[0].window.document.body.style.height = "2000px";
+
+ test(function() {
+ assert_equals(frames[0].window.visualViewport.width, 200 - scrollbarThickness);
+ }, "window.visualViewport.width of iframe viewport should not include scrollbar.");
+ test(function() {
+ assert_equals(frames[0].window.visualViewport.height, 300 - scrollbarThickness);
+ }, "window.visualViewport.height of iframe viewport should not include scrollbar.");
+
+ document.getElementById("size-scrollbars-log").innerText = frames[0].window.visualViewport.width + ", " + frames[0].window.visualViewport.height;
+ }
+
+ window.onload = function() {
+ try {
+ runTest();
+ } finally {
+ done();
+ }
+ }
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-unscaled-size.html b/testing/web-platform/tests/visual-viewport/viewport-unscaled-size.html
new file mode 100644
index 0000000000..8ad9fb2cfe
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-unscaled-size.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: Size unscaled</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <style>
+ html {
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: Size unscaled</h1>
+ <h4>Test Description: This test checks that window.visualViewport returns correct sizes without any pinch-zoom page scale applied.</h4>
+ <div id="complete-notice">
+ <p>window.visualViewport width and height is (<span id="view-size-log"></span>).</p>
+ <p>window.visualViewport width and height when scrollbars are present is (<span id="view-size-scrollbar-log"></span>).</p>
+ </div>
+ <div id="log"></div>
+ </body>
+ <script>
+ var scrollbarThickness = calculateScrollbarThickness();
+
+ test(function() {
+ assert_equals(window.visualViewport.width, document.documentElement.clientWidth);
+ }, "visualViewport.width should match documentElement.clientWidth when unzoomed.");
+ test(function() {
+ assert_equals(window.visualViewport.height, document.documentElement.clientHeight);
+ }, "visualViewport.height should match documentElement.clientHeight when unzoomed.");
+
+ document.getElementById("view-size-log").innerText = window.visualViewport.width + ", " + window.visualViewport.height;
+
+ // Add overflow so scrollbars appear.
+ document.body.style.width = "2000px";
+ document.body.style.height = "2000px";
+
+ test(function() {
+ assert_equals(window.visualViewport.width, document.documentElement.clientWidth);
+ }, "visualViewport.width should exclude scrollbar.");
+ test(function() {
+ assert_equals(window.visualViewport.height, document.documentElement.clientHeight);
+ }, "visualViewport.height should exclude scrollbar.");
+
+ document.getElementById("view-size-scrollbar-log").innerText = window.visualViewport.width + ", " + window.visualViewport.height;
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport-url-bar-changes-height-manual.html b/testing/web-platform/tests/visual-viewport/viewport-url-bar-changes-height-manual.html
new file mode 100644
index 0000000000..563134236b
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport-url-bar-changes-height-manual.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Viewport: URL bar changes height</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="viewport_support.js"></script>
+ <script>
+ setup({explicit_timeout: true});
+ </script>
+ <style>
+ body {
+ margin-bottom: 1000px;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Viewport: URL bar changes height</h1>
+ <h4>
+ Test Description: This test checks that hiding the URL bar correctly
+ affects the visualViewport height. Skip if the UA doesn't have a URL bar
+ that hides/shows with scrolling.
+ </h4>
+ <h2 style="color: red">THIS IS A MANUAL TEST</h2>
+ <p id="skip">
+ <button id="skipbtn" onclick="skipManualTest();">Skip Test</button>
+ </p>
+ <p id="instruction"></p>
+ <button id="continue">Start Test</button>
+ <div id="log"></div>
+ </body>
+ <script>
+ var continueBtn = document.getElementById("continue");
+
+ function continueTest() {
+ nextStep(function(instructionText) {
+ var instruction = document.getElementById("instruction");
+ continueBtn.innerText = "Continue";
+ instruction.innerText = instructionText;
+ });
+ }
+
+ continueBtn.addEventListener('click', continueTest);
+
+ var heights = [ window.visualViewport.height ];
+
+ addManualTestStep(
+ function() {
+ window.visualViewport.addEventListener('resize', function(e) {
+ heights.push( window.visualViewport.height );
+ });
+ },
+ null,
+ '1. Scroll down (slowly, e.g. over ~1 sec) so the URL bar hides.');
+
+ addManualTestStep(
+ function() {
+ continueBtn.remove();
+ assert_greater_than(
+ heights.length, 2, 'URL bar caused multiple resize events');
+
+ var smallest = heights[0];
+ var largest = heights[0];
+ for (var height in heights) {
+ if (height < smallest)
+ smallest = height;
+ if (height > largest)
+ largest = height;
+ }
+
+ var hasIntermediate = false;
+ for (var height in heights) {
+ if (height != smallest && height != largest) {
+ hasIntermediate = true;
+ break;
+ }
+ }
+ assert_true(
+ hasIntermediate,
+ 'Resize fired for intermediate viewport height values');
+ },
+ 'URL bar updates height continually',
+ 'Test Complete');
+ </script>
+</html>
diff --git a/testing/web-platform/tests/visual-viewport/viewport_support.js b/testing/web-platform/tests/visual-viewport/viewport_support.js
new file mode 100644
index 0000000000..a82bd2b028
--- /dev/null
+++ b/testing/web-platform/tests/visual-viewport/viewport_support.js
@@ -0,0 +1,180 @@
+// Simulates a small pinch-zoom to provide some page scale to make the visual
+// viewport able to differ from the layout viewport. The amount of zoom is
+// likely to vary between browsers so tests shouldn't rely on an exact scale
+// factor.
+//
+// Simulates two fingers, 100px apart, pulling apart vertically to 200px of
+// separation.
+function pinchZoomIn() {
+ const viewport = window.visualViewport;
+ const center_x = Math.floor((viewport.width * viewport.scale) / 2);
+ const center_y = Math.floor((viewport.height * viewport.scale) / 2);
+
+ return new test_driver.Actions()
+ .setContext(window)
+ .addPointer("finger1", "touch")
+ .addPointer("finger2", "touch")
+ .pointerMove(center_x, center_y-50, {origin: "viewport", sourceName: "finger1"})
+ .pointerMove(center_x, center_y+50, {origin: "viewport", sourceName: "finger2"})
+ .pointerDown({sourceName: "finger1"})
+ .pointerDown({sourceName: "finger2"})
+ .pointerMove(center_x, center_y-100, {origin: "viewport", sourceName: "finger1"})
+ .pointerMove(center_x, center_y+100, {origin: "viewport", sourceName: "finger2"})
+ .pointerUp({sourceName: "finger1"})
+ .pointerUp({sourceName: "finger2"})
+ .send();
+}
+
+// If scrollbars affect layout (i.e. what the CSS Overflow spec calls "classic
+// scrollbars", as opposed to overlay scrollbars), return the scrollbar
+// thickness in CSS pixels. Returns 0 otherwise.
+function calculateScrollbarThickness() {
+ var container = document.createElement("div");
+ container.style.width = "100px";
+ container.style.height = "100px";
+ container.style.position = "absolute";
+ container.style.visibility = "hidden";
+ container.style.overflow = "auto";
+
+ document.body.appendChild(container);
+
+ var widthBefore = container.clientWidth;
+ var longContent = document.createElement("div");
+ longContent.style.height = "1000px";
+ container.appendChild(longContent);
+
+ var widthAfter = container.clientWidth;
+
+ container.remove();
+
+ return widthBefore - widthAfter;
+}
+
+// Puts up a widget on screen instructing the user to pinch-zoom in to the
+// given scale. The widget is sized such that the given scale is achieved. The
+// widget is placed at (x, y) on the page. A button on the widget is used by
+// the user to let the widget know that the user has finished. If a callback is
+// provided, it will be called when the user dismisses the widget.
+function showPinchWidget(scale, x, y, callback) {
+ var border = 10;
+ var width = window.innerWidth / scale - border;
+ var height = window.innerHeight / scale - border;
+
+ var box = document.createElement("div");
+ box.style.width = width + "px";
+ box.style.height = height + "px";
+
+ // Adjust the x/y coordinates by the border width. We want the box to
+ // appear in a place such that if the user gets the window edges exactly on
+ // the half-point of the border they end up at x/y
+ box.style.left = x - border/2 + "px";
+ box.style.top = y - border/2 + "px";
+
+ box.style.position = "absolute";
+ box.style.backgroundColor = "coral";
+ box.style.border = border + "px solid blue";
+ box.style.borderBottom = "0";
+ box.style.overflow = "auto";
+
+ var oldDocumentOverflow = document.documentElement.style.overflow;
+
+ var instructions = document.createElement("p");
+ instructions.innerText =
+ "Pinch-zoom and align this box so that the left, right, and top " +
+ "window edges are over the border on each side. When done, click the " +
+ "'DONE' button above";
+ instructions.style.textAlign = "center";
+ instructions.style.fontSize = "medium";
+
+ var button = document.createElement("button");
+ button.innerText = "DONE";
+ button.style.width = "50%";
+ button.style.height = "20%";
+ button.style.fontSize = "medium";
+ button.style.marginLeft = "25%";
+ button.addEventListener("click", function() {
+ box.remove();
+ document.documentElement.style.overflow = oldDocumentOverflow;
+ if (callback)
+ callback();
+ });
+
+ box.appendChild(button);
+ box.appendChild(instructions);
+
+ document.documentElement.style.overflow = "hidden";
+
+ document.body.appendChild(box);
+}
+
+// Ends a manual test. Must be called before any async tests are started.
+function skipManualTest() {
+ test(function() { assert_true(false); }, "Manual Test Skipped");
+ done();
+}
+
+var stepInstructions = [];
+var testNames = [];
+var stepFunctions = [];
+var steps;
+var curStep = 0;
+
+// Adds a manual test step to the test. A test will add a series of steps,
+// along with instructions. Once all the tests steps are added, the test can
+// be run by continually running the nextStep() function. All manual test steps
+// must be added before calling nextStep.
+//
+// |func| A function to be executed at the given step. This function can include
+// testharness assertions if |testName| is provided. If this is the last
+// step, the |done()| function (used for manual testharness.js tests)
+// will be called after |func| is executed.
+// |testName| If provided, the |func| will be wrapped in a testharness.js
+// async_test with this name. If null, |func| will be executed as a
+// free function.
+// |instructions| The text to display to the user. Note, these are shown after
+// step is executed so these should be instructions to setup the
+// checks in the next step.
+function addManualTestStep(func, testName, instructions) {
+ stepFunctions.push(func);
+ testNames.push(testName);
+ stepInstructions.push(instructions);
+}
+
+// Runs the next step of the test. This must be called only after all test steps
+// have been added using |addManualTestStep|.
+//
+// |callbackFunc| If provided, will be called with a single argument being the
+// instruction string for the current step. Use this to update
+// any necessary UI.
+function nextStep(callbackFunc) {
+ if (curStep == 0)
+ _startManualTest();
+
+ if (typeof(callbackFunc) === 'function')
+ callbackFunc(stepInstructions[curStep]);
+
+ steps[curStep]();
+ curStep++;
+}
+
+function _startManualTest() {
+ steps = [];
+ for (let i = 0; i < stepFunctions.length; ++i) {
+ var stepFunc = stepFunctions[i];
+ var testName = testNames[i];
+ if (testName) {
+ steps.push(async_test(testName).step_func(function() {
+ stepFunctions[i]();
+ this.done();
+ if (i == stepFunctions.length - 1)
+ done();
+ }));
+ } else {
+ steps.push(function() {
+ stepFunctions[i]();
+ if (i == stepFunctions.length - 1)
+ done();
+ });
+ }
+ }
+}