Bug 1750174 - Auto-refreshing pages reposition themselves to the top upon refresh. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D137383
This commit is contained in:
Peter Van der Beken 2022-02-02 13:22:30 +00:00
Родитель 4755c564c2
Коммит f1ed7fa59e
6 изменённых файлов: 84 добавлений и 28 удалений

Просмотреть файл

@ -3499,13 +3499,21 @@ bool BrowsingContext::IsPopupAllowed() {
/* static */ /* static */
bool BrowsingContext::ShouldAddEntryForRefresh( bool BrowsingContext::ShouldAddEntryForRefresh(
nsIURI* aCurrentURI, const SessionHistoryInfo& aInfo) { nsIURI* aCurrentURI, const SessionHistoryInfo& aInfo) {
if (aInfo.GetPostData()) { return ShouldAddEntryForRefresh(aCurrentURI, aInfo.GetURI(),
aInfo.GetPostData());
}
/* static */
bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aCurrentURI,
nsIURI* aNewURI,
bool aHasPostData) {
if (aHasPostData) {
return true; return true;
} }
bool equalsURI = false; bool equalsURI = false;
if (aCurrentURI) { if (aCurrentURI) {
aCurrentURI->Equals(aInfo.GetURI(), &equalsURI); aCurrentURI->Equals(aNewURI, &equalsURI);
} }
return !equalsURI; return !equalsURI;
} }

Просмотреть файл

@ -908,6 +908,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
static bool ShouldAddEntryForRefresh(nsIURI* aCurrentURI, static bool ShouldAddEntryForRefresh(nsIURI* aCurrentURI,
const SessionHistoryInfo& aInfo); const SessionHistoryInfo& aInfo);
static bool ShouldAddEntryForRefresh(nsIURI* aCurrentURI, nsIURI* aNewURI,
bool aHasPostData);
private: private:
void Attach(bool aFromIPC, ContentParent* aOriginProcess); void Attach(bool aFromIPC, ContentParent* aOriginProcess);

Просмотреть файл

