diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc index 82ebca152d4457..4a5d1df98606d1 100644 --- a/content/browser/navigation_browsertest.cc +++ b/content/browser/navigation_browsertest.cc @@ -2022,10 +2022,15 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AboutSrcDocUsesBeginNavigation) { interceptor.Wait(1); // DidCommitNavigation is called. } -class TextFragmentAnchorBrowserTest : public NavigationBrowserTest { +class TextFragmentAnchorBrowserTest : public NavigationBaseBrowserTest { + public: + TextFragmentAnchorBrowserTest() { + feature_list_.InitAndEnableFeature(features::kDocumentPolicy); + } + protected: void SetUpCommandLine(base::CommandLine* command_line) override { - NavigationBrowserTest::SetUpCommandLine(command_line); + NavigationBaseBrowserTest::SetUpCommandLine(command_line); command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, "TextFragmentIdentifiers"); @@ -2057,9 +2062,17 @@ class TextFragmentAnchorBrowserTest : public NavigationBrowserTest { EXPECT_TRUE(WaitForLoadStop(contents)); EXPECT_TRUE(WaitForRenderFrameReady(contents->GetMainFrame())); } + + RenderWidgetHostImpl* GetWidgetHost() { + return RenderWidgetHostImpl::From( + shell()->web_contents()->GetRenderViewHost()->GetWidget()); + } + + base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledOnUserNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/target_text_link.html")); GURL target_text_url(embedded_test_server()->GetURL( "/scrollable_page_with_content.html#:~:text=text")); @@ -2070,11 +2083,8 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledOnUserNavigation) { TestNavigationObserver observer(main_contents); RenderFrameSubmissionObserver frame_observer(main_contents); - RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( - main_contents->GetRenderViewHost()->GetWidget()); - // We need to wait until hit test data is available. - HitTestRegionObserver hittest_observer(host->GetFrameSinkId()); + HitTestRegionObserver hittest_observer(GetWidgetHost()->GetFrameSinkId()); hittest_observer.WaitForHitTestData(); ClickElementWithId(main_contents, "link"); @@ -2082,12 +2092,14 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledOnUserNavigation) { EXPECT_EQ(target_text_url, main_contents->GetLastCommittedURL()); WaitForPageLoad(main_contents); - frame_observer.WaitForScrollOffsetAtTop(false); + frame_observer.WaitForScrollOffsetAtTop( + /*expected_scroll_offset_at_top=*/false); EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledOnBrowserNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL( "/scrollable_page_with_content.html#:~:text=text")); WebContents* main_contents = shell()->web_contents(); @@ -2096,12 +2108,14 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EXPECT_TRUE(NavigateToURL(shell(), url)); WaitForPageLoad(main_contents); - frame_observer.WaitForScrollOffsetAtTop(false); + frame_observer.WaitForScrollOffsetAtTop( + /*expected_scroll_offset_at_top=*/false); EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledOnUserGestureScriptNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/empty.html")); GURL target_text_url(embedded_test_server()->GetURL( "/scrollable_page_with_content.html#:~:text=text")); @@ -2119,12 +2133,14 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EXPECT_EQ(target_text_url, main_contents->GetLastCommittedURL()); WaitForPageLoad(main_contents); - frame_observer.WaitForScrollOffsetAtTop(false); + frame_observer.WaitForScrollOffsetAtTop( + /*expected_scroll_offset_at_top=*/false); EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, DisabledOnScriptNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/empty.html")); GURL target_text_url(embedded_test_server()->GetURL( "/scrollable_page_with_content.html#:~:text=text")); @@ -2145,11 +2161,13 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout()); run_loop.Run(); + RunUntilInputProcessed(GetWidgetHost()); EXPECT_TRUE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, DisabledOnScriptHistoryNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL target_text_url(embedded_test_server()->GetURL( "/scrollable_page_with_content.html#:~:text=text")); GURL url(embedded_test_server()->GetURL("/empty.html")); @@ -2179,11 +2197,13 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout()); run_loop.Run(); + RunUntilInputProcessed(GetWidgetHost()); EXPECT_TRUE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledOnSameDocumentBrowserNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL( "/scrollable_page_with_content.html#:~:text=text")); WebContents* main_contents = shell()->web_contents(); @@ -2205,12 +2225,14 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EXPECT_TRUE(NavigateToURL(shell(), same_doc_url)); WaitForPageLoad(main_contents); - frame_observer.WaitForScrollOffsetAtTop(false); + frame_observer.WaitForScrollOffsetAtTop( + /*expected_scroll_offset_at_top=*/false); EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, DisabledOnSameDocumentScriptNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); GURL url( embedded_test_server()->GetURL("/scrollable_page_with_content.html")); GURL target_text_url(embedded_test_server()->GetURL( @@ -2232,6 +2254,85 @@ IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout()); run_loop.Run(); + RunUntilInputProcessed(GetWidgetHost()); + EXPECT_TRUE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); +} + +IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, EnabledByDocumentPolicy) { + net::test_server::ControllableHttpResponse response(embedded_test_server(), + "/target.html"); + + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/target.html#:~:text=text")); + WebContents* main_contents = shell()->web_contents(); + RenderFrameSubmissionObserver frame_observer(main_contents); + + // Load the target document + TestNavigationManager navigation_manager(main_contents, url); + shell()->LoadURL(url); + + // Start navigation + EXPECT_TRUE(navigation_manager.WaitForRequestStart()); + navigation_manager.ResumeNavigation(); + + // Send Document-Policy header + response.WaitForRequest(); + response.Send( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Document-Policy: no-force-load-at-top\r\n" + "\r\n" + "

Some text

"); + response.Done(); + + EXPECT_TRUE(navigation_manager.WaitForResponse()); + navigation_manager.ResumeNavigation(); + navigation_manager.WaitForNavigationFinished(); + + WaitForPageLoad(main_contents); + frame_observer.WaitForScrollOffsetAtTop( + /*expected_scroll_offset_at_top=*/false); + EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); +} + +IN_PROC_BROWSER_TEST_F(TextFragmentAnchorBrowserTest, + DisabledByDocumentPolicy) { + net::test_server::ControllableHttpResponse response(embedded_test_server(), + "/target.html"); + + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/target.html#:~:text=text")); + WebContents* main_contents = shell()->web_contents(); + + // Load the target document + TestNavigationManager navigation_manager(main_contents, url); + shell()->LoadURL(url); + + // Start navigation + EXPECT_TRUE(navigation_manager.WaitForRequestStart()); + navigation_manager.ResumeNavigation(); + + // Send Document-Policy header + response.WaitForRequest(); + response.Send( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Document-Policy: force-load-at-top\r\n" + "\r\n" + "

