diff options
Diffstat (limited to 'testing/web-platform/tests/screen-orientation')
21 files changed, 1013 insertions, 0 deletions
diff --git a/testing/web-platform/tests/screen-orientation/META.yml b/testing/web-platform/tests/screen-orientation/META.yml new file mode 100644 index 0000000000..633c917b0e --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/META.yml @@ -0,0 +1,6 @@ +spec: https://w3c.github.io/screen-orientation/ +suggested_reviewers: + - marcoscaceres + - cdumez + - michaelwasserman + - makotokato diff --git a/testing/web-platform/tests/screen-orientation/active-lock.html b/testing/web-platform/tests/screen-orientation/active-lock.html new file mode 100644 index 0000000000..6dcbe4a8ff --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/active-lock.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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> +<p id="#fragment"></p> +<a href="#fragment">fragment</a> +<script type="module"> + import { + attachIframe, + getOppositeOrientation, + makeCleanup, + } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const orientation = getOppositeOrientation(); + const p = screen.orientation.lock("landscape"); + await test_driver.click(document.querySelector("a")); + await p; + }, "Performing a fragment navigation must not abort the screen orientation change"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + const iframe = await attachIframe(); + iframe.contentDocument.body.innerHTML = ` + <p id="#fragment"></p> + <a href="#fragment">fragment</a> + `; + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const orientation = getOppositeOrientation(); + const p = iframe.contentWindow.screen.orientation.lock(orientation); + await test_driver.click(iframe.contentDocument.querySelector("a")); + await p; + iframe.remove(); + }, "Performing a fragment navigation within an iframe must not abort the lock promise"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + const iframe = await attachIframe(); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const orientation = getOppositeOrientation(); + const p = iframe.contentWindow.screen.orientation.lock(orientation); + const frameDOMException = iframe.contentWindow.DOMException; + iframe.contentWindow.location.href = "./resources/empty.html"; + await promise_rejects_dom(t, "AbortError", frameDOMException, p); + iframe.remove(); + }, "Unloading an iframe by navigating it must abort the lock promise"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/event-before-promise.html b/testing/web-platform/tests/screen-orientation/event-before-promise.html new file mode 100644 index 0000000000..56be4379ac --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/event-before-promise.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> + import { + getOppositeOrientation, + makeCleanup, + } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const result = await Promise.race([ + new Promise((resolve) => { + screen.orientation.addEventListener("change", resolve); + }), + screen.orientation.lock(getOppositeOrientation()) + ]); + assert_true(result instanceof Event, "Expected an instance of Event"); + }, "The 'change' event must fire before the [[orientationPendingPromise]] is resolved."); +</script> diff --git a/testing/web-platform/tests/screen-orientation/fullscreen-interactions.html b/testing/web-platform/tests/screen-orientation/fullscreen-interactions.html new file mode 100644 index 0000000000..cffacc1f4f --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/fullscreen-interactions.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> + import { + getOppositeOrientation, + attachIframe, + } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const lockPromise = screen.orientation.lock(getOppositeOrientation()); + await document.exitFullscreen(); + await promise_rejects_dom(t, "AbortError", lockPromise); + }, "Fully unlocking the screen orientation causes a pending lock to be aborted"); + + promise_test(async (t) => { + const iframe = await attachIframe(); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const lockPromise = iframe.contentWindow.screen.orientation.lock( + getOppositeOrientation() + ); + await document.exitFullscreen(); + const frameDOMException = iframe.contentWindow.DOMException; + await promise_rejects_dom(t, "AbortError", frameDOMException, lockPromise); + }, "Fully unlocking the screen orientation causes a pending lock in a nested browsing context to be aborted"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/hidden_document.html b/testing/web-platform/tests/screen-orientation/hidden_document.html new file mode 100644 index 0000000000..6c39d6e6e5 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/hidden_document.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta name="timeout" content="long" /> +<title> + Prevent hidden documents from locking orientation +</title> +<link rel="help" href="https://github.com/w3c/screen-orientation/pull/232" /> +<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="/page-visibility/resources/window_state_context.js"></script> +<script type="module"> + import { makeCleanup, getOppositeOrientation } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + const { minimize, restore } = window_state_context(t); + t.add_cleanup(makeCleanup()); + + await minimize(); + + assert_equals(document.visibilityState, "hidden", "Document must be hidden"); + const opposite = getOppositeOrientation(); + await promise_rejects_dom(t, "SecurityError", screen.orientation.lock(opposite) ); + }, "hidden documents must reject went trying to call lock or unlock"); + + promise_test(async (t) => { + const { minimize, restore } = window_state_context(t); + + await minimize(); + + assert_equals(document.visibilityState, "hidden", "Document must be hidden"); + assert_throws_dom("SecurityError", () => screen.orientation.unlock()); + }, "hidden documents must reject went trying to call unlock"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/idlharness.window.js b/testing/web-platform/tests/screen-orientation/idlharness.window.js new file mode 100644 index 0000000000..115f6ccb1e --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/idlharness.window.js @@ -0,0 +1,17 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js + +'use strict'; + +// https://w3c.github.io/screen-orientation/ + +idl_test( + ['screen-orientation'], + ['dom', 'cssom-view', 'html'], + idl_array => { + idl_array.add_objects({ + Screen: ['screen'], + ScreenOrientation: ['screen.orientation'] + }); + } +); diff --git a/testing/web-platform/tests/screen-orientation/lock-bad-argument.html b/testing/web-platform/tests/screen-orientation/lock-bad-argument.html new file mode 100644 index 0000000000..f13ce2a8b9 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/lock-bad-argument.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(t => { + const invalid_lock_types = [ + "invalid-orientation", + null, + undefined, + 123, + window, + "", + true, + ["portrait-primary", "landscape-primary"], + ]; + const promisesToReject = invalid_lock_types.map(type => + promise_rejects_js(t, TypeError, screen.orientation.lock(type)) + ); + return Promise.all(promisesToReject); +}, "screen.orientation.lock() must throw given invalid input."); + +promise_test(t => { + return promise_rejects_js(t, TypeError, screen.orientation.lock()); +}, "screen.orientation.lock() must throw when the input is missing."); +</script> + diff --git a/testing/web-platform/tests/screen-orientation/lock-basic.html b/testing/web-platform/tests/screen-orientation/lock-basic.html new file mode 100644 index 0000000000..584747113c --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/lock-basic.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> +import { attachIframe, makeCleanup, getOppositeOrientation } from "./resources/orientation-utils.js"; + +promise_test(async t => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen") + await document.documentElement.requestFullscreen(); + const value = await screen.orientation.lock('any'); + assert_equals(value, undefined); +}, "Test that screen.orientation.lock returns a promise which will be fulfilled with a void value."); + +promise_test(async t => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen") + await document.documentElement.requestFullscreen(); + const initialOrientation = screen.orientation.type; + const orientations = [ + 'any', + 'natural', + 'portrait', + 'landscape', + 'portrait-secondary', + 'landscape-primary', + 'landscape-secondary', + 'portrait-primary', + ]; + for (const orientation of orientations) { + try { + await screen.orientation.lock(orientation); + } catch(err) { + if (err.name === "NotSupportedError") { + continue; + } + assert_unreached("Unknown error: " + err); + } + const { type } = screen.orientation; + switch (orientation) { + case 'any': + break; + case 'natural': + assert_true(type.endsWith("primary"), `Expected primary orientation for "${orientation}", got "${type}"`); + break; + case 'portrait': + assert_true(type.startsWith("portrait"), `Expected portrait orientation for "${orientation}", got "${type}"`); + break; + case 'landscape': + assert_true(type.startsWith("landscape"), `Expected landscape orientation for "${orientation}", got "${type}"`); + break; + default: + assert_equals(type, orientation, "Expected orientation to change"); + break; + } + await screen.orientation.lock(initialOrientation); + } +}, "Test that screen.orientation.lock returns a pending promise."); + +promise_test(async t => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen") + await document.documentElement.requestFullscreen(); + const initialType = screen.orientation.type; + const newType = getOppositeOrientation(); + const p = screen.orientation.lock(newType); + assert_equals(screen.orientation.type, initialType, "Must not change orientation until next spin of event loop"); + await p; + const finalType = screen.orientation.type; + assert_true(finalType.startsWith(newType), `Expected type to start with ${newType}, got "${finalType}"`); +}, "Test that screen.orientation.lock() is actually async"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/lock-sandboxed-iframe.html b/testing/web-platform/tests/screen-orientation/lock-sandboxed-iframe.html new file mode 100644 index 0000000000..ab954f04b4 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/lock-sandboxed-iframe.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> + import { + attachIframe + } from "./resources/orientation-utils.js"; + + function wait_result() { + return new Promise((resolve) => { + function callback(evt) { + switch (evt.data.result) { + case "locked": + resolve(evt.data.orientation); + break; + case "errored": + resolve(evt.data.name); + break; + default: + assert_unreached(`Unexpected message: ${evt.data.result}`); + return; + } + window.removeEventListener("message", callback); + resolve(evt.data.msg); + } + window.addEventListener("message", callback); + }); + } + + promise_test(async (t) => { + const iframe = await attachIframe({ + src: "resources/sandboxed-iframe-locking.html", + sandbox: "allow-scripts allow-same-origin", + }); + const message = await wait_result(); + assert_equals( + message, + "SecurityError", + "screen.lockOrientation() throws a SecurityError" + ); + }, "Test without 'allow-orientation-lock' sandboxing directive"); + + promise_test(async (t) => { + const iframe = await attachIframe({ + src: "resources/sandboxed-iframe-locking.html", + sandbox: "allow-scripts allow-same-origin allow-orientation-lock", + }); + const message = await wait_result(); + assert_equals( + message, + "portrait-primary", + "screen.orientation lock to portrait-primary" + ); + }, "Test with 'allow-orientation-lock' sandboxing directive"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/lock-unlock-check.html b/testing/web-platform/tests/screen-orientation/lock-unlock-check.html new file mode 100644 index 0000000000..b415f7b2a8 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/lock-unlock-check.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> + import { + attachIframe, + getOppositeOrientation, + } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + screen.orientation.addEventListener( + "change", + async () => { + await screen.orientation.lock(getOppositeOrientation()); + }, + { once: true } + ); + await screen.orientation.lock(getOppositeOrientation()); + }, "Re-locking the screen orientation after a change event fires must not abort"); + + promise_test(async (t) => { + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + screen.orientation.onchange = async () => { + screen.orientation.onchange = null; + screen.orientation.unlock(); + }; + await screen.orientation.lock(getOppositeOrientation()); + }, "Unlocking the screen orientation after a change event must not abort"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/nested-documents.html b/testing/web-platform/tests/screen-orientation/nested-documents.html new file mode 100644 index 0000000000..efaed9cec8 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/nested-documents.html @@ -0,0 +1,85 @@ +<!DOCTYPE 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> +<body> + <script type="module"> + import { + attachIframe, + makeCleanup, + getOppositeOrientation, + } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + const iframe = await attachIframe(); + const iframeWin = iframe.contentWindow; + + // Go full screen + await test_driver.bless("request full screen"); + await iframe.contentDocument.documentElement.requestFullscreen(); + + // Lock the orientation from the iframe + const opposite = getOppositeOrientation(); + const iframePromise = iframeWin.screen.orientation.lock(opposite); + + // Calling lock() from top-level will cancel the iframe's promise + const topPromise = window.screen.orientation.lock(opposite); + await promise_rejects_dom( + t, + "AbortError", + iframeWin.DOMException, + iframePromise + ); + await topPromise; + }, "Requesting orientation lock from one document cancels the lock request from another document"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + // Create 3 nested iframes + const src = "/screen-orientation/resources/empty.html"; + const outerIframe = await attachIframe({ src: `${src}#1` }); + const innerIframe = await attachIframe({ + context: outerIframe.contentWindow, + src: `${src}#2`, + }); + + const iframes = [outerIframe, innerIframe]; + + // Go full screen + await test_driver.bless("request full screen"); + await innerIframe.contentDocument.documentElement.requestFullscreen(); + const opposite = getOppositeOrientation(); + + // Each iframe tries to lock the orientation + const requestToLock = iframes.map((iframe) => { + return { + promise: iframe.contentWindow.screen.orientation.lock(opposite), + context: iframe.contentWindow, + }; + }); + + // But calling lock() from top-level will aborts all iframe's promises + const topPromise = window.screen.orientation.lock(opposite); + + // Check that all promises are rejected with AbortError + const abortedPromises = []; + for (let i = 0; i < requestToLock.length; i++) { + const { promise, context } = requestToLock[i]; + const p = promise_rejects_dom( + t, + "AbortError", + context.DOMException, + promise, + `Expected request to lock orientation from iframe ${i} to abort` + ); + abortedPromises.push(p); + } + await Promise.all(abortedPromises); + + // Finally, top-level promise resolves + await topPromise; + }, "The orientation lock from one document affects lock requests from other documents"); + </script> +</body> diff --git a/testing/web-platform/tests/screen-orientation/non-fully-active.html b/testing/web-platform/tests/screen-orientation/non-fully-active.html new file mode 100644 index 0000000000..ee1acf07bd --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/non-fully-active.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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> +<body> +<script type="module"> + import { attachIframe, getOppositeOrientation } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + const iframe = await attachIframe(); + const { orientation } = iframe.contentWindow.screen; + + const frameDOMException = iframe.contentWindow.DOMException; + iframe.remove(); + + await promise_rejects_dom( + t, + "InvalidStateError", + frameDOMException, + orientation.lock(getOppositeOrientation()) + ); + }, "Attempting to lock non-fully active documents results in a InvalidStateError"); + + promise_test(async (t) => { + const iframe = await attachIframe(); + const { orientation } = iframe.contentWindow.screen; + + const frameDOMException = iframe.contentWindow.DOMException; + iframe.remove(); + + assert_throws_dom("InvalidStateError", frameDOMException, () => { orientation.unlock() }); + }, "Attempting to unlock non-fully active documents results in a InvalidStateError"); + + promise_test(async (t) => { + const iframe = await attachIframe(); + const { orientation } = iframe.contentWindow.screen; + + await test_driver.bless("request full screen", null, iframe.contentWindow); + await iframe.contentDocument.documentElement.requestFullscreen(); + + const p = orientation.lock(getOppositeOrientation()); + + const frameDOMException = iframe.contentWindow.DOMException; + iframe.remove(); + + await promise_rejects_dom(t, "AbortError", frameDOMException, p); + assert_throws_dom("InvalidStateError", frameDOMException, () => { orientation.unlock() }); + }, "Making a document non-fully active while locking results in an AbortError"); +</script> +</body> diff --git a/testing/web-platform/tests/screen-orientation/onchange-event-subframe.html b/testing/web-platform/tests/screen-orientation/onchange-event-subframe.html new file mode 100644 index 0000000000..1b4b8cd428 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/onchange-event-subframe.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> + import { + attachIframe, + makeCleanup, + getOppositeOrientation, + } from "./resources/orientation-utils.js"; + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request fullscreen"); + await document.documentElement.requestFullscreen(); + let orientations = ["portrait", "landscape"]; + if (screen.orientation.type.includes("portrait")) { + orientations = orientations.reverse(); + } + const messageWatcher = new EventWatcher(t, window, "message"); + const changeWatcher = new EventWatcher(t, screen.orientation, "change"); + const iframe = await attachIframe({ + src: "resources/iframe-listen-orientation-change.html", + sandbox: "allow-scripts allow-same-origin", + }); + for (const orientation of orientations) { + const messagePromise = messageWatcher.wait_for("message"); + const eventPromise = changeWatcher.wait_for("change"); + await screen.orientation.lock(orientation); + const winner = await Promise.race([eventPromise, messagePromise]); + assert_true(winner instanceof Event, "change event must be fired first"); + const message = await messagePromise; + assert_true( + message.data.startsWith(orientation), + "subframe receives orientation change event" + ); + } + iframe.remove(); + }, "Test subframes receive orientation change events"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + const iframe = await attachIframe(); + const opposite = getOppositeOrientation(); + + const topEventPromise = new EventWatcher( + t, + screen.orientation, + "change" + ).wait_for("change"); + const iframeEventPromise = new EventWatcher( + t, + iframe.contentWindow.screen.orientation, + "change" + ).wait_for("change"); + + // Lock from the iframe + await test_driver.bless("request fullscreen"); + await document.documentElement.requestFullscreen(); + const lockPromise = iframe.contentWindow.screen.orientation.lock(opposite); + + const winningEvent = await Promise.race([ + topEventPromise, + iframeEventPromise, + ]); + assert_true( + winningEvent instanceof window.Event, + "top-level change event must be fired first" + ); + + const iframeEvent = await iframeEventPromise; + assert_true( + iframeEvent instanceof iframe.contentWindow.Event, + "iframe event eventually fires" + ); + + await lockPromise; + iframe.remove(); + }, "Check directly that events are fired in right order (from top to bottom)"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/onchange-event.html b/testing/web-platform/tests/screen-orientation/onchange-event.html new file mode 100644 index 0000000000..e0d3afd7b4 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/onchange-event.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> +import { makeCleanup } from "./resources/orientation-utils.js"; +promise_test(async t => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const type = screen.orientation.type.startsWith("portrait") ? "portrait" : "landscape"; + screen.orientation.onchange = t.unreached_func("change event should not be fired"); + await screen.orientation.lock(type); + assert_true(screen.orientation.type.startsWith(type)); +}, "Test that orientationchange event is not fired when the orientation does not change."); + +promise_test(async t => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + let orientations = [ + 'portrait', + 'landscape', + ]; + if (screen.orientation.type.startsWith('portrait')) { + orientations = orientations.reverse(); + } + const orientationWatcher = new EventWatcher(t, screen.orientation, 'change'); + + for (const orientation of orientations) { + // change event is fired before resolving promise by lock. + let lockPromise = screen.orientation.lock(orientation); + const result = await Promise.race([ + lockPromise, + orientationWatcher.wait_for('change'), + ]); + assert_true(result instanceof Event, "The event must be fired first."); + assert_true(screen.orientation.type.startsWith(orientation), "The orientation must match"); + await lockPromise; + } +}, "Test that orientationchange event is fired when the orientation changes."); +</script> diff --git a/testing/web-platform/tests/screen-orientation/orientation-reading.html b/testing/web-platform/tests/screen-orientation/orientation-reading.html new file mode 100644 index 0000000000..90bbb8071d --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/orientation-reading.html @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> +"use strict"; +import { + makeCleanup, + getOppositeOrientation, +} from "./resources/orientation-utils.js"; + +test(() => { + assert_true("type" in screen.orientation, ".type must be present"); + assert_true("angle" in screen.orientation, ".angle must be present"); +}, "screen.orientation attributes are present"); + +async function testExpectedOrientationAngles(expectedAngles) { + for (const [orientation, expectedAngle] of Object.entries(expectedAngles)) { + try { + if (screen.orientation.type !== orientation) { + await screen.orientation.lock(orientation); + } + assert_equals( + screen.orientation.angle, + expectedAngle, + `Orientation angle for '${orientation}' must be ${expectedAngle} degrees` + ); + } catch (err) { + // implementation might not support locking to this orientation + } + } +} + +promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + + const expectedAnglesPortrait = { + "portrait-primary": 0, + "landscape-primary": 90, + "portrait-secondary": 180, + "landscape-secondary": 270, + }; + + await testExpectedOrientationAngles(expectedAnglesPortrait); +}, "Test the orientations and associated angles when the natural orientation is 'portrait'"); + +promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + + const expectedAnglesLandscape = { + "landscape-primary": 0, + "portrait-primary": 90, + "landscape-secondary": 180, + "portrait-secondary": 270, + }; + + await testExpectedOrientationAngles(expectedAnglesLandscape); +}, "Test the orientations and associated angles when the natural orientation is 'landscape'"); + +test(() => { + const { angle, type } = screen.orientation; + + assert_throws_js( + TypeError, + () => { + screen.orientation.type = "foo"; + }, + "throws when setting ScreenOrientation.type to a string in strict mode" + ); + assert_throws_js( + TypeError, + () => { + screen.orientation.angle = 42; + }, + "throws when setting ScreenOrientation.angle to a number in strict mode" + ); + + assert_equals(screen.orientation.type, type); + assert_equals(screen.orientation.angle, angle); +}, "Test that ScreenOrientation properties are not writable"); + +test(() => { + assert_equals(screen.orientation, screen.orientation); +}, "Test that ScreenOrientation is always the same object"); + +promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const initialType = screen.orientation.type; + const initialAngle = screen.orientation.angle; + const orientationWatcher = new EventWatcher(t, screen.orientation, "change"); + const newOrientationType = getOppositeOrientation(); + + // change event is fired before resolving promise by lock. + const event = await Promise.race([ + orientationWatcher.wait_for("change"), + screen.orientation.lock(newOrientationType), + ]); + assert_true(event instanceof Event, "expected event"); + assert_not_equals( + screen.orientation.type, + initialType, + ".type must change" + ); + assert_not_equals( + screen.orientation.angle, + initialAngle, + ".angle must change" + ); +}, "Test that ScreenOrientation's attribute values change after 'change' event fires"); +</script> diff --git a/testing/web-platform/tests/screen-orientation/page-visibility-manual.html b/testing/web-platform/tests/screen-orientation/page-visibility-manual.html new file mode 100644 index 0000000000..cc63279217 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/page-visibility-manual.html @@ -0,0 +1,110 @@ +<!DOCTYPE html> +<html> +<body> +<meta name="timeout" content="long"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<meta name='flags' content='interact'> +<p>Switch the page to background, then switch back in a minute.</p> +<iframe src='about:blank'></iframe> +<script> + +var eventVisibleTest = async_test("Test that a change event is fired when the page is visible."); +var noEventHiddenTest = async_test("Test that change event is not fired when the page is not visible."); +var orientationUnchangeHiddenTest = async_test("Test that screen.orientation keeps returning the same orientation when the page is not visible."); +var orientationUpdateVisibleTest = async_test("Test that screen.orientation is updated once the page is visible again."); +var frameEventsTest = async_test("Test that the iframe got as many events as the main frame."); + +var orientationChangeContinuation = null; +var orientationChangeEventListenerCalls = 0; +var orientationChangeEventListenerCallsForFrame = 0; + +screen.orientation.addEventListener('change', function() { + orientationChangeEventListenerCalls++; + if (orientationChangeEventContinuation) { + setTimeout(orientationChangeEventContinuation); + orientationChangeEventContinuation = null; + } +}); + +window.frames[0].screen.orientation.addEventListener('change', function() { + orientationChangeEventListenerCallsForFrame++; +}); + +document.addEventListener("visibilitychange", function () { + if(document.hidden) + runNoEventHiddenTest(); + else + runOrientationUpdateVisibleTest(); +}); + +function runEventVisibleTest() { + eventVisibleTest.step(function() { + assert_false(document.hidden); + }); + + screen.orientation.lock("landscape-primary").then(function() {}, function() {}); + + orientationChangeEventContinuation = function() { + eventVisibleTest.step(function() { + assert_equals(orientationChangeEventListenerCalls, 1); + assert_equals(screen.orientation.type, "landscape-primary"); + }); + eventVisibleTest.done(); + + }; +} + +function runNoEventHiddenTest() { + + noEventHiddenTest.step(function() { + assert_true(document.hidden); + }); + + screen.orientation.lock("portrait-primary").then(function() {}, function() {}); + + noEventHiddenTest.step(function() { + assert_equals(orientationChangeEventListenerCalls, 1); + }); + noEventHiddenTest.done(); + + runOrientationUnchangeHiddenTest(); +} + +function runOrientationUnchangeHiddenTest() { + orientationUnchangeHiddenTest.step(function() { + assert_equals(screen.orientation.type, "landscape-primary"); + }); + orientationUnchangeHiddenTest.done(); + +} + +function runOrientationUpdateVisibleTest() { + + orientationChangeEventContinuation = function() { + orientationUpdateVisibleTest.step(function() { + assert_false(document.hidden); + // A change event should have been fired. + assert_equals(orientationChangeEventListenerCalls, 2); + // Should keep returning the start returning the orientation value. + assert_equals(screen.orientation.type, "portrait-primary"); + }); + + orientationUpdateVisibleTest.done(); + + runFrameEventsTest(); + }; +} + +function runFrameEventsTest() { + frameEventsTest.step(function() { + assert_equals(orientationChangeEventListenerCallsForFrame, orientationChangeEventListenerCalls); + }); + frameEventsTest.done(); +} + +runEventVisibleTest(); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/screen-orientation/resources/empty.html b/testing/web-platform/tests/screen-orientation/resources/empty.html new file mode 100644 index 0000000000..0e76edd65b --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/resources/empty.html @@ -0,0 +1 @@ +<!DOCTYPE html> diff --git a/testing/web-platform/tests/screen-orientation/resources/iframe-listen-orientation-change.html b/testing/web-platform/tests/screen-orientation/resources/iframe-listen-orientation-change.html new file mode 100644 index 0000000000..68a67f8818 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/resources/iframe-listen-orientation-change.html @@ -0,0 +1,9 @@ +<script> + try { + window.screen.orientation.addEventListener("change", () => { + parent.window.postMessage(screen.orientation.type, "*"); + }); + } catch (err) { + parent.window.postMessage(err.message, "*"); + } +</script> diff --git a/testing/web-platform/tests/screen-orientation/resources/orientation-utils.js b/testing/web-platform/tests/screen-orientation/resources/orientation-utils.js new file mode 100644 index 0000000000..95383750f1 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/resources/orientation-utils.js @@ -0,0 +1,52 @@ +/** + * + * @param {object} options + * @param {string} options.src - The iframe src + * @param {Window} options.context - The browsing context in which the iframe will be created + * @param {string} options.sandbox - The sandbox attribute for the iframe + * @returns + */ +export async function attachIframe(options = {}) { + const { src, context, sandbox, allowFullscreen } = { + ...{ + src: "about:blank", + context: self, + allowFullscreen: true, + sandbox: null, + }, + ...options, + }; + const iframe = context.document.createElement("iframe"); + if (sandbox !== null) iframe.sandbox = sandbox; + iframe.allowFullscreen = allowFullscreen; + await new Promise((resolve) => { + iframe.onload = resolve; + iframe.src = src; + context.document.body.appendChild(iframe); + }); + return iframe; +} + +export function getOppositeOrientation() { + return screen.orientation.type.startsWith("portrait") + ? "landscape" + : "portrait"; +} + +export function makeCleanup( + initialOrientation = screen.orientation?.type.split(/-/)[0] +) { + return async () => { + if (initialOrientation) { + try { + await screen.orientation.lock(initialOrientation); + } catch {} + } + screen.orientation.unlock(); + requestAnimationFrame(async () => { + try { + await document.exitFullscreen(); + } catch {} + }); + }; +} diff --git a/testing/web-platform/tests/screen-orientation/resources/sandboxed-iframe-locking.html b/testing/web-platform/tests/screen-orientation/resources/sandboxed-iframe-locking.html new file mode 100644 index 0000000000..436c67f5b5 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/resources/sandboxed-iframe-locking.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script> +test_driver.set_test_context(parent); + +// At first, run simple unlock test without lock. +screen.orientation?.unlock(); + +test_driver.bless("request full screen", async () => { + const data = {}; + try { + await document.documentElement.requestFullscreen(); + await screen.orientation.lock("portrait") + data.result = "locked"; + data.orientation = screen.orientation.type; + } catch (error) { + data.result = "errored"; + data.name = error.name; + } + + screen.orientation.unlock(); + try { + await document.exitFullscreen(); + } catch (error) { + data.result = "errored"; + data.name = error.name; + } + + parent.window.postMessage(data, "*"); +}); +</script> diff --git a/testing/web-platform/tests/screen-orientation/unlock.html b/testing/web-platform/tests/screen-orientation/unlock.html new file mode 100644 index 0000000000..d5f32eb7be --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/unlock.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<meta viewport="width=device-width, initial-scale=1" /> +<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 type="module"> + import { + getOppositeOrientation, + makeCleanup, + attachIframe, + } from "./resources/orientation-utils.js"; + + test(() => { + screen.orientation.unlock(); + }, "unlock() doesn't throw when there is no lock"); + + test(() => { + const value = screen.orientation.unlock(); + assert_equals(value, undefined); + }, "unlock() returns a void value"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + screen.orientation.unlock(); + }, "unlock() doesn't throw when there is no lock with fullscreen"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const promise = screen.orientation.lock(getOppositeOrientation()); + screen.orientation.unlock(); + await promise_rejects_dom(t, "AbortError", promise); + }, "unlock() aborts a pending lock request"); + + promise_test(async (t) => { + t.add_cleanup(makeCleanup()); + await test_driver.bless("request full screen"); + await document.documentElement.requestFullscreen(); + const iframe = await attachIframe(); + const promise = iframe.contentWindow.screen.orientation.lock( + getOppositeOrientation() + ); + screen.orientation.unlock(); + await promise_rejects_dom( + t, + "AbortError", + iframe.contentWindow.DOMException, + promise + ); + }, "unlock() aborts a pending lock request across documents"); +</script> |