@ -535,6 +535,11 @@ CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
return nullptr; return nullptr;
} }
Unused << SetHistoryEntryCount(entry->BCHistoryLength()); Unused << SetHistoryEntryCount(entry->BCHistoryLength());
} else if (aLoadState->LoadType() == LOAD_REFRESH &&
!ShouldAddEntryForRefresh(aLoadState->URI(),
aLoadState->PostDataStream()) &&
mActiveEntry) {
entry = mActiveEntry;
} else { } else {
entry = new SessionHistoryEntry(aLoadState, aChannel); entry = new SessionHistoryEntry(aLoadState, aChannel);
if (IsTop()) { if (IsTop()) {

Просмотреть файл

@ -464,9 +464,13 @@ class CanonicalBrowsingContext final : public BrowsingContext {
void RemovePendingDiscard(); void RemovePendingDiscard();
bool ShouldAddEntryForRefresh(const SessionHistoryEntry* aEntry) { bool ShouldAddEntryForRefresh(const SessionHistoryEntry* aEntry) {
return ShouldAddEntryForRefresh(aEntry->Info().GetURI(),
aEntry->Info().GetPostData());
}
bool ShouldAddEntryForRefresh(nsIURI* aNewURI, bool aHasPostData) {
nsCOMPtr<nsIURI> currentURI = GetCurrentURI(); nsCOMPtr<nsIURI> currentURI = GetCurrentURI();
return BrowsingContext::ShouldAddEntryForRefresh(currentURI, return BrowsingContext::ShouldAddEntryForRefresh(currentURI, aNewURI,
aEntry->Info()); aHasPostData);
} }
// XXX(farre): Store a ContentParent pointer here rather than mProcessId? // XXX(farre): Store a ContentParent pointer here rather than mProcessId?

Просмотреть файл

@ -13,10 +13,10 @@ function handleRequest(request, response) {
// index == 0 First load, returns first meta refresh // index == 0 First load, returns first meta refresh
// index == 1 Second load, caused by first meta refresh, returns second meta refresh // index == 1 Second load, caused by first meta refresh, returns second meta refresh
// index == 2 Third load, caused by second meta refresh, doesn't return a meta refresh // index == 2 Third load, caused by second meta refresh, doesn't return a meta refresh
let query = new URLSearchParams(request.queryString);
if (index < 2) { if (index < 2) {
let query = new URLSearchParams(request.queryString);
refresh = query.get("seconds"); refresh = query.get("seconds");
if (query.get("crossorigin") == "true") { if (query.get("crossOrigin") == "true") {
const hosts = ["example.org", "example.com"]; const hosts = ["example.org", "example.com"];
let url = `${request.scheme}://${hosts[index]}${request.path}?${request.queryString}`; let url = `${request.scheme}://${hosts[index]}${request.path}?${request.queryString}`;
@ -24,6 +24,10 @@ function handleRequest(request, response) {
} }
refresh = `<meta http-equiv="Refresh" content="${refresh}">`; refresh = `<meta http-equiv="Refresh" content="${refresh}">`;
} }
// We want to scroll for the first load, and check that the meta refreshes keep the same
// scroll position.
let scroll = index == 0 ? `scrollTo(0, ${query.get("scrollTo")});` : "";
setState("index", String(index + 1)); setState("index", String(index + 1));
response.write( response.write(
@ -35,10 +39,12 @@ function handleRequest(request, response) {
${refresh} ${refresh}
<script> <script>
window.addEventListener("pageshow", () => { window.addEventListener("pageshow", () => {
${scroll}
window.top.opener.postMessage({ window.top.opener.postMessage({
commandType: "pageShow", commandType: "pageShow",
commandData: { commandData: {
inputValue: document.getElementById("input").value, inputValue: document.getElementById("input").value,
scrollPosition: window.scrollY,
}, },
}, "*"); }, "*");
}); });
@ -62,6 +68,9 @@ function handleRequest(request, response) {
</head> </head>
<body> <body>
<input type="text" id="input" value="initial"></input> <input type="text" id="input" value="initial"></input>
<div style='height: 9000px;'></div>
<p>
</p>
</body> </body>
</html>` </html>`
); );

Просмотреть файл

@ -16,8 +16,32 @@
const SJS = new URL("file_bug1742865.sjs", location.href); const SJS = new URL("file_bug1742865.sjs", location.href);
const SJS_OUTER = new URL("file_bug1742865_outer.sjs", location.href); const SJS_OUTER = new URL("file_bug1742865_outer.sjs", location.href);
const SCROLL = 500;
let tolerance;
function setup() {
return SpecialPowers.spawn(window.top, [], () => {
return SpecialPowers.getDOMWindowUtils(content.window).getResolution();
}).then(resolution => {
// Allow a half pixel difference if the top document's resolution is lower
// than 1.0 because the scroll position is aligned with screen pixels
// instead of CSS pixels.
tolerance = resolution < 1.0 ? 0.5 : 0.0;
});
}
function checkScrollPosition(scrollPosition, shouldKeepScrollPosition) {
isfuzzy(scrollPosition, shouldKeepScrollPosition ? SCROLL : 0, tolerance,
`Scroll position ${shouldKeepScrollPosition ? "should" : "shouldn't"} be maintained for meta refresh`);
}
function openWindowAndCheckRefresh(url, params, shouldAddToHistory, shouldKeepScrollPosition) {
info(`Running test for ${JSON.stringify(params)}`);
url = new URL(url);
Object.entries(params).forEach(([k, v]) => { url.searchParams.append(k, v) });
url.searchParams.append("scrollTo", SCROLL);
function openWindowAndCheckRefresh(url, shouldAddToHistory) {
let resetURL = new URL(SJS); let resetURL = new URL(SJS);
resetURL.search = "?reset"; resetURL.search = "?reset";
return fetch(resetURL).then(() => { return fetch(resetURL).then(() => {
@ -41,14 +65,18 @@
is(commandType, "pageShow", "Unknown command type"); is(commandType, "pageShow", "Unknown command type");
let { inputValue } = commandData; let { inputValue, scrollPosition } = commandData;
switch (++count) { switch (++count) {
// file_bug1742865.sjs causes 3 loads: // file_bug1742865.sjs causes 3 loads:
// * first load, returns first meta refresh // * first load, returns first meta refresh
// * second load, caused by first meta refresh, returns second meta refresh // * second load, caused by first meta refresh, returns second meta refresh
// * third load, caused by second meta refresh, doesn't return a meta refresh // * third load, caused by second meta refresh, doesn't return a meta refresh
case 2:
checkScrollPosition(scrollPosition, shouldKeepScrollPosition);
break;
case 3: case 3:
checkScrollPosition(scrollPosition, shouldKeepScrollPosition);
win.postMessage("changeInputValue", "*"); win.postMessage("changeInputValue", "*");
break; break;
case 4: case 4:
@ -56,6 +84,7 @@
break; break;
case 5: case 5:
is(inputValue, "1234", "Entries for auto-refresh should be attached to session history"); is(inputValue, "1234", "Entries for auto-refresh should be attached to session history");
checkScrollPosition(scrollPosition, shouldKeepScrollPosition);
removeEventListener("message", listener); removeEventListener("message", listener);
win.close(); win.close();
resolve(); resolve();
@ -67,36 +96,35 @@
}); });
} }
function doTest(seconds, crossOrigin, shouldAddToHistory) { function doTest(seconds, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition) {
let url = new URL(SJS); let params = {
url.searchParams.append("seconds", seconds); seconds,
url.searchParams.append("crossorigin", crossOrigin); crossOrigin,
};
let urlOuter = new URL(SJS_OUTER); return openWindowAndCheckRefresh(SJS, params, shouldAddToHistory, shouldKeepScrollPosition).then(() =>
urlOuter.searchParams.append("seconds", seconds); openWindowAndCheckRefresh(SJS_OUTER, params, shouldAddToHistory, shouldKeepScrollPosition)
urlOuter.searchParams.append("crossorigin", crossOrigin);
return openWindowAndCheckRefresh(url, shouldAddToHistory).then(() =>
openWindowAndCheckRefresh(urlOuter, shouldAddToHistory)
); );
} }
function runTest() { async function runTest() {
const FAST = Math.min(1, REFRESH_REDIRECT_TIMER); const FAST = Math.min(1, REFRESH_REDIRECT_TIMER);
const SLOW = REFRESH_REDIRECT_TIMER + 1; const SLOW = REFRESH_REDIRECT_TIMER + 1;
let tests = [ let tests = [
// [ time, crossOrigin, shouldAddToHistory ] // [ time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition ]
[ FAST, false, false ], [ FAST, false, false, true ],
[ FAST, true, false ], [ FAST, true, false, false ],
[ SLOW, false, false ], [ SLOW, false, false, true ],
[ SLOW, true, true ], [ SLOW, true, true, false ],
]; ];
let test = Promise.resolve(); await setup();
for (let [ time, crossOrigin, shouldAddToHistory ] of tests) {
test = test.then(() => doTest(time, crossOrigin, shouldAddToHistory)); for (let [ time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition ] of tests) {
await doTest(time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition);
} }
test.then(() => SimpleTest.finish());
SimpleTest.finish();
} }
</script> </script>
</head> </head>