Some text

"); + response.Done(); + + EXPECT_TRUE(navigation_manager.WaitForResponse()); + navigation_manager.ResumeNavigation(); + navigation_manager.WaitForNavigationFinished(); + + WaitForPageLoad(main_contents); + // Wait a short amount of time to ensure the page does not scroll. + base::RunLoop run_loop; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout()); + run_loop.Run(); + RunUntilInputProcessed(GetWidgetHost()); EXPECT_TRUE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); } @@ -3259,4 +3360,205 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, EXPECT_EQ(2, rewrite_count); } +class DocumentPolicyBrowserTest : public NavigationBaseBrowserTest { + public: + DocumentPolicyBrowserTest() { + feature_list_.InitAndEnableFeature(features::kDocumentPolicy); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Test that scroll restoration can be disabled with +// Document-Policy: force-load-at-top +IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest, + ScrollRestorationDisabledByDocumentPolicy) { + net::test_server::ControllableHttpResponse response(embedded_test_server(), + "/target.html"); + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/target.html")); + WebContents* main_contents = shell()->web_contents(); + RenderFrameSubmissionObserver frame_observer(main_contents); + TestNavigationManager navigation_manager(main_contents, url); + + // Load the document with document policy force-load-at-top + shell()->LoadURL(url); + EXPECT_TRUE(navigation_manager.WaitForRequestStart()); + navigation_manager.ResumeNavigation(); + response.WaitForRequest(); + response.Send( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Document-Policy: force-load-at-top\r\n" + "\r\n" + "

Some text

"); + response.Done(); + + EXPECT_TRUE(navigation_manager.WaitForResponse()); + navigation_manager.ResumeNavigation(); + navigation_manager.WaitForNavigationFinished(); + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + + // Scroll down the page a bit + EXPECT_TRUE(ExecuteScript(main_contents, "window.scrollTo(0, 1000)")); + frame_observer.WaitForScrollOffsetAtTop(false); + + // Navigate away + EXPECT_TRUE(ExecuteScript(main_contents, "window.location = 'about:blank'")); + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + + // Navigate back + EXPECT_TRUE(ExecuteScript(main_contents, "history.back()")); + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + + // Wait a short amount of time to ensure the page does not scroll. + base::RunLoop run_loop; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout()); + run_loop.Run(); + RunUntilInputProcessed(RenderWidgetHostImpl::From( + main_contents->GetRenderViewHost()->GetWidget())); + EXPECT_TRUE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); +} + +// Test that scroll restoration works as expected with +// Document-Policy: no-force-load-at-top +IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest, + ScrollRestorationEnabledByDocumentPolicy) { + net::test_server::ControllableHttpResponse response(embedded_test_server(), + "/target.html"); + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/target.html")); + WebContents* main_contents = shell()->web_contents(); + RenderFrameSubmissionObserver frame_observer(main_contents); + TestNavigationManager navigation_manager(main_contents, url); + + // Load the document with document policy no-force-load-at-top + shell()->LoadURL(url); + EXPECT_TRUE(navigation_manager.WaitForRequestStart()); + navigation_manager.ResumeNavigation(); + response.WaitForRequest(); + response.Send( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Document-Policy: no-force-load-at-top\r\n" + "\r\n" + "

