let initial_window_height, initial_visual_viewport_width, initial_visual_viewport_height;
// setup a meta viewport tag in the top document.
add_setup(async () => {
// Try to close the software keyboard to invoke `documentElement.focus()`.
document.documentElement.focus();
await SimpleTest.promiseWaitForCondition(
() => document.activeElement == document.documentElement, "Waiting for focus");
const eventPromise = new Promise(resolve => content.window.addEventListener("resize", resolve));
// Flush the viewport change.
content.document.documentElement.getBoundingClientRect();
// If this top level content is rendered as `scale < 1.0`, it means there
// was no meta viewport tag at all, so that adding the above meta viewport
// tag will fire a resize event, thus we need to wait for the event here.
// Otherwise, we will wait for the event in the first `resizes-content`
// test and the test will fail.
//
// NOTE: We need this `scale < 1.0` check for --run-until-failure option.
if (initial_scale < 1.0) {
await eventPromise;
}
});
SimpleTest.registerCleanupFunction(async () => {
await SpecialPowers.spawn(parent, [], async () => {
const meta = content.document.querySelector("#interactive-widget"); meta.setAttribute("content", "");
// Flush the change above.
content.document.documentElement.getBoundingClientRect();
// A dummy Promise to make sure that SpecialPowers.spawn's Promise will
// never be resolved until this script has run in the parent context.
await new Promise(resolve => resolve());
});
});
[ initial_window_height,
initial_visual_viewport_height, initial_visual_viewport_width ] = await getViewportMetrics();
ok(initial_visual_viewport_width < initial_visual_viewport_height,
`the visual viewport height (${initial_visual_viewport_height}) is less ` +
`than the visual viewport width (${initial_visual_viewport_width}), ` +
`it hightly suspects the virtual keyboard persists there, thus ` +
`we can't run this interactive-widget tests properly`);
});
async function setupInteractiveWidget(aValue) {
await SpecialPowers.spawn(parent, [aValue], async (value) => {
const meta = content.document.querySelector("#interactive-widget"); meta.setAttribute("content", `width=device-width, initial-scale=1, user-scalable=no, interactive-widget=${value}`);
// Flush the viewport change.
content.document.documentElement.getBoundingClientRect();
// A dummy Promise to make sure that SpecialPowers.spawn's Promise will
// never be resolved until these script have run in the parent context.
await new Promise(resolve => resolve());
});
}
// SpecialPowers.spawn doesn't provide any reasonable way to make sure event
// listeners have been set in the given context (bug 1743857), so here we post
// a message just before setting up a resize event listener and return two
// Promises, one will be resolved when we received the message, the other will
// be resolved when we got a resize event.
function setupResizeEventListener(aInteractiveWidget) {
const ready = new Promise(resolve => {
window.addEventListener("message", msg => {
if (msg.data == "interactive-widget:ready") {
resolve(msg.data)
}
}, { once: true });
});
const resizePromise = SpecialPowers.spawn(parent, [aInteractiveWidget], async (interactiveWidget) => {
// #testframe is the iframe id where our mochitest harness loads each test
// document, but if this test runs solely just like ./mach test TEST_PATH,
// the test document gets loaded in the top level content.
const target = content.document.querySelector("#testframe") ?
content.document.querySelector("#testframe").contentWindow : content.window;
let eventPromise;
if (interactiveWidget == "resizes-content") {
eventPromise = new Promise(resolve => content.window.addEventListener("resize", resolve));
} else if (interactiveWidget == "resizes-visual") {
eventPromise = new Promise(resolve => content.window.visualViewport.addEventListener("resize", resolve));
} else {
ok(false, `Unexpected interactive-widget=${interactiveWidget}`);
}
target.postMessage("interactive-widget:ready", "*");
await eventPromise;
});
return [ ready, resizePromise ];
}
// A utility function to hide the software keyboard.
// This function needs to be called while the software keyboard is shown on
// `resizes-content' or `resizes-visual` mode.
async function hideKeyboard() {
const interactiveWidget = await SpecialPowers.spawn(parent, [], () => {
const meta = content.document.querySelector("#interactive-widget");
return meta.getAttribute("content").match(/interactive-widget=([\w-].+?)[,\s]*$/)[1];
});
let [ readyPromise, resizePromise ] = setupResizeEventListener(interactiveWidget);
await readyPromise;
// Tap outside the textarea to hide the software keyboard.
await synthesizeNativeTap(document.querySelector("textarea"), 150, 50);
await resizePromise;
await SimpleTest.promiseWaitForCondition(
async () => {
let [ current_window_height, current_visual_viewport_height ] = await getViewportMetrics();
return current_window_height == initial_window_height &&
current_visual_viewport_height == initial_visual_viewport_height;
}, "Waiting for restoring the initial state");
}
// `resizes-content` test
add_task(async () => {
await setupInteractiveWidget("resizes-content");
// Setup a resize event listener in the top level document.
let [ readyPromise, resizePromise ] = setupResizeEventListener("resizes-content");
// Make sure the event listener has been set.
await readyPromise;
// Tap the textarea to show the software keyboard.
await synthesizeNativeTap(document.querySelector("textarea"), 50, 50);
await resizePromise;
// Now the software keyboard has appeared, before running the next test we
// need to hide the keyboard.
SimpleTest.registerCurrentTaskCleanupFunction(async () => await hideKeyboard());
await promiseAfterPaint();
await SimpleTest.promiseWaitForCondition(
() => document.activeElement == document.querySelector("textarea"), "Waiting for focus");
let [ window_height, visual_viewport_height ] = await getViewportMetrics();
ok(window_height < initial_window_height,
`The layout viewport got resized to ${window_height} from ${initial_window_height}`);
ok(visual_viewport_height < initial_visual_viewport_height,
`The visual viewport got resized to ${visual_viewport_height} from ${initial_visual_viewport_height}`);
});
// `resizes-visual` test
add_task(async () => {
await setupInteractiveWidget("resizes-visual");
// Setup a resize event listener in the top level document.
let [ readyPromise, resizePromise ] = setupResizeEventListener("resizes-visual");
// Make sure the event listener has been set.
await readyPromise;
// Tap the textarea to show the software keyboard.
await synthesizeNativeTap(document.querySelector("textarea"), 50, 50);
await resizePromise;
// Now the software keyboard has appeared, before running the next test we
// need to hide the keyboard.
SimpleTest.registerCurrentTaskCleanupFunction(async () => await hideKeyboard());
let [ window_height, visual_viewport_height ] = await getViewportMetrics();
is(window_height, initial_window_height, "The layout viewport is not resized on resizes-visual");
ok(visual_viewport_height < initial_visual_viewport_height,
`The visual viewport got resized to ${visual_viewport_height} from ${initial_visual_viewport_height}`);
});
// Append an element in the top level document that the element will be the
// underneath the software keyboard.
async function appendSpacer() {
await SpecialPowers.spawn(parent, [], async () => {
const div = content.document.createElement("div"); div.setAttribute("id", "interactive-widget-test-spacer"); div.style = "height: 200vh; position: absolute; top: 90vh;";
content.document.body.appendChild(div);
// Flush the change.
content.document.documentElement.getBoundingClientRect();
// A dummy Promise to make sure that SpecialPowers.spawn's Promise will
// never be resolved until these script have run in the parent context.
await new Promise(resolve => resolve());
});
// A dummy Promise to make sure that SpecialPowers.spawn's Promise will
// never be resolved until these script have run in the parent context.
await new Promise(resolve => resolve());
});
});
}
// `overlays-content` test
add_task(async () => {
await setupInteractiveWidget("overlays-content");
await appendSpacer();
// Tap the textarea to show the software keyboard.
await synthesizeNativeTap(document.querySelector("textarea"), 50, 50);
// Now the software keyboard has appeared, before running the next test we
// need to hide the keyboard.
SimpleTest.registerCurrentTaskCleanupFunction(async () => {
// Switch back to `resizes-content` mode so that we can receive a resize
// event when the keyboard gets hidden.
await setupInteractiveWidget("resizes-content");
await hideKeyboard();
});
let [ window_height, visual_viewport_height ] = await getViewportMetrics();
is(window_height, initial_window_height, "The layout viewport is not resized on overlays-content");
is(visual_viewport_height, initial_visual_viewport_height, "The visual viewport is not resized on overlays-content");
// Call a scrollIntoView() on an element underneath the keyboard and see if
// the current scroll position changes.
const scrollPosition = await SpecialPowers.spawn(parent, [], () => {
return content.window.scrollY;
});
await SpecialPowers.spawn(parent, [], async () => {
const div = content.document.querySelector("#interactive-widget-test-spacer"); div.scrollIntoView({ behavior: "instant" });
// Though two rAFs ensure there's at least one scroll event if there is,
// we use two additional rAFs just in case.
await new Promise(resolve => content.window.requestAnimationFrame(resolve));
await new Promise(resolve => content.window.requestAnimationFrame(resolve));
await new Promise(resolve => content.window.requestAnimationFrame(resolve));
await new Promise(resolve => content.window.requestAnimationFrame(resolve));
});
const newScrollPosition = await SpecialPowers.spawn(parent, [], () => {
return content.window.scrollY;
});
is(scrollPosition, newScrollPosition, "The scrollIntoView() call has no effect");
});
</script>
</body>
</html>
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.