diff options
Diffstat (limited to 'testing/web-platform/tests/window-placement')
6 files changed, 368 insertions, 0 deletions
diff --git a/testing/web-platform/tests/window-placement/META.yml b/testing/web-platform/tests/window-placement/META.yml new file mode 100644 index 0000000000..2c6d60f4e7 --- /dev/null +++ b/testing/web-platform/tests/window-placement/META.yml @@ -0,0 +1,4 @@ +spec: https://w3c.github.io/window-placement/ +suggested_reviewers: + - michaelwasserman + - bradtriebwasser diff --git a/testing/web-platform/tests/window-placement/README.md b/testing/web-platform/tests/window-placement/README.md new file mode 100644 index 0000000000..d9e324c911 --- /dev/null +++ b/testing/web-platform/tests/window-placement/README.md @@ -0,0 +1,5 @@ +# Window Management Testing + +[Window Management Specification](https://w3c.github.io/window-placement/) + +The tests in this directory require at least 2 displays on the host machine to yield meaningful results. A well-supported configuration is 2 displays at 1920x1080 resolution arranged horizonally (primary on the left, secondary on the right). diff --git a/testing/web-platform/tests/window-placement/fullscreen-companion-window-manual.tentative.https.html b/testing/web-platform/tests/window-placement/fullscreen-companion-window-manual.tentative.https.html new file mode 100644 index 0000000000..10f30a1906 --- /dev/null +++ b/testing/web-platform/tests/window-placement/fullscreen-companion-window-manual.tentative.https.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> +<!-- user agents are not required to support open features other than `noopener` + and on some platforms position and size features don't make sense --> +<meta name="flags" content="may"> +<title>Multi-Screen Window Management test: Fullscreen Companion Window</title> +<link rel="help" href="https://w3c.github.io/window-placement/"> +This test uses multi-screen details to request fullscreen and open a pop-up<br> +(companion window) in the same user activation.<br> +It runs manually with `wpt serve` and a compatible browser.<br><br> +<button id="setUpButton">Request screen details</button> +<ul id="popupButtons"></ul> +<button id="cleanUpButton">Close any open popups</button><br> +<input id="autoCleanUp" type="checkbox" checked=true>Auto-close popups</input> +<ul id="logger"></ul> +<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/helpers.js"></script> + +<script> +'use strict'; +let popups = []; + +cleanUpButton.addEventListener('click', async () => { + popups.forEach(p => p.close()); +}); + +// expectPopup should be true if the test should expect the pop-up to be +// created, or false if the popup is not expected to be created (blocked). +async function testPopupOnScreen(popupTest, screen, expectPopup) { + // Show a popup child window on the associated screen. + const left = screen.availLeft + Math.floor(screen.availWidth / 2) - 150; + const top = screen.availTop + Math.floor(screen.availHeight / 2) - 50; + log(`Opening a popup on '${screen.label}' at (${left}, ${top})`); + let popup = window.open( + '/resources/blank.html', '', + `left=${left},top=${top},width=300,height=100`); + assert_equals(!!popup, expectPopup, 'Popup reference'); + if (popup === null) + return; + assert_equals(!popup.closed, expectPopup, 'Popup open'); + popups.push(popup); + if (autoCleanUp.checked) { + // TODO(crbug.com/1338645): Remove this workaround (delay) after browser code is + // fixed. + popupTest.add_cleanup(()=>{ + setTimeout(popup.close, 1000); + }); + } +} + +promise_test(async setUpTest => { + await setUpWindowManagement(setUpTest, setUpButton); + const screenDetails = await getScreenDetails(); + assert_true(!!screenDetails, 'Error getting screen details'); + for (const [i, fullscreenScreen] of screenDetails.screens.entries()) { + const popupScreen = + screenDetails.screens[(i + 1) % screenDetails.screens.length]; + let testName = + `Fullscreen on '${fullscreenScreen.label}' and open popup on '${popupScreen.label}'`; + promise_test(async popupTest => { + await addTestTriggerButtonAndAwaitClick(popupButtons, + testName, + popupTest); + await document.documentElement.requestFullscreen( + { screen: fullscreenScreen } + ); + await testPopupOnScreen(popupTest, popupScreen, + /*expectPopup=*/screenDetails.screens.length > 1); + }, testName); + } +}, 'Use multi-screen details to request fullscreen and open a pop-up in the same user activation.'); +</script> diff --git a/testing/web-platform/tests/window-placement/multi-screen-fullscreen-manual.tentative.https.html b/testing/web-platform/tests/window-placement/multi-screen-fullscreen-manual.tentative.https.html new file mode 100644 index 0000000000..620cd6e507 --- /dev/null +++ b/testing/web-platform/tests/window-placement/multi-screen-fullscreen-manual.tentative.https.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> +<title>Multi-Screen Window Placement test: element.requestFullscreen()</title> +<link rel="help" href="https://w3c.github.io/window-placement/"> +This test uses multi-screen details to request fullscreen on different +displays and swap between them.<br> +It runs manually with `wpt serve` and a compatible browser.<br><br> +<button id="setUpButton">Request screen details</button> +<ul id="testButtons"></ul> +<ul id="logger"></ul> +<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/helpers.js"></script> + +<script> +'use strict'; + +// Waits until `window` is located on `screen` or until 3 seconds elapses. +async function ensureWindowOnScreen(window, screen) { + // Returns true if window `w` in on screen `s`. + const isWindowOnScreen = async (w, s) => { + const center = { + x: w.screenLeft + w.outerWidth / 2, + y: w.screenTop + w.outerHeight / 2 + }; + return center.x >= s.left && (center.x < s.left + s.width) && + center.y >= s.top && (center.y < s.top + s.height) && + (await w.getScreenDetails()).currentScreen == s; + } + // Checks every 100ms if window `w` is on screen `s` up to 3s maximum. + const waitForWindowOnScreen = async (w, s, resolve, timestamp = Date.now()) => { + if (!w || w.closed || Date.now() - timestamp > 3000) + resolve(false); + else if (await isWindowOnScreen(w, s)) + resolve(true); + else + setTimeout(waitForWindowOnScreen.bind(this, w, s, resolve, timestamp), 100); + } + return new Promise(resolve => { waitForWindowOnScreen(window, screen, resolve); }); +} + +// Asserts that the browser window is correctly positioned on `screen`. +// `expectFullscreen` specifies whether to expect the window to be +// fullscreen or not. +async function testWindowOnScreen(test, screen, expectFullscreen) { + + // In chrome, the requestFullscreen promise may resolve before the + // transition completes and the bounds may be incorect in the meantime. + // Wait until the window is on the expected screen. + // TODO(crbug.com/1330724) Remove this. + await ensureWindowOnScreen(window, screen); + + assert_equals(!!document.fullscreenElement, expectFullscreen); + assert_equals((await window.getScreenDetails()).currentScreen, screen); + + if (expectFullscreen) { + // Window bounds should equal the bounds of the screen when in fullscreen. + assert_equals(window.screenX, screen.left); + assert_equals(window.screenY, screen.top); + assert_equals(window.innerWidth, screen.width); + assert_equals(window.innerHeight, screen.height); + } else { + // Verify the window is somewhere within the specified screen + assert_true(window.screenX >= screen.left); + assert_true(window.screenY >= screen.top); + assert_true(window.screenX + window.outerWidth <= screen.left + screen.width); + assert_true(window.screenY + window.outerHeight <= screen.top + screen.height); + } +} + +promise_test(async setUpTest => { + await setUpWindowPlacement(setUpTest, setUpButton); + const screenDetails = await getScreenDetails(); + assert_true(!!screenDetails, 'Error getting screen details'); + for (const [i, fullscreenScreen] of screenDetails.screens.entries()) { + const originalScreen = screenDetails.currentScreen; + const swapFullscreen = + screenDetails.screens[(i + 1) % screenDetails.screens.length]; + let testName = + `Fullscreen on '${fullscreenScreen.label}' and swap to ${swapFullscreen.label}`; + promise_test(async fullscreenTest => { + // Step 1: Enter Fullscreen. + await addTestTriggerButtonAndAwaitClick(testButtons, + `Step 1: ${testName} (Enter Fullscreen)`, + fullscreenTest); + log(`Requesting fullscreen on screen: ${fullscreenScreen.label}`); + await document.documentElement.requestFullscreen( + { screen: fullscreenScreen } + ); + await testWindowOnScreen(fullscreenTest, fullscreenScreen, + /*expectFullscreen*/true); + // Step 2: Swap to another screen. + await addTestTriggerButtonAndAwaitClick(testButtons, + `Step 2: ${testName} (Swap screens)`, + fullscreenTest); + log(`Swapping fullscreen to screen: ${swapFullscreen.label}`); + await document.documentElement.requestFullscreen( + { screen: swapFullscreen } + ); + await testWindowOnScreen(fullscreenTest, swapFullscreen, + /*expectFullscreen*/true); + // Step 3: Exit fullscreen. Should restore window to `originalScreen`. + await addTestTriggerButtonAndAwaitClick(testButtons, + `Step 3: ${testName} (Exit Fullscreen)`, + fullscreenTest); + log(`Exiting fullscreen. Window should restore to ${originalScreen.label}.`); + await document.exitFullscreen(); + await testWindowOnScreen(fullscreenTest, originalScreen, + /*expectFullscreen*/false); + }, testName); + } +}, 'Use multi-screen details to request fullscreen on target displays and swap between them'); +</script> diff --git a/testing/web-platform/tests/window-placement/multi-screen-window-open.tentative.https.html b/testing/web-platform/tests/window-placement/multi-screen-window-open.tentative.https.html new file mode 100644 index 0000000000..c453107d56 --- /dev/null +++ b/testing/web-platform/tests/window-placement/multi-screen-window-open.tentative.https.html @@ -0,0 +1,99 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> +<!-- user agents are not required to support open features other than `noopener` + and on some platforms position and size features don't make sense --> +<meta name="flags" content="may"> +<title>Multi-Screen Window Management test: window.open()</title> +<link rel="help" href="https://w3c.github.io/window-placement/"> +This test uses multi-screen details to open a popup window on each screen.<br> +It runs automated or manually with `wpt serve` and a compatible browser.<br><br> +<button id="setUpButton">Request screen details</button> +<ul id="popupButtons"></ul> +<button id="cleanUpButton">Close any open popups</button><br> +<input id="autoCleanUp" type="checkbox" checked=true>Auto-close popups</input> +<ul id="logger"></ul> +<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/helpers.js"></script> + +<script> +'use strict'; +let popups = []; + +cleanUpButton.addEventListener('click', async () => { + popups.forEach(p => p.close()); +}); + +function checkPopupPlacement(popup, x, y, allowSameScreenClamping) { + // Window.screenX|Y may be zero, if the user agent wishes to hide information + // about the screen of the output device. They also may incorrectly reflect + // the origin of content viewport; in that case, estimate window coordinates + // by subtracing estimated frame insets (top-heavy, horizontally centered). + // Synchronous estimated placements may be clamped to the current screen. + const error = 10; + const dX = popup.screenX - x - (popup.outerWidth - popup.innerWidth) / 2; + const dY = popup.screenY - y - (popup.outerHeight - popup.innerHeight); + + assert_true(!popup.screenX || popup.screenX == x || Math.abs(dX) <= error || + (allowSameScreenClamping && popup.screenX >= screen.availLeft && + popup.screenX < screen.availLeft + screen.availWidth)); + assert_true(!popup.screenY || popup.screenY == y || Math.abs(dY) <= error || + (allowSameScreenClamping && popup.screenY >= screen.availTop && + popup.screenY < screen.availTop + screen.availHeight)); +} + +async function testPopupOnScreen(popupTest, screen) { + // Show a popup child window on the associated screen. + const left = screen.availLeft + Math.floor(screen.availWidth / 2) - 150; + const top = screen.availTop + Math.floor(screen.availHeight / 2) - 50; + log(`Opening a popup on '${screen.label}' at (${left}, ${top})`); + let popup = window.open( + '/resources/blank.html', '', + `left=${left},top=${top},width=300,height=100`); + popups.push(popup); + if (autoCleanUp.checked) + popupTest.add_cleanup(popup.close); + + // Window.open() synchronously returns a Window with estimated screenX|Y. + // Initial `screenX` and `screenY` values should match `left` and `top`. + log(`<div style='margin-left: 40px'>Initial bounds: + (${popup.screenX}, ${popup.screenY}) + </div>`); + // Allow synchronous estimated placements to be clamped to the current screen. + checkPopupPlacement(popup, left, top, true); + popup.initialScreenX = popup.screenX; + popup.initialScreenY = popup.screenY; + + // Await document.visibilitychange to check resolved Window.screenX|Y values + // after asynchronous window creation and clamped placement has occurred. + const visibilitychangeWatcher = + new EventWatcher(popupTest, popup.document, ['visibilitychange']); + await visibilitychangeWatcher.wait_for('visibilitychange'); + popup.document.write(`Expected: (${left}, ${top}) <br> \ + Initial: (${popup.initialScreenX}, ${popup.initialScreenY}) <br> \ + Resolved: (${popup.screenX}, ${popup.screenY}) `); + log(`<div style='margin-left: 40px'>Resolved bounds: + (${popup.screenX}, ${popup.screenY}) + </div>`); + // Do not allow resolved placements to be clamped to the current screen. + checkPopupPlacement(popup, left, top, false); +} + +promise_test(async setUpTest => { + await setUpWindowManagement(setUpTest, setUpButton); + const screenDetails = await getScreenDetails(); + assert_true(!!screenDetails, 'Error getting screen details'); + assert_greater_than(screenDetails.screens.length, 0, 'Connect a screen'); + for (const s of screenDetails.screens) { + promise_test(async popupTest => { + await addTestTriggerButtonAndAwaitClick(popupButtons, + `Open a popup on '${s.label}'`, + popupTest); + await testPopupOnScreen(popupTest, s); + }, `Open a popup on '${s.label}'`); + } +}, 'Use multi-screen details to open a popup window on each screen'); +</script> diff --git a/testing/web-platform/tests/window-placement/resources/helpers.js b/testing/web-platform/tests/window-placement/resources/helpers.js new file mode 100644 index 0000000000..92442f9133 --- /dev/null +++ b/testing/web-platform/tests/window-placement/resources/helpers.js @@ -0,0 +1,68 @@ + +// Logs (appends) an HTML string to a logger element in a list format. +// An element in the document with id "logger" will be used as the log +// container. +function log(str) { + const entry = document.createElement('li'); + entry.innerHTML = str; + const loggerElement = document.getElementById('logger'); + loggerElement.appendChild(entry); + return entry; +} + +// Common setup for window management tests. Performs some basic assertions, and +// then waits for a click on the `setUpButton` element (for manual tests). +// Example usage: +// promise_test(async setUpTest => { +// await setUpWindowManagement(setUpTest, setUpButton); +// ... +// }); +async function setUpWindowManagement(setUpTest, setUpButton) { + assert_true( + 'getScreenDetails' in self && 'isExtended' in screen, + `API not supported; use Chromium (not content_shell) and enable + chrome://flags/#enable-experimental-web-platform-features`); + if (!screen.isExtended) + log(`WARNING: Use multiple screens for full test coverage`); + if (window.location.href.startsWith('file')) + log(`WARNING: Run via 'wpt serve'; file URLs lack permission support`); + + try { // Support manual testing where test_driver is not running. + await test_driver.set_permission({ name: 'window-management' }, 'granted'); + } catch { + } + const setUpWatcher = new EventWatcher(setUpTest, setUpButton, ['click']); + const setUpClick = setUpWatcher.wait_for('click'); + try { // Support manual testing where test_driver is not running. + await test_driver.click(setUpButton); + } catch { + } + await setUpClick; + setUpButton.disabled = true; +} + + +// Adds a button to the given `buttonContainer` element with the contents of +// `name`. Attaches an event watcher to the given test and waits for a signal +// from the test driver to click the button. If no test driver is available +// (manual testing) then awaits an actual click from the user instead. If +// `disableOnClick` is true, the button will also be disabled after it is +// clicked. +async function addTestTriggerButtonAndAwaitClick(buttonContainer, name, test) { + const button = document.createElement('button'); + button.innerHTML = name; + const entry = document.createElement('li'); + entry.appendChild(button); + buttonContainer.appendChild(entry); + const testWatcher = new EventWatcher(test, button, ['click']); + const buttonClick = testWatcher.wait_for('click'); + // Disable the button when it is clicked. + button.onclick = function() { + button.disabled = true; + }; + try { // Support manual testing where test_driver is not running. + await test_driver.click(button); + } catch { + } + await buttonClick; +} |