From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser/browser_html_scroll_restoration.js | 223 +++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 toolkit/mozapps/extensions/test/browser/browser_html_scroll_restoration.js (limited to 'toolkit/mozapps/extensions/test/browser/browser_html_scroll_restoration.js') diff --git a/toolkit/mozapps/extensions/test/browser/browser_html_scroll_restoration.js b/toolkit/mozapps/extensions/test/browser/browser_html_scroll_restoration.js new file mode 100644 index 0000000000..8376782762 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_html_scroll_restoration.js @@ -0,0 +1,223 @@ +/* eslint max-len: ["error", 80] */ +"use strict"; + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +AddonTestUtils.initMochitest(this); +const server = AddonTestUtils.createHttpServer(); +const TEST_API_URL = `http://localhost:${server.identity.primaryPort}/discoapi`; + +const EXT_ID_EXTENSION = "extension@example.com"; +const EXT_ID_THEME = "theme@example.com"; + +let requestCount = 0; +server.registerPathHandler("/discoapi", (request, response) => { + // This test is expected to load the results only once, and then cache the + // results. + is(++requestCount, 1, "Expect only one discoapi request"); + + let results = { + results: [ + { + addon: { + authors: [{ name: "Some author" }], + current_version: { + files: [{ platform: "all", url: "data:," }], + }, + url: "data:,", + guid: "recommendation@example.com", + type: "extension", + }, + }, + ], + }; + response.write(JSON.stringify(results)); +}); + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.getAddons.discovery.api_url", TEST_API_URL]], + }); + + let mockProvider = new MockProvider(); + mockProvider.createAddons([ + { + id: EXT_ID_EXTENSION, + name: "Mock 1", + type: "extension", + userPermissions: { + origins: [""], + permissions: ["tabs"], + }, + }, + { + id: EXT_ID_THEME, + name: "Mock 2", + type: "theme", + }, + ]); +}); + +async function switchToView(win, type, param = "") { + let loaded = waitForViewLoad(win); + win.gViewController.loadView(`addons://${type}/${param}`); + await loaded; + await waitForStableLayout(win); +} + +// delta = -1 = go back. +// delta = +1 = go forwards. +async function historyGo(win, delta, expectedViewType) { + let loaded = waitForViewLoad(win); + win.history.go(delta); + await loaded; + is( + win.gViewController.currentViewId, + expectedViewType, + "Expected view after history navigation" + ); + await waitForStableLayout(win); +} + +async function waitForStableLayout(win) { + // In the test, it is important that the layout is fully stable before we + // consider the view loaded, because those affect the offset calculations. + await TestUtils.waitForCondition( + () => isLayoutStable(win), + "Waiting for layout to stabilize" + ); +} + +function isLayoutStable(win) { + // elements may affect the layout of a page, and therefore we + // should check whether its embedded style sheet has finished loading. + for (let bar of win.document.querySelectorAll("message-bar")) { + // Check for the existence of a CSS property from message-bar.css. + if (!win.getComputedStyle(bar).getPropertyValue("--message-bar-icon-url")) { + return false; + } + } + return true; +} + +function getScrollOffset(win) { + let { scrollTop: top, scrollLeft: left } = win.document.documentElement; + return { top, left }; +} + +// Scroll an element into view. The purpose of this is to simulate a real-world +// scenario where the user has moved part of the UI is in the viewport. +function scrollTopLeftIntoView(elem) { + elem.scrollIntoView({ block: "start", inline: "start" }); + // Sanity check: In this test, a large padding has been added to the top and + // left of the document. So when an element has been scrolled into view, the + // top and left offsets must be non-zero. + assertNonZeroScrollOffsets(getScrollOffset(elem.ownerGlobal)); +} + +function assertNonZeroScrollOffsets(offsets) { + ok(offsets.left, "Should have scrolled to the right"); + ok(offsets.top, "Should have scrolled down"); +} + +function checkScrollOffset(win, expected, msg = "") { + let actual = getScrollOffset(win); + let fuzz = AppConstants.platform == "macosx" ? 3 : 1; + isfuzzy(actual.top, expected.top, fuzz, `Top scroll offset - ${msg}`); + isfuzzy(actual.left, expected.left, fuzz, `Left scroll offset - ${msg}`); +} + +add_task(async function test_scroll_restoration() { + let win = await loadInitialView("discover"); + + // Wait until the recommendations have been loaded. These are cached after + // the first load, so we only need to wait once, at the start of the test. + await win.document.querySelector("recommended-addon-list").cardsReady; + + // Force scrollbar to appear, by adding enough space at the top and left. + win.document.body.style.paddingTop = "200vh"; + win.document.body.style.paddingLeft = "100vw"; + win.document.body.style.width = "200vw"; + + checkScrollOffset(win, { top: 0, left: 0 }, "initial page load"); + + scrollTopLeftIntoView(win.document.querySelector("recommended-addon-card")); + let discoOffsets = getScrollOffset(win); + assertNonZeroScrollOffsets(discoOffsets); + + // Switch from disco pane to extension list + + await switchToView(win, "list", "extension"); + checkScrollOffset(win, { top: 0, left: 0 }, "initial extension list"); + + scrollTopLeftIntoView(getAddonCard(win, EXT_ID_EXTENSION)); + let extListOffsets = getScrollOffset(win); + assertNonZeroScrollOffsets(extListOffsets); + + // Switch from extension list to details view. + + let loaded = waitForViewLoad(win); + getAddonCard(win, EXT_ID_EXTENSION).click(); + await loaded; + + checkScrollOffset(win, { top: 0, left: 0 }, "initial details view"); + scrollTopLeftIntoView(getAddonCard(win, EXT_ID_EXTENSION)); + let detailsOffsets = getScrollOffset(win); + assertNonZeroScrollOffsets(detailsOffsets); + + // Switch from details view back to extension list. + + await historyGo(win, -1, "addons://list/extension"); + checkScrollOffset(win, extListOffsets, "back to extension list"); + + // Now scroll to the bottom-right corner, so we can check whether the scroll + // offset is correctly restored when the extension view is loaded, even when + // the recommendations are loaded after the initial render. + ok( + win.document.querySelector("recommended-addon-card"), + "Recommendations have already been loaded" + ); + win.document.body.scrollIntoView({ block: "end", inline: "end" }); + extListOffsets = getScrollOffset(win); + assertNonZeroScrollOffsets(extListOffsets); + + // Switch back from the extension list to the details view. + + await historyGo(win, +1, `addons://detail/${EXT_ID_EXTENSION}`); + checkScrollOffset(win, detailsOffsets, "details view with default tab"); + + // Switch from the default details tab to the permissions tab. + // (this does not change the history). + win.document.querySelector(".tab-button[name='permissions']").click(); + + // Switch back from the details view to the extension list. + + await historyGo(win, -1, "addons://list/extension"); + checkScrollOffset(win, extListOffsets, "bottom-right of extension list"); + ok( + win.document.querySelector("recommended-addon-card"), + "Recommendations should have been loaded again" + ); + + // Switch back from extension list to the details view. + + await historyGo(win, +1, `addons://detail/${EXT_ID_EXTENSION}`); + // Scroll offsets are not remembered for the details view, because at the + // time of leaving the details view, the non-default tab was selected. + checkScrollOffset(win, { top: 0, left: 0 }, "details view, non-default tab"); + + // Switch back from the details view to the disco pane. + + await historyGo(win, -2, "addons://discover/"); + checkScrollOffset(win, discoOffsets, "after switching back to disco pane"); + + // Switch from disco pane to theme list. + + // Verifies that the extension list and theme lists are independent. + await switchToView(win, "list", "theme"); + checkScrollOffset(win, { top: 0, left: 0 }, "initial theme list"); + + await closeView(win); +}); -- cgit v1.2.3