Some text

"); + response.Done(); + + EXPECT_TRUE(navigation_manager.WaitForResponse()); + navigation_manager.ResumeNavigation(); + navigation_manager.WaitForNavigationFinished(); + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + + // Scroll down the page a bit + EXPECT_TRUE(ExecuteScript(main_contents, "window.scrollTo(0, 1000)")); + frame_observer.WaitForScrollOffsetAtTop(false); + + // Navigate away + EXPECT_TRUE(ExecuteScript(main_contents, "window.location = 'about:blank'")); + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + + // Navigate back + EXPECT_TRUE(ExecuteScript(main_contents, "history.back()")); + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + + // Ensure scroll restoration activated + frame_observer.WaitForScrollOffsetAtTop(false); + EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); +} + +// Test that element fragment anchor scrolling can be disabled with +// Document-Policy: force-load-at-top +IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest, + FragmentAnchorDisabledByDocumentPolicy) { + net::test_server::ControllableHttpResponse response(embedded_test_server(), + "/target.html"); + + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/target.html#text")); + WebContents* main_contents = shell()->web_contents(); + + // Load the target document + TestNavigationManager navigation_manager(main_contents, url); + shell()->LoadURL(url); + + // Start navigation + EXPECT_TRUE(navigation_manager.WaitForRequestStart()); + navigation_manager.ResumeNavigation(); + + // Send Document-Policy header + response.WaitForRequest(); + response.Send( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Document-Policy: force-load-at-top\r\n" + "\r\n" + "

Some text

"); + response.Done(); + + EXPECT_TRUE(navigation_manager.WaitForResponse()); + navigation_manager.ResumeNavigation(); + navigation_manager.WaitForNavigationFinished(); + + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + // Wait a short amount of time to ensure the page does not scroll. + base::RunLoop run_loop; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout()); + run_loop.Run(); + RunUntilInputProcessed(RenderWidgetHostImpl::From( + main_contents->GetRenderViewHost()->GetWidget())); + EXPECT_TRUE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); +} + +// Test that element fragment anchor scrolling works as expected with +// Document-Policy: no-force-load-at-top +IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest, + FragmentAnchorEnabledByDocumentPolicy) { + net::test_server::ControllableHttpResponse response(embedded_test_server(), + "/target.html"); + + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/target.html#text")); + WebContents* main_contents = shell()->web_contents(); + RenderFrameSubmissionObserver frame_observer(main_contents); + + // Load the target document + TestNavigationManager navigation_manager(main_contents, url); + shell()->LoadURL(url); + + // Start navigation + EXPECT_TRUE(navigation_manager.WaitForRequestStart()); + navigation_manager.ResumeNavigation(); + + // Send Document-Policy header + response.WaitForRequest(); + response.Send( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Document-Policy: no-force-load-at-top\r\n" + "\r\n" + "

Some text

