fune/docshell/test/browser/browser_backforward_userinteraction.js
Johann Hofmann 15fcfd1312 Bug 1650095 - Part 4 - For Fission, reset SHEntryHasUserInteraction cache in the parent when adding/updating SH entries. r=smaug
When re-enabling the test case blocked by bug 1670933 it became apparent that we were not allowing
session history entries to be marked with user interaction when the SH entry was created by navigation
through a sub-frame. The code that we had for this only covered updating SH entries after pushState etc.,
not adding new entries for document loads.

When SH lives in the child this is easier to manage in nsDocShell, but with Fission it probably makes sense
to move this code to the parent.

Differential Revision: https://phabricator.services.mozilla.com/D97421
2020-11-18 23:42:46 +00:00

380 lines
11 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_PAGE =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
) + "dummy_page.html";
const IFRAME_PAGE =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
) + "dummy_iframe_page.html";
async function assertMenulist(entries, baseURL = TEST_PAGE) {
// Wait for the session data to be flushed before continuing the test
await new Promise(resolve =>
SessionStore.getSessionHistory(gBrowser.selectedTab, resolve)
);
let backButton = document.getElementById("back-button");
let contextMenu = document.getElementById("backForwardMenu");
info("waiting for the history menu to open");
let popupShownPromise = BrowserTestUtils.waitForEvent(
contextMenu,
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(backButton, {
type: "contextmenu",
button: 2,
});
await popupShownPromise;
ok(true, "history menu opened");
let nodes = contextMenu.childNodes;
is(
nodes.length,
entries.length,
"Has the expected number of contextMenu entries"
);
for (let i = 0; i < entries.length; i++) {
let node = nodes[i];
is(
node.getAttribute("uri").replace(/[?|#]/, "!"),
baseURL + "!entry=" + entries[i],
"contextMenu node has the correct uri"
);
}
let popupHiddenPromise = BrowserTestUtils.waitForEvent(
contextMenu,
"popuphidden"
);
contextMenu.hidePopup();
await popupHiddenPromise;
}
// There are different ways of loading a page, but they should exhibit roughly the same
// back-forward behavior for the purpose of requiring user interaction. Thus, we
// have a utility function that runs the same test with a parameterized method of loading
// new URLs.
async function runTopLevelTest(loadMethod, useHashes = false) {
let p = useHashes ? "#" : "?";
// Test with both pref on and off
for (let requireUserInteraction of [true, false]) {
Services.prefs.setBoolPref(
"browser.navigation.requireUserInteraction",
requireUserInteraction
);
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_PAGE + p + "entry=0"
);
let browser = tab.linkedBrowser;
assertBackForwardState(false, false);
await loadMethod(TEST_PAGE + p + "entry=1");
assertBackForwardState(true, false);
await assertMenulist([1, 0]);
await loadMethod(TEST_PAGE + p + "entry=2");
assertBackForwardState(true, false);
await assertMenulist(requireUserInteraction ? [2, 0] : [2, 1, 0]);
await loadMethod(TEST_PAGE + p + "entry=3");
info("Adding user interaction for entry=3");
// Add some user interaction to entry 3
await BrowserTestUtils.synthesizeMouse(
"body",
0,
0,
{},
browser.browsingContext,
true
);
assertBackForwardState(true, false);
await assertMenulist(requireUserInteraction ? [3, 0] : [3, 2, 1, 0]);
await loadMethod(TEST_PAGE + p + "entry=4");
assertBackForwardState(true, false);
await assertMenulist(requireUserInteraction ? [4, 3, 0] : [4, 3, 2, 1, 0]);
info("Adding user interaction for entry=4");
// Add some user interaction to entry 4
await BrowserTestUtils.synthesizeMouse(
"body",
0,
0,
{},
browser.browsingContext,
true
);
await loadMethod(TEST_PAGE + p + "entry=5");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
);
await goBack(TEST_PAGE + p + "entry=4");
await goBack(TEST_PAGE + p + "entry=3");
if (!requireUserInteraction) {
await goBack(TEST_PAGE + p + "entry=2");
await goBack(TEST_PAGE + p + "entry=1");
}
assertBackForwardState(true, true);
await assertMenulist(
requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
);
await goBack(TEST_PAGE + p + "entry=0");
assertBackForwardState(false, true);
if (!requireUserInteraction) {
await goForward(TEST_PAGE + p + "entry=1");
await goForward(TEST_PAGE + p + "entry=2");
}
await goForward(TEST_PAGE + p + "entry=3");
assertBackForwardState(true, true);
await assertMenulist(
requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
);
await goForward(TEST_PAGE + p + "entry=4");
assertBackForwardState(true, true);
await assertMenulist(
requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
);
await goForward(TEST_PAGE + p + "entry=5");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
);
BrowserTestUtils.removeTab(tab);
}
Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
}
async function runIframeTest(loadMethod) {
// Test with both pref on and off
for (let requireUserInteraction of [true, false]) {
Services.prefs.setBoolPref(
"browser.navigation.requireUserInteraction",
requireUserInteraction
);
// First test the boring case where we only have one iframe.
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
IFRAME_PAGE + "?entry=0"
);
let browser = tab.linkedBrowser;
assertBackForwardState(false, false);
await loadMethod(TEST_PAGE + "?sub_entry=1", "frame1");
assertBackForwardState(true, false);
await assertMenulist([0, 0], IFRAME_PAGE);
await loadMethod(TEST_PAGE + "?sub_entry=2", "frame1");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [0, 0] : [0, 0, 0],
IFRAME_PAGE
);
let bc = await SpecialPowers.spawn(browser, [], function() {
return content.document.getElementById("frame1").browsingContext;
});
// Add some user interaction to sub entry 2
await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
await loadMethod(TEST_PAGE + "?sub_entry=3", "frame1");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0],
IFRAME_PAGE
);
await loadMethod(TEST_PAGE + "?sub_entry=4", "frame1");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0, 0],
IFRAME_PAGE
);
if (!requireUserInteraction) {
await goBack(TEST_PAGE + "?sub_entry=3", true);
}
await goBack(TEST_PAGE + "?sub_entry=2", true);
assertBackForwardState(true, true);
await assertMenulist(
requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0, 0],
IFRAME_PAGE
);
await loadMethod(IFRAME_PAGE + "?entry=1");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [1, 0, 0] : [1, 0, 0, 0],
IFRAME_PAGE
);
BrowserTestUtils.removeTab(tab);
// Two iframes, now we're talking.
tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
IFRAME_PAGE + "?entry=0"
);
browser = tab.linkedBrowser;
await loadMethod(IFRAME_PAGE + "?entry=1");
assertBackForwardState(true, false);
await assertMenulist(requireUserInteraction ? [1, 0] : [1, 0], IFRAME_PAGE);
// Add some user interaction to frame 1.
bc = await SpecialPowers.spawn(browser, [], function() {
return content.document.getElementById("frame1").browsingContext;
});
await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
// Add some user interaction to frame 2.
bc = await SpecialPowers.spawn(browser, [], function() {
return content.document.getElementById("frame2").browsingContext;
});
await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
// Navigate frame 2.
await loadMethod(TEST_PAGE + "?sub_entry=1", "frame2");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [1, 1, 0] : [1, 1, 0],
IFRAME_PAGE
);
// Add some user interaction to frame 1, again.
bc = await SpecialPowers.spawn(browser, [], function() {
return content.document.getElementById("frame1").browsingContext;
});
await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
// Navigate frame 2, again.
await loadMethod(TEST_PAGE + "?sub_entry=2", "frame2");
assertBackForwardState(true, false);
await assertMenulist(
requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0],
IFRAME_PAGE
);
await goBack(TEST_PAGE + "?sub_entry=1", true);
assertBackForwardState(true, true);
await assertMenulist(
requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0],
IFRAME_PAGE
);
await goBack(TEST_PAGE + "?sub_entry=0", true);
assertBackForwardState(true, true);
await assertMenulist(
requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0],
IFRAME_PAGE
);
BrowserTestUtils.removeTab(tab);
}
Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
}
// Test that when the pref is flipped, we are skipping history
// entries without user interaction when following links with hash URIs.
add_task(async function test_hashURI() {
async function followLinkHash(url) {
info(`Creating and following a link to ${url}`);
let browser = gBrowser.selectedBrowser;
let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url);
await SpecialPowers.spawn(browser, [url], function(url) {
let a = content.document.createElement("a");
a.href = url;
content.document.body.appendChild(a);
a.click();
});
await loaded;
info(`Loaded ${url}`);
}
await runTopLevelTest(followLinkHash, true);
});
// Test that when the pref is flipped, we are skipping history
// entries without user interaction when using history.pushState.
add_task(async function test_pushState() {
await runTopLevelTest(pushState);
});
// Test that when the pref is flipped, we are skipping history
// entries without user interaction when using loadURI.
add_task(async function test_loadURI() {
await runTopLevelTest(loadURI);
});
// Test that when the pref is flipped, we are skipping history
// entries without user interaction when following a link.
add_task(async function test_followLink() {
await runTopLevelTest(followLink);
});
// Test that when the pref is flipped, we are skipping history
// entries without user interaction when navigating inside an iframe
// using history.pushState.
add_task(async function test_iframe_pushState() {
await runIframeTest(pushState);
});
// Test that when the pref is flipped, we are skipping history
// entries without user interaction when navigating inside an iframe
// by following links.
add_task(async function test_iframe_followLink() {
await runIframeTest(followLink);
});