From 28e9652e7f437233684a1db21a3c1183f0f9e596 Mon Sep 17 00:00:00 2001 From: Sean Feng Date: Tue, 5 Sep 2023 18:28:53 +0000 Subject: [PATCH] Bug 1830820 - Add tests for user input delay handling r=smaug Differential Revision: https://phabricator.services.mozilla.com/D186053 --- dom/base/test/browser.ini | 3 + .../test/browser_user_input_handling_delay.js | 82 ++++++++++++++ ...er_user_input_handling_delay_aboutblank.js | 58 ++++++++++ ...owser_user_input_handling_delay_bfcache.js | 107 ++++++++++++++++++ layout/base/PresShell.cpp | 3 +- modules/libpref/init/StaticPrefList.yaml | 7 ++ 6 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 dom/base/test/browser_user_input_handling_delay.js create mode 100644 dom/base/test/browser_user_input_handling_delay_aboutblank.js create mode 100644 dom/base/test/browser_user_input_handling_delay_bfcache.js diff --git a/dom/base/test/browser.ini b/dom/base/test/browser.ini index 9054f586e7a7..4a556174081d 100644 --- a/dom/base/test/browser.ini +++ b/dom/base/test/browser.ini @@ -105,3 +105,6 @@ support-files = file_browser_refresh_iframe.sjs [browser_page_load_event_telemetry.js] [browser_xml_toggle.js] +[browser_user_input_handling_delay.js] +[browser_user_input_handling_delay_bfcache.js] +[browser_user_input_handling_delay_aboutblank.js] diff --git a/dom/base/test/browser_user_input_handling_delay.js b/dom/base/test/browser_user_input_handling_delay.js new file mode 100644 index 000000000000..ccd3369c34c4 --- /dev/null +++ b/dom/base/test/browser_user_input_handling_delay.js @@ -0,0 +1,82 @@ +/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +async function test_user_input_handling_delay_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + `data:text/html,` + ); + + let canHandleInput = false; + let mouseDownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "mousedown" + ).then(function () { + Assert.ok( + canHandleInput, + "This promise should be resolved after the 5 seconds mark has passed" + ); + }); + + for (let i = 0; i < 10; ++i) { + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + } + // Wait for roughly 5 seconds to give chances for the + // above mousedown event to be handled. + await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + for (let i = 0; i < 330; ++i) { + await new Promise(r => { + content.requestAnimationFrame(r); + }); + } + }); + + // If any user input events were handled in the above 5 seconds + // the mouseDownPromise would be resolved with canHandleInput = false, + // so that the test would fail. + canHandleInput = true; + + // Ensure the events can be handled eventually + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + + await mouseDownPromise; + + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinRAF() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 100], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_helper(prefs); +}); + +add_task(async function test_MinElapsedTime() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 0], + ["dom.input_events.security.minTimeElapsedInMS", 5000], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_helper(prefs); +}); diff --git a/dom/base/test/browser_user_input_handling_delay_aboutblank.js b/dom/base/test/browser_user_input_handling_delay_aboutblank.js new file mode 100644 index 000000000000..c49a9bf7ed9c --- /dev/null +++ b/dom/base/test/browser_user_input_handling_delay_aboutblank.js @@ -0,0 +1,58 @@ +/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +async function test_user_input_handling_delay_aboutblank_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:blank"); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + // Open about:blank + content.window.open(); + }); + + const tab = await newTabOpened; + + let mouseDownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "mousedown" + ).then(function () { + Assert.ok(true, "about:blank can handle user input events anytime"); + }); + + // Now gBrowser.selectedBrowser is the newly opened about:blank + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + + await mouseDownPromise; + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinRAF_aboutblank() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 100], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_aboutblank_helper(prefs); +}); + +add_task(async function test_MinElapsedTime_aboutblank() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 0], + ["dom.input_events.security.minTimeElapsedInMS", 5000], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_aboutblank_helper(prefs); +}); diff --git a/dom/base/test/browser_user_input_handling_delay_bfcache.js b/dom/base/test/browser_user_input_handling_delay_bfcache.js new file mode 100644 index 000000000000..826f4340c5c7 --- /dev/null +++ b/dom/base/test/browser_user_input_handling_delay_bfcache.js @@ -0,0 +1,107 @@ +/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +async function test_user_input_handling_delay_BFCache_helper(prefs) { + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + `data:text/html,`, + true + ); + + let switchAwayPromise = BrowserTestUtils.browserLoaded( + browser, + false, + "about:blank" + ); + // Navigate away to make the page enters BFCache + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.location = "about:blank"; + }); + await switchAwayPromise; + + // Navigate back to restore the page from BFCache + let pageShownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "pageshow", + true + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.history.back(); + }); + + await pageShownPromise; + + let canHandleInput = false; + let mouseDownPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "mousedown" + ).then(function () { + Assert.ok( + canHandleInput, + "This promise should be resolved after the 5 seconds mark has passed" + ); + }); + // Ensure the events are discarded initially + for (let i = 0; i < 10; ++i) { + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + } + + // Wait for roughly 5 seconds to give chances for the + // above mousedown event to be handled. + await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + for (let i = 0; i < 330; ++i) { + await new Promise(r => { + content.requestAnimationFrame(r); + }); + } + }); + + // If any user input events were handled in the above 5 seconds + // the mouseDownPromise would be resolved with canHandleInput = false, + // so that the test would fail. + canHandleInput = true; + + // Ensure the events can be handled eventually + await BrowserTestUtils.synthesizeMouseAtPoint( + 10, + 10, + { type: "mousedown" }, + tab.linkedBrowser + ); + + await mouseDownPromise; + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_MinRAF_BFCache() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 100], + ["dom.input_events.security.minTimeElapsedInMS", 0], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_BFCache_helper(prefs); +}); + +add_task(async function test_MinElapsedTime_BFCache() { + const prefs = [ + ["dom.input_events.security.minNumTicks", 0], + ["dom.input_events.security.minTimeElapsedInMS", 5000], + ["dom.input_events.security.isUserInputHandlingDelayTest", true], + ]; + + await test_user_input_handling_delay_BFCache_helper(prefs); +}); diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 6bf99561f30c..7e991e5fb0b0 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -1988,7 +1988,8 @@ bool PresShell::CanHandleUserInputEvents(WidgetGUIEvent* aGUIEvent) { return true; } - if (aGUIEvent->mFlags.mIsSynthesizedForTests) { + if (aGUIEvent->mFlags.mIsSynthesizedForTests && + !StaticPrefs::dom_input_events_security_isUserInputHandlingDelayTest()) { return true; } diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 440dcf92aedf..0bf217a46a75 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -2813,6 +2813,13 @@ value: 100 mirror: always +# By default user input handling delay is disabled (mostly) for testing , +# this is used for forcefully enable it for certain tests. +- name: dom.input_events.security.isUserInputHandlingDelayTest + type: bool + value: false + mirror: always + # The maximum time (milliseconds) we reserve for handling input events in each # frame. - name: dom.input_event_queue.duration.max