"); + response.Done(); + + EXPECT_TRUE(navigation_manager.WaitForResponse()); + navigation_manager.ResumeNavigation(); + navigation_manager.WaitForNavigationFinished(); + + EXPECT_TRUE(WaitForLoadStop(main_contents)); + EXPECT_TRUE(WaitForRenderFrameReady(main_contents->GetMainFrame())); + frame_observer.WaitForScrollOffsetAtTop( + /*expected_scroll_offset_at_top=*/false); + EXPECT_FALSE(main_contents->GetMainFrame()->GetView()->IsScrollOffsetAtTop()); +} + } // namespace content diff --git a/third_party/blink/common/feature_policy/document_policy.cc b/third_party/blink/common/feature_policy/document_policy.cc index f5191e2a6e198b..8b1df0dbcfe984 100644 --- a/third_party/blink/common/feature_policy/document_policy.cc +++ b/third_party/blink/common/feature_policy/document_policy.cc @@ -156,6 +156,7 @@ bool DocumentPolicy::IsFeatureSupported( switch (feature) { case mojom::DocumentPolicyFeature::kFontDisplay: case mojom::DocumentPolicyFeature::kUnoptimizedLosslessImages: + case mojom::DocumentPolicyFeature::kForceLoadAtTop: return true; default: return false; diff --git a/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom b/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom index 3f48446e3879b1..576fd9f9266585 100644 --- a/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom +++ b/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom @@ -11,6 +11,8 @@ enum DocumentPolicyFeature { kFontDisplay = 1, // When disallowed, these policies require images to have a reasonable byte-to-pixel ratio. kUnoptimizedLosslessImages = 2, + // Controls whether the browser should allow navigations that cause the page to scroll. + kForceLoadAtTop = 3, // Don't change assigned numbers of any item, and don't reuse removed slots. // Add new features at the end of the enum. }; diff --git a/third_party/blink/renderer/core/feature_policy/document_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/document_policy_features.json5 index e415a3dc055a76..576ee3f394613c 100644 --- a/third_party/blink/renderer/core/feature_policy/document_policy_features.json5 +++ b/third_party/blink/renderer/core/feature_policy/document_policy_features.json5 @@ -10,7 +10,7 @@ // which gets parsed from the header or the policy attribute. document_policy_name: {}, value_name: {}, - // value type allowed in mojom::PolicyValueType which is defined in + // value type allowed in mojom::PolicyValueType which is defined in // third_party/blink/public/mojom/feature_policy/policy_value.mojom. value_type: {}, // valid c++ expression strings, e.g. true/false, 1.0, -1. @@ -46,5 +46,16 @@ default_value: "max", depends_on: ["UnoptimizedImagePolicies"], }, + { + // The ForceLoadAtTop policy lets pages opt-out of scrolling that + // automatically happens on page load. This includes fragment scrolls, + // text fragment scrolls (i.e. this provides an opt-out for the Scroll To + // Text feature), and scroll restoration. + name: "ForceLoadAtTop", + document_policy_name: "force-load-at-top", + value_name: "", + value_type: "Bool", + default_value: "false", + }, ], } diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 0fcd127e1c1d6c..06af4fca136c5f 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc @@ -181,6 +181,7 @@ DocumentLoader::DocumentLoader( frame_policy_ = params_->frame_policy.value_or(FramePolicy()); document_policy_ = CreateDocumentPolicy(); + // If the document is blocked by document policy, there won't be content // in the sub-frametree, thus no need to initialize required_policy for // subtree. @@ -1488,6 +1489,13 @@ void DocumentLoader::InstallNewDocument( if (!RuntimeEnabledFeatures::DocumentPolicyEnabled(owner_document)) document_policy_ = DocumentPolicy::FeatureState{}; + if (document_policy_.contains( + mojom::blink::DocumentPolicyFeature::kForceLoadAtTop)) { + navigation_scroll_allowed_ = + !(document_policy_[mojom::blink::DocumentPolicyFeature::kForceLoadAtTop] + .BoolValue()); + } + DocumentInit init = DocumentInit::Create() .WithDocumentLoader(this) diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h index f1b44c1673ca33..3c0f4bf36ea05a 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h @@ -321,6 +321,8 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected, return last_same_document_navigation_was_browser_initiated_; } + bool NavigationScrollAllowed() const { return navigation_scroll_allowed_; } + protected: Vector redirect_chain_; @@ -551,6 +553,9 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected, // Whether this load request is a result of a browser initiated same-document // navigation. bool last_same_document_navigation_was_browser_initiated_ = false; + + // Whether the document can be scrolled on load + bool navigation_scroll_allowed_ = true; }; DECLARE_WEAK_IDENTIFIER_MAP(DocumentLoader); diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index 6059cf817fc16e..1bd925035aa770 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc @@ -1183,7 +1183,8 @@ void FrameLoader::CommitDocumentLoader( void FrameLoader::RestoreScrollPositionAndViewState() { if (!frame_->GetPage() || !GetDocumentLoader() || !GetDocumentLoader()->GetHistoryItem() || - !GetDocumentLoader()->GetHistoryItem()->GetViewState()) { + !GetDocumentLoader()->GetHistoryItem()->GetViewState() || + !GetDocumentLoader()->NavigationScrollAllowed()) { return; } RestoreScrollPositionAndViewState( @@ -1307,12 +1308,15 @@ void FrameLoader::ProcessFragment(const KURL& url, // restoration type is manual, then we should not override it unless this // is a same document reload. bool should_scroll_to_fragment = - (load_start_type == kNavigationWithinSameDocument && - !IsBackForwardLoadType(frame_load_type)) || - (!GetDocumentLoader()->GetInitialScrollState().did_restore_from_history && - !(GetDocumentLoader()->GetHistoryItem() && - GetDocumentLoader()->GetHistoryItem()->ScrollRestorationType() == - kScrollRestorationManual)); + GetDocumentLoader()->NavigationScrollAllowed() && + ((load_start_type == kNavigationWithinSameDocument && + !IsBackForwardLoadType(frame_load_type)) || + (!GetDocumentLoader() + ->GetInitialScrollState() + .did_restore_from_history && + !(GetDocumentLoader()->GetHistoryItem() && + GetDocumentLoader()->GetHistoryItem()->ScrollRestorationType() == + kScrollRestorationManual))); view->ProcessUrlFragment(url, load_start_type == kNavigationWithinSameDocument,