blob: 5f66af3455ecf0a3b17dfae033897a0ec2c3f285 [file] [log] [blame]
// Copyright 2015 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/data_reduction_proxy/content/browser/data_reduction_proxy_debug_blocking_page.h"
#include <string>
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_ui_manager.h"
#include "content/public/browser/interstitial_page.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
static const char* kBypassURL = "http://www.bypass.com/";
static const char* kBypassURL2 = "http://www.bypass2.com/";
static const char* kBypassURL3 = "http://www.bypass3.com/";
static const char* kGoogleURL = "http://www.google.com/";
static const char* kOtherURL = "http://www.other.com/";
namespace data_reduction_proxy {
// A DataReductionProxyDebugBlockingPage class that does not create windows.
class TestDataReductionProxyDebugBlockingPage
: public DataReductionProxyDebugBlockingPage {
public:
TestDataReductionProxyDebugBlockingPage(
DataReductionProxyDebugUIManager* ui_manager,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
content::WebContents* web_contents,
const BypassResourceList& bypass_resources,
const std::string& app_locale)
: DataReductionProxyDebugBlockingPage(
ui_manager, io_task_runner, web_contents,
bypass_resources, app_locale) {
DontCreateViewForTesting();
}
};
class TestDataReductionProxyDebugBlockingPageFactory
: public DataReductionProxyDebugBlockingPageFactory {
public:
TestDataReductionProxyDebugBlockingPageFactory() {
}
~TestDataReductionProxyDebugBlockingPageFactory() override {
}
DataReductionProxyDebugBlockingPage* CreateDataReductionProxyPage(
DataReductionProxyDebugUIManager* ui_manager,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
content::WebContents* web_contents,
const DataReductionProxyDebugBlockingPage::BypassResourceList&
resource_list,
const std::string& app_locale) override {
return new TestDataReductionProxyDebugBlockingPage(
ui_manager, io_task_runner, web_contents, resource_list, app_locale);
}
};
// TODO(megjablon): Add tests that verify the reception of a bypass message
// triggers the interstitial.
class DataReductionProxyDebugBlockingPageTest
: public content::RenderViewHostTestHarness {
public:
// The decision the user made.
enum UserResponse {
PENDING,
OK,
CANCEL
};
DataReductionProxyDebugBlockingPageTest() : user_response_(PENDING) {
}
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
DataReductionProxyDebugBlockingPage::RegisterFactory(&factory_);
ui_manager_ = new DataReductionProxyDebugUIManager(
base::MessageLoopProxy::current(),
base::MessageLoopProxy::current(),
"en-US");
ResetUserResponse();
}
void TearDown() override {
// Release the UI manager before the BrowserThreads are destroyed.
ui_manager_ = NULL;
DataReductionProxyDebugBlockingPage::RegisterFactory(NULL);
content::RenderViewHostTestHarness::TearDown();
}
void OnBlockingPageComplete(bool proceed) {
if (proceed)
user_response_ = OK;
else
user_response_ = CANCEL;
}
void Navigate(const char* url,
int page_id,
int nav_entry_id,
bool did_create_new_entry) {
NavigateInternal(url, page_id, nav_entry_id, did_create_new_entry, false);
}
void NavigateCrossSite(const char* url,
int page_id,
int nav_entry_id,
bool did_create_new_entry) {
NavigateInternal(url, page_id, nav_entry_id, did_create_new_entry, true);
}
void NavigateInternal(const char* url,
int page_id,
int nav_entry_id,
bool did_create_new_entry,
bool is_cross_site) {
// The pending RVH should commit for cross-site navigations.
content::RenderFrameHost* render_frame_host =
is_cross_site
? content::WebContentsTester::For(web_contents())
->GetPendingMainFrame()
: web_contents()->GetMainFrame();
content::WebContentsTester::For(web_contents())
->TestDidNavigate(render_frame_host, page_id, nav_entry_id,
did_create_new_entry, GURL(url),
ui::PAGE_TRANSITION_TYPED);
}
void GoBack(bool is_cross_site) {
content::NavigationEntry* entry =
web_contents()->GetController().GetEntryAtOffset(-1);
ASSERT_TRUE(entry);
web_contents()->GetController().GoBack();
// The pending RVH should commit for cross-site navigations.
content::RenderFrameHost* render_frame_host =
is_cross_site
? content::WebContentsTester::For(web_contents())
->GetPendingMainFrame()
: web_contents()->GetMainFrame();
content::WebContentsTester::For(web_contents())
->TestDidNavigate(render_frame_host, entry->GetPageID(),
entry->GetUniqueID(), false, GURL(entry->GetURL()),
ui::PAGE_TRANSITION_TYPED);
}
void ShowInterstitial(bool is_subresource, const char* url) {
DataReductionProxyDebugUIManager::BypassResource resource;
InitResource(&resource, is_subresource, GURL(url));
DataReductionProxyDebugBlockingPage::ShowBlockingPage(
ui_manager_.get(), base::MessageLoopProxy::current(),
resource, std::string());
}
// Returns the DataReductionProxyDebugBlockingPage currently showing or NULL
// if none is showing.
DataReductionProxyDebugBlockingPage*
GetDataReductionProxyDebugBlockingPage() {
content::InterstitialPage* interstitial =
content::InterstitialPage::GetInterstitialPage(web_contents());
if (!interstitial)
return NULL;
EXPECT_EQ(DataReductionProxyDebugBlockingPage::kTypeForTesting,
interstitial->GetDelegateForTesting()->GetTypeForTesting());
return static_cast<DataReductionProxyDebugBlockingPage*>(
interstitial->GetDelegateForTesting());
}
UserResponse user_response() const {
return user_response_;
}
void ResetUserResponse() {
user_response_ = PENDING;
}
static void ProceedThroughInterstitial(
DataReductionProxyDebugBlockingPage* interstitial) {
interstitial->interstitial_page()->Proceed();
base::RunLoop().RunUntilIdle();
}
static void DontProceedThroughInterstitial(
DataReductionProxyDebugBlockingPage* interstitial) {
interstitial->interstitial_page()->DontProceed();
base::RunLoop().RunUntilIdle();
}
void DontProceedThroughSubresourceInterstitial(
DataReductionProxyDebugBlockingPage* sb_interstitial) {
// CommandReceived(kTakeMeBackCommand) does a back navigation for
// subresource interstitials.
GoBack(false);
base::RunLoop().RunUntilIdle();
}
private:
void InitResource(DataReductionProxyDebugUIManager::BypassResource* resource,
bool is_subresource,
const GURL& url) {
resource->callback =
base::Bind(&DataReductionProxyDebugBlockingPageTest::
OnBlockingPageComplete,
base::Unretained(this));
resource->url = url;
resource->is_subresource = is_subresource;
resource->render_process_host_id =
web_contents()->GetRenderProcessHost()->GetID();
resource->render_view_id =
web_contents()->GetRenderViewHost()->GetRoutingID();
}
scoped_refptr<DataReductionProxyDebugUIManager> ui_manager_;
UserResponse user_response_;
TestDataReductionProxyDebugBlockingPageFactory factory_;
};
// Tests showing a blocking page for a bypassed request and not proceeding.
TEST_F(DataReductionProxyDebugBlockingPageTest, BypassPageDontProceed) {
// Start a load.
controller().LoadURL(GURL(kBypassURL), content::Referrer(),
ui::PAGE_TRANSITION_TYPED, std::string());
// Simulate the load causing an interstitial to be shown.
ShowInterstitial(false, kBypassURL);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
base::RunLoop().RunUntilIdle();
// Simulate the user clicking "don't proceed".
DontProceedThroughInterstitial(interstitial);
// The interstitial should be gone.
EXPECT_EQ(CANCEL, user_response());
EXPECT_FALSE(GetDataReductionProxyDebugBlockingPage());
// The user did not proceed, the pending entry should be gone.
EXPECT_FALSE(controller().GetPendingEntry());
}
// Tests showing a blocking page for a bypassed request and proceeding.
TEST_F(DataReductionProxyDebugBlockingPageTest, BypassPageProceed) {
// Start a load.
controller().LoadURL(GURL(kBypassURL), content::Referrer(),
ui::PAGE_TRANSITION_TYPED, std::string());
int pending_id = controller().GetPendingEntry()->GetUniqueID();
// Simulate the load causing an interstitial to be shown.
ShowInterstitial(false, kBypassURL);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
base::RunLoop().RunUntilIdle();
// Simulate the user clicking "proceed".
ProceedThroughInterstitial(interstitial);
// The interstitial is shown until the navigation commits.
ASSERT_TRUE(GetDataReductionProxyDebugBlockingPage());
// Commit the navigation.
Navigate(kBypassURL, 1, pending_id, true);
// The interstitial should be gone now.
EXPECT_EQ(OK, user_response());
ASSERT_FALSE(GetDataReductionProxyDebugBlockingPage());
}
// Tests showing a blocking page for a page that contains bypassed subresources
// and not proceeding.
TEST_F(DataReductionProxyDebugBlockingPageTest, BypassSubresourceDontProceed) {
// Navigate somewhere.
Navigate(kGoogleURL, 1, 0, true);
// Navigate somewhere else.
Navigate(kOtherURL, 2, 0, true);
// Simulate that page loading a bypass-resource triggering an interstitial.
ShowInterstitial(true, kBypassURL);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
base::RunLoop().RunUntilIdle();
// Simulate the user clicking "don't proceed".
DontProceedThroughSubresourceInterstitial(interstitial);
EXPECT_EQ(CANCEL, user_response());
EXPECT_FALSE(GetDataReductionProxyDebugBlockingPage());
// The user did not proceed, the controler should be back to the first page,
// the 2nd one should have been removed from the navigation controller.
ASSERT_EQ(1, controller().GetEntryCount());
EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->GetURL().spec());
}
// Tests showing a blocking page for a page that contains bypassed subresources
// and proceeding.
TEST_F(DataReductionProxyDebugBlockingPageTest, BypassSubresourceProceed) {
// Navigate somewhere.
Navigate(kGoogleURL, 1, 0, true);
// Simulate that page loading a bypass-resource triggering an interstitial.
ShowInterstitial(true, kBypassURL);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
base::RunLoop().RunUntilIdle();
// Simulate the user clicking "proceed".
ProceedThroughInterstitial(interstitial);
EXPECT_EQ(OK, user_response());
EXPECT_FALSE(GetDataReductionProxyDebugBlockingPage());
// The user did proceed, the controller should be back to showing the page.
ASSERT_EQ(1, controller().GetEntryCount());
EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->GetURL().spec());
}
// Tests showing a blocking page for a page that contains multiple bypassed
// subresources and not proceeding. This just tests that the extra bypassed
// subresources (which trigger queued interstitial pages) do not break anything.
TEST_F(DataReductionProxyDebugBlockingPageTest,
BypassMultipleSubresourcesDontProceed) {
// Navigate somewhere.
Navigate(kGoogleURL, 1, 0, true);
// Navigate somewhere else.
Navigate(kOtherURL, 2, 0, true);
// Simulate that page loading a bypass-resource triggering an interstitial.
ShowInterstitial(true, kBypassURL);
// More bypassed resources loading causing more interstitials. The new
// interstitials should be queued.
ShowInterstitial(true, kBypassURL2);
ShowInterstitial(true, kBypassURL3);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
// Simulate the user clicking "don't proceed".
DontProceedThroughSubresourceInterstitial(interstitial);
EXPECT_EQ(CANCEL, user_response());
EXPECT_FALSE(GetDataReductionProxyDebugBlockingPage());
// The user did not proceed, the controller should be back to the first page,
// the 2nd one should have been removed from the navigation controller.
ASSERT_EQ(1, controller().GetEntryCount());
EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->GetURL().spec());
}
// Tests showing a blocking page for a page that contains multiple bypassed
// subresources. Proceeding through the first interstitial should proceed
// through all interstitials.
TEST_F(DataReductionProxyDebugBlockingPageTest,
BypassMultipleSubresourcesProceed) {
// Navigate somewhere.
Navigate(kGoogleURL, 1, 0, true);
// Simulate that page loading a bypass-resource triggering an interstitial.
ShowInterstitial(true, kBypassURL);
// More bypassed resources loading causing more interstitials. The new
// interstitials should be queued.
ShowInterstitial(true, kBypassURL2);
ShowInterstitial(true, kBypassURL3);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
// Proceed through the 1st interstitial.
ProceedThroughInterstitial(interstitial);
EXPECT_EQ(OK, user_response());
// The user did proceed, the controller should be back to showing the page.
ASSERT_EQ(1, controller().GetEntryCount());
EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->GetURL().spec());
}
// Tests showing a blocking page then navigating back and forth to make sure the
// controller entries are OK.
TEST_F(DataReductionProxyDebugBlockingPageTest, NavigatingBackAndForth) {
// Navigate somewhere.
Navigate(kGoogleURL, 1, 0, true);
// Now navigate to a bypassed page triggerring an interstitial.
controller().LoadURL(GURL(kBypassURL), content::Referrer(),
ui::PAGE_TRANSITION_TYPED, std::string());
int pending_id = controller().GetPendingEntry()->GetUniqueID();
ShowInterstitial(false, kBypassURL);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
// Proceed through the 1st interstitial.
ProceedThroughInterstitial(interstitial);
NavigateCrossSite(kBypassURL, 2, pending_id, true); // Commit navigation.
GoBack(true);
// We are back on the first page.
interstitial = GetDataReductionProxyDebugBlockingPage();
ASSERT_FALSE(interstitial);
ASSERT_EQ(2, controller().GetEntryCount());
EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->GetURL().spec());
// Navigate forward to the bypassed URL.
web_contents()->GetController().GoForward();
pending_id = controller().GetPendingEntry()->GetUniqueID();
ShowInterstitial(false, kBypassURL);
interstitial = GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
// Let's proceed and make sure everything is OK.
ProceedThroughInterstitial(interstitial);
// Commit the navigation.
NavigateCrossSite(kBypassURL, 2, pending_id, false);
interstitial = GetDataReductionProxyDebugBlockingPage();
ASSERT_FALSE(interstitial);
ASSERT_EQ(2, controller().GetEntryCount());
EXPECT_EQ(kBypassURL, controller().GetActiveEntry()->GetURL().spec());
}
// Tests that calling "don't proceed" after "proceed" has been called causes
// the interstitial to disappear.
TEST_F(DataReductionProxyDebugBlockingPageTest, ProceedThenDontProceed) {
// Start a load.
controller().LoadURL(GURL(kBypassURL), content::Referrer(),
ui::PAGE_TRANSITION_TYPED, std::string());
// Simulate the load causing an interstitial to be shown.
ShowInterstitial(false, kBypassURL);
DataReductionProxyDebugBlockingPage* interstitial =
GetDataReductionProxyDebugBlockingPage();
ASSERT_TRUE(interstitial);
base::RunLoop().RunUntilIdle();
// Simulate the user clicking "proceed" then "don't proceed".
interstitial->interstitial_page()->Proceed();
interstitial->interstitial_page()->DontProceed();
base::RunLoop().RunUntilIdle();
// The interstitial should be gone.
EXPECT_EQ(OK, user_response());
EXPECT_FALSE(GetDataReductionProxyDebugBlockingPage());
}
} // namespace data_reduction_proxy