Commit 8e3141ae authored by Justin DeWitt's avatar Justin DeWitt Committed by Commit Bot

[Offline Pages] Allow same-page snapshots for POST requests.

Previously we disabled POST requests from being snapshotted by OPC.
However, this inadvertently prevented SnapshotController from marking
a WebContents as snapshottable, so any POST requests would be unable to
complete - they would hang in downloading state until Chrome exited.
After that, the background loader would take over and error out because
it does not support non-GET requests and would use a GET HTTP request
for a POST resource which typically fails or redirects to a login page
of some kind.

Additionally, extends NavigationSimulator to use the method from
SetMethod when committing navigations, rather than the currently-hardcoded
"GET" method.

Bug: 859403
Change-Id: Ic10dd0b088da9e3ebf2a42e3cca6096eea449d16
Reviewed-on: https://chromium-review.googlesource.com/1125031Reviewed-by: default avatarCamille Lamy <clamy@chromium.org>
Reviewed-by: default avatarCarlos Knippschild <carlosk@chromium.org>
Commit-Queue: Justin DeWitt <dewittj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#573516}
parent 1b9bda41
...@@ -243,20 +243,18 @@ void RecentTabHelper::DidFinishNavigation( ...@@ -243,20 +243,18 @@ void RecentTabHelper::DidFinishNavigation(
// Always reset so that posted tasks get canceled. // Always reset so that posted tasks get canceled.
snapshot_controller_->Reset(); snapshot_controller_->Reset();
// Check for conditions that should stop last_n from creating snapshots of // Check for conditions that should stop us from creating snapshots of
// this page: // this page:
// - It is an error page. // - It is an error page.
// - The navigation is a POST as offline pages are never loaded for them.
// - The navigated URL is not supported. // - The navigated URL is not supported.
// - The page being loaded is already an offline page. // - The page being loaded is already an offline page.
bool can_save = bool can_save =
!navigation_handle->IsErrorPage() && !navigation_handle->IsPost() && !navigation_handle->IsErrorPage() &&
OfflinePageModel::CanSaveURL(web_contents()->GetLastCommittedURL()) && OfflinePageModel::CanSaveURL(web_contents()->GetLastCommittedURL()) &&
current_offline_page == nullptr; current_offline_page == nullptr;
DVLOG_IF(1, !can_save) DVLOG_IF(1, !can_save)
<< " - Page can not be saved for offline usage (reasons: " << " - Page can not be saved for offline usage (reasons: "
<< !navigation_handle->IsErrorPage() << ", " << !navigation_handle->IsErrorPage() << ", "
<< !navigation_handle->IsPost() << ", "
<< OfflinePageModel::CanSaveURL(web_contents()->GetLastCommittedURL()) << OfflinePageModel::CanSaveURL(web_contents()->GetLastCommittedURL())
<< ", " << (current_offline_page == nullptr) << ")"; << ", " << (current_offline_page == nullptr) << ")";
...@@ -264,7 +262,12 @@ void RecentTabHelper::DidFinishNavigation( ...@@ -264,7 +262,12 @@ void RecentTabHelper::DidFinishNavigation(
if (!can_save) if (!can_save)
snapshot_controller_->Stop(); snapshot_controller_->Stop();
// Last N should be disabled when:
// - Running on low end devices.
// - Viewing POST content for privacy considerations.
// - Disabled by flag.
last_n_listen_to_tab_hidden_ = can_save && !delegate_->IsLowEndDevice() && last_n_listen_to_tab_hidden_ = can_save && !delegate_->IsLowEndDevice() &&
!navigation_handle->IsPost() &&
IsOffliningRecentPagesEnabled(); IsOffliningRecentPagesEnabled();
DVLOG_IF(1, can_save && !last_n_listen_to_tab_hidden_) DVLOG_IF(1, can_save && !last_n_listen_to_tab_hidden_)
<< " - Page can not be saved by last_n"; << " - Page can not be saved by last_n";
......
...@@ -105,6 +105,9 @@ class RecentTabHelperTest ...@@ -105,6 +105,9 @@ class RecentTabHelperTest
// navigation. // navigation.
void NavigateAndCommitTyped(const GURL& url); void NavigateAndCommitTyped(const GURL& url);
// Navigates to the URL and commit as if a form had been submitted.
void NavigateAndCommitPost(const GURL& url);
ClientId NewDownloadClientId(); ClientId NewDownloadClientId();
RecentTabHelper* recent_tab_helper() const { return recent_tab_helper_; } RecentTabHelper* recent_tab_helper() const { return recent_tab_helper_; }
...@@ -272,6 +275,15 @@ void RecentTabHelperTest::NavigateAndCommitTyped(const GURL& url) { ...@@ -272,6 +275,15 @@ void RecentTabHelperTest::NavigateAndCommitTyped(const GURL& url) {
simulator->Commit(); simulator->Commit();
} }
void RecentTabHelperTest::NavigateAndCommitPost(const GURL& url) {
auto simulator =
content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
simulator->SetMethod("POST");
simulator->SetTransition(ui::PAGE_TRANSITION_FORM_SUBMIT);
simulator->Start();
simulator->Commit();
}
ClientId RecentTabHelperTest::NewDownloadClientId() { ClientId RecentTabHelperTest::NewDownloadClientId() {
static int counter = 0; static int counter = 0;
return ClientId(kDownloadNamespace, return ClientId(kDownloadNamespace,
...@@ -1089,4 +1101,33 @@ TEST_F(RecentTabHelperTest, NoSaveIfTabIsClosing) { ...@@ -1089,4 +1101,33 @@ TEST_F(RecentTabHelperTest, NoSaveIfTabIsClosing) {
IsSavingSamePageEnum::kNewPage, 1); IsSavingSamePageEnum::kNewPage, 1);
} }
TEST_F(RecentTabHelperTest, NoSaveOfflinePageCacheForPost) {
// Navigate and finish loading. Nothing should be saved, but we should record
// that it is possible to save the page.
NavigateAndCommitPost(kTestPageUrl);
histogram_tester()->ExpectUniqueSample("OfflinePages.CanSaveRecentPage", true,
1);
recent_tab_helper()->DocumentOnLoadCompletedInMainFrame();
// Move the snapshot controller's time forward so it gets past timeouts.
FastForwardSnapshotController();
ASSERT_EQ(0U, GetAllPages().size());
// Tab is hidden with a fully loaded page. A snapshot save should not happen
// due to the POST method - OfflinePageCache is disabled.
recent_tab_helper()->OnVisibilityChanged(content::Visibility::HIDDEN);
RunUntilIdle();
EXPECT_EQ(0U, page_added_count());
ASSERT_EQ(0U, GetAllPages().size());
histogram_tester()->ExpectTotalCount("OfflinePages.LastN.IsSavingSamePage",
0);
// A manual download should succeed despite being ineligible for OPC.
recent_tab_helper()->ObserveAndDownloadCurrentPage(NewDownloadClientId(),
123L, "");
RunUntilIdle();
EXPECT_EQ(1U, page_added_count());
ASSERT_EQ(1U, GetAllPages().size());
}
} // namespace offline_pages } // namespace offline_pages
...@@ -512,7 +512,7 @@ void NavigationSimulator::Commit() { ...@@ -512,7 +512,7 @@ void NavigationSimulator::Commit() {
params.gesture = params.gesture =
has_user_gesture_ ? NavigationGestureUser : NavigationGestureAuto; has_user_gesture_ ? NavigationGestureUser : NavigationGestureAuto;
params.contents_mime_type = contents_mime_type_; params.contents_mime_type = contents_mime_type_;
params.method = "GET"; params.method = handle_->IsPost() ? "POST" : "GET";
params.http_status_code = 200; params.http_status_code = 200;
params.history_list_was_cleared = false; params.history_list_was_cleared = false;
params.original_request_url = navigation_url_; params.original_request_url = navigation_url_;
......
...@@ -134,6 +134,36 @@ class CancellingNavigationSimulatorTest ...@@ -134,6 +134,36 @@ class CancellingNavigationSimulatorTest
DISALLOW_COPY_AND_ASSIGN(CancellingNavigationSimulatorTest); DISALLOW_COPY_AND_ASSIGN(CancellingNavigationSimulatorTest);
}; };
class MethodCheckingNavigationSimulatorTest : public NavigationSimulatorTest,
public WebContentsObserver {
public:
MethodCheckingNavigationSimulatorTest() = default;
~MethodCheckingNavigationSimulatorTest() override = default;
void SetUp() override {
RenderViewHostImplTestHarness::SetUp();
contents()->GetMainFrame()->InitializeRenderFrameIfNeeded();
Observe(RenderViewHostImplTestHarness::web_contents());
}
void DidFinishNavigation(content::NavigationHandle* handle) override {
did_finish_navigation_ = true;
is_post_ = handle->IsPost();
}
bool did_finish_navigation() { return did_finish_navigation_; }
bool is_post() { return is_post_; }
private:
// set upon DidFinishNavigation.
bool did_finish_navigation_ = false;
// Not valid until |did_finish_navigation_| is true;
bool is_post_ = false;
DISALLOW_COPY_AND_ASSIGN(MethodCheckingNavigationSimulatorTest);
};
TEST_F(NavigationSimulatorTest, AutoAdvanceOff) { TEST_F(NavigationSimulatorTest, AutoAdvanceOff) {
std::unique_ptr<NavigationSimulator> simulator = std::unique_ptr<NavigationSimulator> simulator =
NavigationSimulator::CreateRendererInitiated( NavigationSimulator::CreateRendererInitiated(
...@@ -166,6 +196,32 @@ TEST_F(NavigationSimulatorTest, AutoAdvanceOff) { ...@@ -166,6 +196,32 @@ TEST_F(NavigationSimulatorTest, AutoAdvanceOff) {
simulator->Commit(); simulator->Commit();
} }
TEST_F(MethodCheckingNavigationSimulatorTest, SetMethodPostWithRedirect) {
std::unique_ptr<NavigationSimulator> simulator =
NavigationSimulator::CreateRendererInitiated(
GURL("https://example.test/"), main_rfh());
simulator->SetMethod("POST");
simulator->Start();
simulator->Redirect(GURL("https://example.test/2.html"));
simulator->Commit();
ASSERT_TRUE(did_finish_navigation());
EXPECT_FALSE(is_post())
<< "If a POST request redirects, it should convert to a GET";
}
TEST_F(MethodCheckingNavigationSimulatorTest, SetMethodPost) {
std::unique_ptr<NavigationSimulator> simulator =
NavigationSimulator::CreateRendererInitiated(
GURL("https://example.test/"), main_rfh());
simulator->SetMethod("POST");
simulator->Start();
simulator->Commit();
ASSERT_TRUE(did_finish_navigation());
EXPECT_TRUE(is_post());
}
// Stress test the navigation simulator by having a navigation throttle cancel // Stress test the navigation simulator by having a navigation throttle cancel
// the navigation at various points in the flow, both synchronously and // the navigation at various points in the flow, both synchronously and
// asynchronously. // asynchronously.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment