forked from mirrors/gecko-dev
		
	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:
		
							parent
							
								
									4755c564c2
								
							
						
					
					
						commit
						f1ed7fa59e
					
				
					 6 changed files with 84 additions and 28 deletions
				
			
		|  | @ -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> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Peter Van der Beken
						Peter Van der Beken