|  | // 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 "content/public/test/navigation_simulator.h" | 
|  |  | 
|  | #include "base/memory/weak_ptr.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "content/public/browser/navigation_handle.h" | 
|  | #include "content/public/browser/navigation_throttle.h" | 
|  | #include "content/test/test_render_frame_host.h" | 
|  | #include "content/test/test_web_contents.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | enum CancelTime { | 
|  | WILL_SEND_REQUEST, | 
|  | WILL_REDIRECT_REQUEST, | 
|  | WILL_PROCESS_RESPONSE, | 
|  | NEVER, | 
|  | }; | 
|  |  | 
|  | enum ResultSynchrony { | 
|  | SYNCHRONOUS, | 
|  | ASYNCHRONOUS, | 
|  | }; | 
|  |  | 
|  | std::string CancelTimeToString(CancelTime cancel_time) { | 
|  | switch (cancel_time) { | 
|  | case WILL_SEND_REQUEST: | 
|  | return "WILL_SEND_REQUEST"; | 
|  | case WILL_REDIRECT_REQUEST: | 
|  | return "WILL_REDIRECT_REQUEST"; | 
|  | case WILL_PROCESS_RESPONSE: | 
|  | return "WILL_PROCESS_RESPONSE"; | 
|  | case NEVER: | 
|  | return "NEVER"; | 
|  | } | 
|  | NOTREACHED(); | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | std::string ResultSynchronyToString(ResultSynchrony sync) { | 
|  | return sync == SYNCHRONOUS ? "SYNCHRONOUS" : "ASYNCHRONOUS"; | 
|  | } | 
|  |  | 
|  | class CancellingNavigationThrottle : public NavigationThrottle { | 
|  | public: | 
|  | CancellingNavigationThrottle(NavigationHandle* handle, | 
|  | CancelTime cancel_time, | 
|  | ResultSynchrony sync) | 
|  | : NavigationThrottle(handle), | 
|  | cancel_time_(cancel_time), | 
|  | sync_(sync), | 
|  | weak_ptr_factory_(this) {} | 
|  |  | 
|  | ~CancellingNavigationThrottle() override {} | 
|  |  | 
|  | NavigationThrottle::ThrottleCheckResult WillStartRequest() override { | 
|  | return ProcessState(cancel_time_ == WILL_SEND_REQUEST); | 
|  | } | 
|  |  | 
|  | NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override { | 
|  | return ProcessState(cancel_time_ == WILL_REDIRECT_REQUEST); | 
|  | } | 
|  |  | 
|  | NavigationThrottle::ThrottleCheckResult WillProcessResponse() override { | 
|  | return ProcessState(cancel_time_ == WILL_PROCESS_RESPONSE); | 
|  | } | 
|  |  | 
|  | NavigationThrottle::ThrottleCheckResult ProcessState(bool should_cancel) { | 
|  | if (sync_ == ASYNCHRONOUS) { | 
|  | BrowserThread::PostTask( | 
|  | BrowserThread::UI, FROM_HERE, | 
|  | base::Bind(&CancellingNavigationThrottle::MaybeCancel, | 
|  | weak_ptr_factory_.GetWeakPtr(), should_cancel)); | 
|  | return NavigationThrottle::DEFER; | 
|  | } | 
|  | return should_cancel ? NavigationThrottle::CANCEL | 
|  | : NavigationThrottle::PROCEED; | 
|  | } | 
|  |  | 
|  | void MaybeCancel(bool cancel) { | 
|  | if (cancel) | 
|  | navigation_handle()->CancelDeferredNavigation(NavigationThrottle::CANCEL); | 
|  | else | 
|  | navigation_handle()->Resume(); | 
|  | } | 
|  |  | 
|  | const CancelTime cancel_time_; | 
|  | const ResultSynchrony sync_; | 
|  | base::WeakPtrFactory<CancellingNavigationThrottle> weak_ptr_factory_; | 
|  | }; | 
|  |  | 
|  | class NavigationSimulatorTest : public RenderViewHostImplTestHarness, | 
|  | public WebContentsObserver, | 
|  | public testing::WithParamInterface< | 
|  | std::tuple<CancelTime, ResultSynchrony>> { | 
|  | public: | 
|  | void SetUp() override { | 
|  | RenderViewHostImplTestHarness::SetUp(); | 
|  | contents()->GetMainFrame()->InitializeRenderFrameIfNeeded(); | 
|  | Observe(RenderViewHostImplTestHarness::web_contents()); | 
|  | std::tie(cancel_time_, sync_) = GetParam(); | 
|  | simulator_ = NavigationSimulator::CreateRendererInitiated( | 
|  | GURL("https://example.test"), main_rfh()); | 
|  | } | 
|  |  | 
|  | void DidStartNavigation(content::NavigationHandle* handle) override { | 
|  | handle->RegisterThrottleForTesting( | 
|  | base::MakeUnique<CancellingNavigationThrottle>(handle, cancel_time_, | 
|  | sync_)); | 
|  | } | 
|  |  | 
|  | CancelTime cancel_time_; | 
|  | ResultSynchrony sync_; | 
|  | std::unique_ptr<NavigationSimulator> simulator_; | 
|  | }; | 
|  |  | 
|  | // Stress test the navigation simulator by having a navigation throttle cancel | 
|  | // the navigation at various points in the flow, both synchronously and | 
|  | // asynchronously. | 
|  | TEST_P(NavigationSimulatorTest, Cancel) { | 
|  | SCOPED_TRACE(::testing::Message() | 
|  | << "CancelTime: " << CancelTimeToString(cancel_time_) | 
|  | << " ResultSynchrony: " << ResultSynchronyToString(sync_)); | 
|  | simulator_->Start(); | 
|  | if (cancel_time_ == WILL_SEND_REQUEST) { | 
|  | EXPECT_EQ(NavigationThrottle::CANCEL, | 
|  | simulator_->GetLastThrottleCheckResult()); | 
|  | return; | 
|  | } | 
|  | EXPECT_EQ(NavigationThrottle::PROCEED, | 
|  | simulator_->GetLastThrottleCheckResult()); | 
|  | simulator_->Redirect(GURL("https://example.redirect")); | 
|  | if (cancel_time_ == WILL_REDIRECT_REQUEST) { | 
|  | EXPECT_EQ(NavigationThrottle::CANCEL, | 
|  | simulator_->GetLastThrottleCheckResult()); | 
|  | return; | 
|  | } | 
|  | EXPECT_EQ(NavigationThrottle::PROCEED, | 
|  | simulator_->GetLastThrottleCheckResult()); | 
|  | simulator_->Commit(); | 
|  | if (cancel_time_ == WILL_PROCESS_RESPONSE) { | 
|  | EXPECT_EQ(NavigationThrottle::CANCEL, | 
|  | simulator_->GetLastThrottleCheckResult()); | 
|  | return; | 
|  | } | 
|  | EXPECT_EQ(NavigationThrottle::PROCEED, | 
|  | simulator_->GetLastThrottleCheckResult()); | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P( | 
|  | CancelMethod, | 
|  | NavigationSimulatorTest, | 
|  | ::testing::Values(std::make_tuple(WILL_SEND_REQUEST, SYNCHRONOUS), | 
|  | std::make_tuple(WILL_SEND_REQUEST, ASYNCHRONOUS), | 
|  | std::make_tuple(WILL_REDIRECT_REQUEST, SYNCHRONOUS), | 
|  | std::make_tuple(WILL_REDIRECT_REQUEST, ASYNCHRONOUS), | 
|  | std::make_tuple(WILL_PROCESS_RESPONSE, SYNCHRONOUS), | 
|  | std::make_tuple(WILL_PROCESS_RESPONSE, ASYNCHRONOUS), | 
|  | std::make_tuple(NEVER, SYNCHRONOUS), | 
|  | std::make_tuple(NEVER, ASYNCHRONOUS))); | 
|  |  | 
|  | }  // namespace content |