blob: d72a3ad601d62818d5a2d0ead46f646c21c04c7d [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include <memory>
#include "base/bind.h"
#include "base/i18n/rtl.h"
#include "base/time/time.h"
#include "components/security_interstitials/content/security_interstitial_controller_client.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "content/public/browser/certificate_request_result_type.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_renderer_host.h"
#include "net/base/net_errors.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "url/gurl.h"
namespace security_interstitials {
const char kTestSslMetricsName[] = "test_blocking_page";
std::unique_ptr<security_interstitials::MetricsHelper> CreateTestMetricsHelper(
content::WebContents* web_contents) {
MetricsHelper::ReportDetails report_details;
report_details.metric_prefix = kTestSslMetricsName;
return std::make_unique<security_interstitials::MetricsHelper>(
GURL(), report_details, nullptr);
}
class TestInterstitialPage : public SecurityInterstitialPage {
public:
// |*destroyed_tracker| is set to true in the destructor.
TestInterstitialPage(content::WebContents* web_contents,
const GURL& request_url,
bool* destroyed_tracker)
: SecurityInterstitialPage(
web_contents,
request_url,
std::make_unique<SecurityInterstitialControllerClient>(
web_contents,
CreateTestMetricsHelper(web_contents),
nullptr,
base::i18n::GetConfiguredLocale(),
GURL())),
destroyed_tracker_(destroyed_tracker) {}
~TestInterstitialPage() override { *destroyed_tracker_ = true; }
void OnInterstitialClosing() override {}
protected:
bool ShouldCreateNewNavigation() const override { return false; }
void PopulateInterstitialStrings(
base::DictionaryValue* load_time_data) override {}
private:
bool* destroyed_tracker_;
};
class SecurityInterstitialTabHelperTest
: public content::RenderViewHostTestHarness {
protected:
std::unique_ptr<content::NavigationHandle> CreateHandle(
bool committed,
bool is_same_document) {
std::unique_ptr<content::MockNavigationHandle> handle =
std::make_unique<content::MockNavigationHandle>(GURL(), main_rfh());
handle->set_has_committed(committed);
handle->set_is_same_document(is_same_document);
return handle;
}
// The lifetime of the blocking page is managed by the
// SecurityInterstitialTabHelper for the test's web_contents.
// |destroyed_tracker| will be set to true when the corresponding blocking
// page is destroyed.
void CreateAssociatedBlockingPage(content::NavigationHandle* handle,
bool* destroyed_tracker) {
SecurityInterstitialTabHelper::AssociateBlockingPage(
web_contents(), handle->GetNavigationId(),
std::make_unique<TestInterstitialPage>(web_contents(), GURL(),
destroyed_tracker));
}
};
// Tests that the helper properly handles the lifetime of a single blocking
// page, interleaved with other navigations.
TEST_F(SecurityInterstitialTabHelperTest, SingleBlockingPage) {
std::unique_ptr<content::NavigationHandle> blocking_page_handle =
CreateHandle(true, false);
bool blocking_page_destroyed = false;
CreateAssociatedBlockingPage(blocking_page_handle.get(),
&blocking_page_destroyed);
SecurityInterstitialTabHelper* helper =
SecurityInterstitialTabHelper::FromWebContents(web_contents());
// Test that a same-document navigation doesn't destroy the blocking page if
// its navigation hasn't committed yet.
std::unique_ptr<content::NavigationHandle> same_document_handle =
CreateHandle(true, true);
helper->DidFinishNavigation(same_document_handle.get());
EXPECT_FALSE(blocking_page_destroyed);
// Test that a committed (non-same-document) navigation doesn't destroy the
// blocking page if its navigation hasn't committed yet.
std::unique_ptr<content::NavigationHandle> committed_handle1 =
CreateHandle(true, false);
helper->DidFinishNavigation(committed_handle1.get());
EXPECT_FALSE(blocking_page_destroyed);
// Simulate comitting the interstitial.
helper->DidFinishNavigation(blocking_page_handle.get());
EXPECT_FALSE(blocking_page_destroyed);
// Test that a subsequent committed navigation releases the blocking page
// stored for the currently committed navigation.
std::unique_ptr<content::NavigationHandle> committed_handle2 =
CreateHandle(true, false);
helper->DidFinishNavigation(committed_handle2.get());
EXPECT_TRUE(blocking_page_destroyed);
}
// Tests that the helper properly handles the lifetime of multiple blocking
// pages, committed in a different order than they are created.
TEST_F(SecurityInterstitialTabHelperTest, MultipleBlockingPages) {
// Simulate associating the first interstitial.
std::unique_ptr<content::NavigationHandle> handle1 =
CreateHandle(true, false);
bool blocking_page1_destroyed = false;
CreateAssociatedBlockingPage(handle1.get(), &blocking_page1_destroyed);
// We can directly retrieve the helper for testing once
// CreateAssociatedBlockingPage() was called.
SecurityInterstitialTabHelper* helper =
SecurityInterstitialTabHelper::FromWebContents(web_contents());
// Simulate commiting the first interstitial.
helper->DidFinishNavigation(handle1.get());
EXPECT_FALSE(blocking_page1_destroyed);
// Associate the second interstitial.
std::unique_ptr<content::NavigationHandle> handle2 =
CreateHandle(true, false);
bool blocking_page2_destroyed = false;
CreateAssociatedBlockingPage(handle2.get(), &blocking_page2_destroyed);
EXPECT_FALSE(blocking_page1_destroyed);
EXPECT_FALSE(blocking_page2_destroyed);
// Associate the third interstitial.
std::unique_ptr<content::NavigationHandle> handle3 =
CreateHandle(true, false);
bool blocking_page3_destroyed = false;
CreateAssociatedBlockingPage(handle3.get(), &blocking_page3_destroyed);
EXPECT_FALSE(blocking_page1_destroyed);
EXPECT_FALSE(blocking_page2_destroyed);
EXPECT_FALSE(blocking_page3_destroyed);
// Simulate commiting the third interstitial.
helper->DidFinishNavigation(handle3.get());
EXPECT_TRUE(blocking_page1_destroyed);
EXPECT_FALSE(blocking_page2_destroyed);
EXPECT_FALSE(blocking_page3_destroyed);
// Simulate commiting the second interstitial.
helper->DidFinishNavigation(handle2.get());
EXPECT_TRUE(blocking_page1_destroyed);
EXPECT_FALSE(blocking_page2_destroyed);
EXPECT_TRUE(blocking_page3_destroyed);
// Test that a subsequent committed navigation releases the last blocking
// page.
std::unique_ptr<content::NavigationHandle> committed_handle4 =
CreateHandle(true, false);
helper->DidFinishNavigation(committed_handle4.get());
EXPECT_TRUE(blocking_page2_destroyed);
}
// Tests that the helper properly handles a navigation that finishes without
// committing.
TEST_F(SecurityInterstitialTabHelperTest, NavigationDoesNotCommit) {
std::unique_ptr<content::NavigationHandle> committed_handle =
CreateHandle(true, false);
bool committed_blocking_page_destroyed = false;
CreateAssociatedBlockingPage(committed_handle.get(),
&committed_blocking_page_destroyed);
SecurityInterstitialTabHelper* helper =
SecurityInterstitialTabHelper::FromWebContents(web_contents());
helper->DidFinishNavigation(committed_handle.get());
EXPECT_FALSE(committed_blocking_page_destroyed);
// Simulate a navigation that does not commit.
std::unique_ptr<content::NavigationHandle> non_committed_handle =
CreateHandle(false, false);
bool non_committed_blocking_page_destroyed = false;
CreateAssociatedBlockingPage(non_committed_handle.get(),
&non_committed_blocking_page_destroyed);
helper->DidFinishNavigation(non_committed_handle.get());
// The blocking page for the non-committed navigation should have been cleaned
// up, but the one for the previous committed navigation should still be
// around.
EXPECT_TRUE(non_committed_blocking_page_destroyed);
EXPECT_FALSE(committed_blocking_page_destroyed);
// When a navigation does commit, the previous one should be cleaned up.
std::unique_ptr<content::NavigationHandle> next_committed_handle =
CreateHandle(true, false);
helper->DidFinishNavigation(next_committed_handle.get());
EXPECT_TRUE(committed_blocking_page_destroyed);
}
} // namespace security_interstitials